API Reference
Rev's metering API lets you record agent interactions, compute pricing in real-time, and track revenue across agents and customers.
Authentication
All /v1/* endpoints require a Bearer token. Include your API key in the Authorization header.
Authorization: Bearer rg_your_api_key_here
Don't have an API key? Sign up for free to get one instantly. API keys start with the rg_ prefix.
Keep your key secret. Never expose API keys in client-side code. Use environment variables or a secrets manager.
Base URL
All endpoints below are relative to this base URL. For example, POST /v1/meter means POST https://rev.polsia.app/v1/meter.
Errors
Rev uses standard HTTP status codes. Errors return a JSON body with an error field.
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created (new resource) |
| 400 | Bad request (missing or invalid parameters) |
| 401 | Unauthorized (missing or invalid API key) |
| 404 | Not found |
| 500 | Internal server error |
{
"error": "agent_id is required"
}
Record a metered agent event and get the computed price back instantly. This is the core endpoint โ call it every time an agent performs an action you want to bill for.
Request body
| Parameter | Type | Description | |
|---|---|---|---|
| agent_id | string | required | Unique identifier for the agent performing the action |
| action | string | required | Action type: chat, completion, embedding, tool_call, image_gen, search |
| tokens | integer | optional | Number of tokens used. Defaults to 0. |
| outcome | string | optional | success or failure. When success, the outcome bonus is applied. |
| metadata | object | optional | Arbitrary JSON metadata attached to the event for your records. |
Example
curl -X POST https://rev.polsia.app/v1/meter \ -H "Content-Type: application/json" \ -H "Authorization: Bearer rg_your_api_key" \ -d '{ "agent_id": "agent-copilot", "action": "completion", "tokens": 2400, "outcome": "success" }'
const res = await fetch('https://rev.polsia.app/v1/meter', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.REVGRID_API_KEY}`, }, body: JSON.stringify({ agent_id: 'agent-copilot', action: 'completion', tokens: 2400, outcome: 'success', }), }); const data = await res.json(); console.log(data.price.total_cents); // 1.47
Response 201
{
"event_id": 42,
"price": {
"total_cents": 1.47,
"breakdown": {
"base_rate_cents": 0.5,
"token_charge_cents": 0.72,
"outcome_bonus_cents": 0.25
},
"config": {
"action_type": "completion",
"base_rate_cents": 0.5,
"token_rate_cents": 0.0003,
"outcome_bonus_cents": 0.25,
"is_default": true
}
},
"event": {
"customer_id": "acme-corp",
"agent_id": "agent-copilot",
"action": "completion",
"tokens": 2400,
"outcome": "success"
},
"created_at": "2026-04-26T12:00:00.000Z"
}
Get a usage summary for the authenticated customer. Shows total events, revenue, tokens, and unique agents for the specified period.
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
| period | string | optional | 24h, 7d, 30d, or 90d. Defaults to 30d. |
Example
curl https://rev.polsia.app/v1/usage?period=7d \
-H "Authorization: Bearer rg_your_api_key"
const res = await fetch('https://rev.polsia.app/v1/usage?period=7d', { headers: { 'Authorization': `Bearer ${API_KEY}` }, }); const usage = await res.json();
Response 200
{
"customer_id": "acme-corp",
"period": "7d",
"summary": {
"total_events": 1284,
"total_cents": 4523.12,
"total_dollars": 45.23,
"total_tokens": 892400,
"unique_agents": 3
},
"by_action": [
{ "action": "completion", "event_count": 800, "total_cents": 3200.50 },
{ "action": "chat", "event_count": 400, "total_cents": 1100.62 }
]
}
Get the active pricing configuration for the authenticated customer. Shows the effective rate for each action type (custom rates override defaults).
Example
curl https://rev.polsia.app/v1/pricing \
-H "Authorization: Bearer rg_your_api_key"
const res = await fetch('https://rev.polsia.app/v1/pricing', { headers: { 'Authorization': `Bearer ${API_KEY}` }, }); const pricing = await res.json();
Response 200
{
"customer_id": "acme-corp",
"configs": [
{
"action_type": "chat",
"base_rate_cents": 0.1,
"token_rate_cents": 0.0001,
"outcome_bonus_cents": 0.05,
"is_custom": false
}
]
}
Dashboard Analytics
Revenue and usage analytics endpoints. These are public (no auth required) and power the dashboard UI.
Get overall platform metrics: total events, revenue, active agents, and API keys.
Example
curl https://rev.polsia.app/api/dashboard/stats
const res = await fetch('https://rev.polsia.app/api/dashboard/stats'); const stats = await res.json();
Response 200
{
"total_events": 12450,
"total_revenue_cents": 89234.50,
"total_revenue_dollars": 892.35,
"events_today": 340,
"revenue_today_cents": 2456.12,
"revenue_today_dollars": 24.56,
"revenue_week_cents": 15200.00,
"revenue_week_dollars": 152.00,
"active_agents_today": 5,
"total_customers": 8,
"active_api_keys": 12
}
Revenue breakdown per agent. Shows event count, total revenue, tokens, and customer reach for each agent.
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
| period | string | optional | 24h, 7d, or 30d. Defaults to 30d. |
Example
curl https://rev.polsia.app/api/dashboard/revenue-by-agent?period=7d
Response 200
{
"period": "7d",
"agents": [
{
"agent_id": "agent-copilot",
"event_count": 800,
"total_cents": 3200.50,
"total_dollars": 32.01,
"total_tokens": 560000,
"customer_count": 3
}
]
}
Revenue breakdown per customer. Shows event count, revenue, tokens, and agent usage for each customer.
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
| period | string | optional | 24h, 7d, or 30d. Defaults to 30d. |
Example
curl https://rev.polsia.app/api/dashboard/revenue-by-customer?period=30d
Response 200
{
"period": "30d",
"customers": [
{
"customer_id": "acme-corp",
"customer_name": "Acme Corp",
"event_count": 1284,
"total_cents": 4523.12,
"total_dollars": 45.23,
"total_tokens": 892400,
"agent_count": 3
}
]
}
Get the most recent metering events with full details including price breakdown and metadata.
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
| limit | integer | optional | Number of events (1-100). Defaults to 50. |
Example
curl https://rev.polsia.app/api/dashboard/recent-events?limit=10
Response 200
{
"events": [
{
"id": 42,
"customer_id": "acme-corp",
"customer_name": "Acme Corp",
"agent_id": "agent-copilot",
"action": "completion",
"tokens": 2400,
"outcome": "success",
"price_cents": 1.47,
"price_breakdown": { "base_rate_cents": 0.5, "token_charge_cents": 0.72, "outcome_bonus_cents": 0.25 },
"created_at": "2026-04-26T12:00:00.000Z"
}
]
}
Revenue timeseries data. Bucketed by hour (24h) or day (7d/30d).
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
| period | string | optional | 24h, 7d, or 30d. Defaults to 30d. |
Example
curl https://rev.polsia.app/api/dashboard/revenue-over-time?period=7d
Response 200
{
"period": "7d",
"data": [
{ "period_start": "2026-04-20T00:00:00.000Z", "event_count": 180, "total_cents": 2100.50, "total_dollars": 21.01 },
{ "period_start": "2026-04-21T00:00:00.000Z", "event_count": 210, "total_cents": 2450.00, "total_dollars": 24.50 }
]
}
Webhooks
Get real-time notifications when billing events happen. Register a URL and Rev will POST event payloads to it with HMAC-SHA256 signatures for verification.
Event types: meter.created, pricing.updated, key.created, key.revoked. Use * to subscribe to all events.
Retry policy: Failed deliveries are retried up to 3 times with exponential backoff (1s, 5s, 25s). Client errors (4xx except 429) are not retried.
Webhook payload format
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"type": "meter.created",
"created_at": "2026-04-28T12:00:00.000Z",
"data": {
"event_id": 42,
"price": { /* price breakdown */ },
"event": { /* event details */ }
}
}
Headers sent with each delivery
| Header | Description |
|---|---|
| X-Rev-Signature | HMAC-SHA256 hex digest of the request body using your webhook secret |
| X-Rev-Event | The event type (e.g. meter.created) |
| X-Rev-Delivery | Unique delivery ID (UUID) for idempotency |
| Content-Type | application/json |
Register a webhook URL to receive event notifications. The response includes a secret for verifying signatures — store it securely.
Request body
| Parameter | Type | Description | |
|---|---|---|---|
| url | string | required | The HTTPS URL to receive webhook POST requests |
| events | string[] | required | Array of event types to subscribe to: meter.created, pricing.updated, key.created, key.revoked, or * for all |
Example
curl -X POST https://rev.polsia.app/v1/webhooks \ -H "Content-Type: application/json" \ -H "Authorization: Bearer rg_your_api_key" \ -d '{ "url": "https://your-app.com/webhooks/revgrid", "events": ["meter.created", "pricing.updated"] }'
const res = await fetch('https://rev.polsia.app/v1/webhooks', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.REVGRID_API_KEY}`, }, body: JSON.stringify({ url: 'https://your-app.com/webhooks/revgrid', events: ['meter.created', 'pricing.updated'], }), }); const webhook = await res.json(); console.log(webhook.secret); // Store this securely!
Response 201
{
"id": 1,
"customer_id": "acme-corp",
"url": "https://your-app.com/webhooks/revgrid",
"secret": "whsec_a1b2c3d4...",
"events": ["meter.created", "pricing.updated"],
"active": true,
"created_at": "2026-04-28T12:00:00.000Z"
}
List all webhooks registered for the authenticated customer. Secrets are not included in the response.
Example
curl https://rev.polsia.app/v1/webhooks \
-H "Authorization: Bearer rg_your_api_key"
Response 200
{
"webhooks": [
{
"id": 1,
"customer_id": "acme-corp",
"url": "https://your-app.com/webhooks/revgrid",
"events": ["meter.created", "pricing.updated"],
"active": true,
"created_at": "2026-04-28T12:00:00.000Z"
}
]
}
Permanently delete a webhook. You can only delete webhooks belonging to the authenticated customer.
Example
curl -X DELETE https://rev.polsia.app/v1/webhooks/1 \
-H "Authorization: Bearer rg_your_api_key"
Response 200
{ "success": true, "deleted_id": 1 }
Verifying Webhook Signatures
Every webhook delivery includes an X-Rev-Signature header containing an HMAC-SHA256 hex digest of the request body. Always verify this signature to ensure the payload came from Rev.
Node.js verification example
const crypto = require('crypto'); // Use express.raw() for webhook routes to get the raw body app.post('/webhooks/revgrid', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-revgrid-signature']; const secret = process.env.REVGRID_WEBHOOK_SECRET; // Compute expected signature const expected = crypto .createHmac('sha256', secret) .update(req.body) .digest('hex'); // Constant-time comparison to prevent timing attacks if (!crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) )) { return res.status(401).json({ error: 'Invalid signature' }); } const event = JSON.parse(req.body); console.log('Received:', event.type, event.data); // Process the event... res.status(200).json({ received: true }); });
import hmac, hashlib, json from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/webhooks/revgrid', methods=['POST']) def handle_webhook(): signature = request.headers.get('X-Rev-Signature') secret = os.environ['REVGRID_WEBHOOK_SECRET'] expected = hmac.new( secret.encode(), request.data, hashlib.sha256 ).hexdigest() if not hmac.compare_digest(signature, expected): return jsonify(error='Invalid signature'), 401 event = request.get_json() print(f"Received: {event['type']}") # Process the event... return jsonify(received=True), 200
Important: Use the raw request body (not parsed JSON) for signature verification. Parsing and re-serializing JSON may change whitespace or key order, breaking the signature check. Use express.raw() in Node.js or request.data in Flask.
Admin Endpoints
Manage API keys and pricing configurations. These endpoints currently have no authentication.
Create a new API key for a customer. The key is returned in the response โ store it securely, it won't be shown again in full.
Request body
| Parameter | Type | Description | |
|---|---|---|---|
| customer_id | string | required | Unique identifier for the customer |
| customer_name | string | optional | Human-readable name for the customer |
Example
curl -X POST https://rev.polsia.app/api/admin/api-keys \ -H "Content-Type: application/json" \ -d '{"customer_id": "acme-corp", "customer_name": "Acme Corp"}'
Response 201
{
"id": 1,
"customer_id": "acme-corp",
"customer_name": "Acme Corp",
"api_key": "rg_a1b2c3d4e5f6...",
"created_at": "2026-04-26T12:00:00.000Z"
}
List all API keys. Keys are shown masked by default.
Example
curl https://rev.polsia.app/api/admin/api-keys
Response 200
{
"keys": [
{
"id": 1,
"customer_id": "acme-corp",
"customer_name": "Acme Corp",
"api_key_masked": "rg_a1b2...f6g7",
"is_active": true,
"created_at": "2026-04-26T12:00:00.000Z"
}
]
}
Revoke an API key by ID. The key is soft-deleted (marked inactive) and can no longer authenticate requests.
Example
curl -X DELETE https://rev.polsia.app/api/admin/api-keys/1
Response 200
{ "success": true }
Create or update a pricing configuration for an action type. Previous config for the same action+customer is deactivated automatically.
Request body
| Parameter | Type | Description | |
|---|---|---|---|
| action_type | string | required | The action to configure pricing for |
| base_rate_cents | number | optional | Base rate in cents per event. Defaults to 0. |
| token_rate_cents | number | optional | Per-token rate in cents. Defaults to 0. |
| outcome_bonus_cents | number | optional | Bonus applied on success outcome. Defaults to 0. |
| customer_id | string | optional | If set, creates a customer-specific override. Otherwise sets the default rate. |
Example
curl -X POST https://rev.polsia.app/api/admin/pricing \ -H "Content-Type: application/json" \ -d '{ "action_type": "completion", "base_rate_cents": 0.75, "token_rate_cents": 0.0004, "outcome_bonus_cents": 0.30, "customer_id": "acme-corp" }'
Response 201
{
"id": 7,
"customer_id": "acme-corp",
"action_type": "completion",
"base_rate_cents": 0.75,
"token_rate_cents": 0.0004,
"outcome_bonus_cents": 0.3,
"is_active": true,
"created_at": "2026-04-26T12:00:00.000Z"
}
List all active pricing configurations, including both defaults and customer-specific overrides.
Example
curl https://rev.polsia.app/api/admin/pricing
Response 200
{
"configs": [
{
"id": 1,
"customer_id": null,
"action_type": "chat",
"base_rate_cents": 0.1,
"token_rate_cents": 0.0001,
"outcome_bonus_cents": 0.05,
"is_active": true
}
]
}
Outcome Guarantees
Charge only when your agent succeeds โ and guarantee it in writing. Register a task with success criteria and conditional pricing, then resolve it when the outcome is known. The charge fires automatically.
Why outcome-based pricing? Traditional metering charges for inputs โ tokens used, API calls made โ regardless of whether the agent actually delivered value. Outcome pricing flips that: you charge on success and eat the cost on failure. That alignment is what makes AI agents trustworthy to buyers. It also lets you charge more per success than you ever could per token.
Lifecycle: pending โ succeeded or failed (via resolve) โ or expired if expires_at passes without a resolve. Background job expires outcomes every 60 seconds.
On success: price_on_success is recorded as a charge and outcome.succeeded webhook fires.
On failure: price_on_failure is charged (default $0) and outcome.failed fires.
Register a task with success-conditional pricing. Returns immediately with status pending. The charge fires when you resolve it.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| task_id | string | required | Your unique identifier for this task. Must be unique per customer. |
| agent_id | string | required | The agent executing this task. |
| success_criteria | object | optional | Free-form JSON describing what success looks like. Stored for reference; not evaluated by Rev. |
| price_on_success | integer | required | Amount to charge (in cents) when the outcome is resolved as successful. |
| price_on_failure | integer | optional | Amount to charge on failure. Defaults to 0 (free failure guarantee). |
| expires_at | string | required | ISO 8601 timestamp after which the outcome auto-expires as failed if not resolved. |
Code Example
curl -X POST https://rev.polsia.app/v1/outcomes \ -H "Authorization: Bearer rg_your_key" \ -H "Content-Type: application/json" \ -d '{ "task_id": "task_abc123", "agent_id": "agent-legal", "success_criteria": { "description": "Draft reviewed and approved by client", "deliverable": "signed_contract" }, "price_on_success": 4900, "price_on_failure": 0, "expires_at": "2026-04-30T00:00:00Z" }'
const res = await fetch('https://rev.polsia.app/v1/outcomes', { method: 'POST', headers: { 'Authorization': 'Bearer rg_your_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ task_id: 'task_abc123', agent_id: 'agent-legal', success_criteria: { deliverable: 'signed_contract' }, price_on_success: 4900, price_on_failure: 0, expires_at: '2026-04-30T00:00:00Z', }), }); const outcome = await res.json();
Response 201
{
"task_id": "task_abc123",
"customer_id": "cust_acme",
"agent_id": "agent-legal",
"success_criteria": { "deliverable": "signed_contract" },
"price_on_success_cents": 4900,
"price_on_failure_cents": 0,
"expires_at": "2026-04-30T00:00:00.000Z",
"status": "pending",
"created_at": "2026-04-29T19:27:00.000Z"
}
Resolve a pending outcome as succeeded or failed. Fires the charge and emits the outcome.succeeded or outcome.failed webhook.
Path Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| task_id | string | required | The task_id used when registering the outcome. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| success | boolean | required | true to resolve as succeeded (charges price_on_success). false for failed (charges price_on_failure). |
| evidence | object | optional | Free-form JSON proof of the outcome. Stored with the record. |
Code Example
curl -X POST https://rev.polsia.app/v1/outcomes/task_abc123/resolve \ -H "Authorization: Bearer rg_your_key" \ -H "Content-Type: application/json" \ -d '{ "success": true, "evidence": { "contract_url": "https://docs.example.com/signed/abc123.pdf", "signed_at": "2026-04-29T20:00:00Z" } }'
const res = await fetch('https://rev.polsia.app/v1/outcomes/task_abc123/resolve', { method: 'POST', headers: { 'Authorization': 'Bearer rg_your_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ success: true, evidence: { contract_url: 'https://docs.example.com/signed/abc123.pdf' }, }), });
Response 200
{
"task_id": "task_abc123",
"status": "succeeded",
"success": true,
"charge_cents": 4900,
"evidence": { "contract_url": "https://docs.example.com/signed/abc123.pdf" },
"resolved_at": "2026-04-29T20:00:01.000Z"
}
Fetch the current state of an outcome. If it has passed expires_at without resolution, status is updated to expired on read.
Response 200
{
"task_id": "task_abc123",
"customer_id": "cust_acme",
"agent_id": "agent-legal",
"success_criteria": { "deliverable": "signed_contract" },
"price_on_success_cents": 4900,
"price_on_failure_cents": 0,
"expires_at": "2026-04-30T00:00:00.000Z",
"status": "succeeded",
"charge_cents": 4900,
"evidence": { "contract_url": "https://docs.example.com/signed/abc123.pdf" },
"resolved_at": "2026-04-29T20:00:01.000Z",
"created_at": "2026-04-29T19:27:00.000Z"
}
SDKs
Official client libraries so you can integrate Rev without hand-rolling HTTP.
Sync + async client with type hints, dataclass response models, and automatic retry on 5xx. Python 3.9+.
# PyPI pip install revgrid # optional: dev/test extras pip install "revgrid[dev]"
Quickstart
from revgrid import Rev client = Rev(api_key="rg_...") # meter a usage event โ returns computed price resp = client.meter( agent_id="gpt4-coder", action="completion", tokens_in=1200, tokens_out=800, ) print(f"Charged {resp.price.total_cents}ยข (event_id={resp.event_id})") # usage summary usage = client.usage(period="7d") print(f"${usage.summary.total_dollars:.2f} this week")
import asyncio from revgrid import AsyncRev async def main(): async with AsyncRev(api_key="rg_...") as client: resp = await client.meter( agent_id="my-agent", action="tool_call", tokens=500, ) print(resp.price.total_cents) asyncio.run(main())
from revgrid import Rev client = Rev(api_key="rg_...") # register: pay $5 only if task succeeds outcome = client.register_outcome( task_id="task-abc-123", agent_id="my-agent", price_on_success=500, # cents price_on_failure=0, expires_at="2026-05-01T00:00:00Z", ) # ... agent runs the task ... result = client.resolve_outcome( task_id="task-abc-123", success=True, evidence={"url": "https://proof.example.com"}, ) print(f"Status: {result.status}, charged: {result.charge_cents}ยข")
from revgrid import Rev client = Rev(api_key="rg_...") # register a webhook endpoint wh = client.create_webhook( url="https://yourapp.com/webhooks/revgrid", events=["meter.created", "outcome.succeeded"], ) print(f"Webhook secret: {wh.secret}") # verify incoming signatures verifier = client.webhook_verifier(secret=wh.secret) def handle_webhook(raw_body, sig_header): verifier.verify(raw_body, sig_header) # raises ValueError on bad sig # process event...
pip install revgrid
UTM Link Generator
Generate properly tagged links for outreach. Every link you share should include UTM parameters so the analytics dashboard can attribute traffic by source and campaign.
Template Links by Channel
| Channel | UTM Parameters |
|---|---|
| Cold emails | utm_source=email&utm_medium=cold_outreach&utm_campaign=wave{N} |
| Twitter / X | utm_source=twitter&utm_medium=social&utm_campaign={topic} |
| Directories | utm_source=directory&utm_medium=listing&utm_campaign={name} |
| utm_source=linkedin&utm_medium=social&utm_campaign={topic} |