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.

Header format
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

Production
https://rev.polsia.app

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.

CodeMeaning
200Success
201Created (new resource)
400Bad request (missing or invalid parameters)
401Unauthorized (missing or invalid API key)
404Not found
500Internal server error
Error response
{
  "error": "agent_id is required"
}

POST /v1/meter Bearer token

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

ParameterTypeDescription
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

JSON
{
  "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 /v1/usage Bearer token

Get a usage summary for the authenticated customer. Shows total events, revenue, tokens, and unique agents for the specified period.

Query parameters

ParameterTypeDescription
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

JSON
{
  "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 /v1/pricing Bearer token

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

JSON
{
  "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 /api/dashboard/stats No auth

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

JSON
{
  "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
}

GET /api/dashboard/revenue-by-agent No auth

Revenue breakdown per agent. Shows event count, total revenue, tokens, and customer reach for each agent.

Query parameters

ParameterTypeDescription
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

JSON
{
  "period": "7d",
  "agents": [
    {
      "agent_id": "agent-copilot",
      "event_count": 800,
      "total_cents": 3200.50,
      "total_dollars": 32.01,
      "total_tokens": 560000,
      "customer_count": 3
    }
  ]
}

GET /api/dashboard/revenue-by-customer No auth

Revenue breakdown per customer. Shows event count, revenue, tokens, and agent usage for each customer.

Query parameters

ParameterTypeDescription
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

JSON
{
  "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 /api/dashboard/recent-events No auth

Get the most recent metering events with full details including price breakdown and metadata.

Query parameters

ParameterTypeDescription
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

JSON
{
  "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"
    }
  ]
}

GET /api/dashboard/revenue-over-time No auth

Revenue timeseries data. Bucketed by hour (24h) or day (7d/30d).

Query parameters

ParameterTypeDescription
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

JSON
{
  "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

JSON
{
  "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

HeaderDescription
X-Rev-SignatureHMAC-SHA256 hex digest of the request body using your webhook secret
X-Rev-EventThe event type (e.g. meter.created)
X-Rev-DeliveryUnique delivery ID (UUID) for idempotency
Content-Typeapplication/json

POST /v1/webhooks Bearer token

Register a webhook URL to receive event notifications. The response includes a secret for verifying signatures — store it securely.

Request body

ParameterTypeDescription
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

JSON
{
  "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"
}

GET /v1/webhooks Bearer token

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

JSON
{
  "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"
    }
  ]
}

DELETE /v1/webhooks/:id Bearer token

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

JSON
{ "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.

POST /api/admin/api-keys No auth

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

ParameterTypeDescription
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

JSON
{
  "id": 1,
  "customer_id": "acme-corp",
  "customer_name": "Acme Corp",
  "api_key": "rg_a1b2c3d4e5f6...",
  "created_at": "2026-04-26T12:00:00.000Z"
}

GET /api/admin/api-keys No auth

List all API keys. Keys are shown masked by default.

Example

curl https://rev.polsia.app/api/admin/api-keys

Response 200

JSON
{
  "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"
    }
  ]
}

DELETE /api/admin/api-keys/:id No auth

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

JSON
{ "success": true }

POST /api/admin/pricing No auth

Create or update a pricing configuration for an action type. Previous config for the same action+customer is deactivated automatically.

Request body

ParameterTypeDescription
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

JSON
{
  "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"
}

GET /api/admin/pricing No auth

List all active pricing configurations, including both defaults and customer-specific overrides.

Example

curl https://rev.polsia.app/api/admin/pricing

Response 200

JSON
{
  "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.


POST /v1/outcomes Auth required

Register a task with success-conditional pricing. Returns immediately with status pending. The charge fires when you resolve it.

Request Body

FieldTypeRequiredDescription
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

JSON
{
  "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"
}

POST /v1/outcomes/:task_id/resolve Auth required

Resolve a pending outcome as succeeded or failed. Fires the charge and emits the outcome.succeeded or outcome.failed webhook.

Path Parameters

FieldTypeRequiredDescription
task_id string required The task_id used when registering the outcome.

Request Body

FieldTypeRequiredDescription
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

JSON
{
  "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"
}

GET /v1/outcomes/:task_id Auth required

Fetch the current state of an outcome. If it has passed expires_at without resolution, status is updated to expired on read.

Response 200

JSON
{
  "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.

๐Ÿ

Python SDK

v0.1.0 GitHub โ†—

Sync + async client with type hints, dataclass response models, and automatic retry on 5xx. Python 3.9+.

Install
# 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...
๐Ÿ“ฆ
Full docs & source

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}
LinkedIn utm_source=linkedin&utm_medium=social&utm_campaign={topic}