REST API Reference

All endpoints are served over HTTPS. Base URL: https://tokage.dev

Authentication

All requests must include an API key. Keys are prefixed tok_live_ for production environments. Use either header format:

shell
# Bearer token (recommended)
curl https://tokage.dev/api/v1/events \
  -H "Authorization: Bearer tok_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

# X-API-Key header (alternative)
curl https://tokage.dev/api/v1/events \
  -H "X-API-Key: tok_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

API keys are scoped to a tenant. All ingested events are automatically isolated by tenant at the database layer — you cannot read or write another tenant's data.

POST /api/v1/events — Single Event

Ingest a single token usage event. Returns HTTP 202 Accepted only after the event has been durably written to Redpanda with acks=all.

Request

http
POST /api/v1/events
Content-Type: application/json
Authorization: Bearer tok_live_...

{
  "schema_version": 1,
  "event_id": "0190cfb2-1234-7000-8000-abcdef012345",   // optional: UUIDv7
  "model_provider": "anthropic",
  "model_id": "claude-sonnet-4-6",
  "input_tokens": 512,
  "output_tokens": 128,
  "total_tokens": 640,
  "timestamp_client": "2026-02-24T00:00:00Z",           // optional: RFC 3339
  "application_id": "my-app",                           // optional
  "user_id": "alice@example.com",                       // optional
  "team_id": "engineering",                             // optional
  "environment": "production",                          // optional
  "metadata": { "session_id": "abc123" },              // optional: max 64 keys
  "tags": ["gpt-4-upgrade", "experiment-a"]             // optional: max 32 tags
}

Response

http
HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "event_id": "0190cfb2-1234-7000-8000-abcdef012345"
}

202 guarantee: A 202 response means the event is durable. It will not be lost. If you receive anything other than 202, the event was not committed.

POST /api/v1/events/batch — Batch Ingestion

Ingest up to 1,000 events in a single request. Maximum body size is 5 MB. Each event is individually validated; the entire batch is either accepted or rejected atomically.

Request

http
POST /api/v1/events/batch
Content-Type: application/json
Authorization: Bearer tok_live_...

{
  "events": [
    {
      "schema_version": 1,
      "model_provider": "openai",
      "model_id": "gpt-4o",
      "input_tokens": 1024,
      "output_tokens": 256,
      "total_tokens": 1280
    },
    {
      "schema_version": 1,
      "model_provider": "anthropic",
      "model_id": "claude-haiku-4-5",
      "input_tokens": 200,
      "output_tokens": 80,
      "total_tokens": 280
    }
  ]
}

Response

http
HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "accepted": 2,
  "event_ids": [
    "0190cfb2-aaaa-7000-8000-111111111111",
    "0190cfb2-bbbb-7000-8000-222222222222"
  ]
}
Rate limits: 1,000 events/s steady-state, 2,000/s burst per tenant. For sustained high-throughput ingestion, use the batch endpoint.

Analytics Endpoints

All analytics endpoints require from and to query parameters in RFC 3339 format. Results are scoped to your tenant automatically.

MethodPathDescription
GET/api/v1/analytics/cost-by-modelCost and token totals grouped by model
GET/api/v1/analytics/cost-by-applicationCost and token totals grouped by application_id
GET/api/v1/analytics/cost-by-userCost and token totals grouped by user_id
GET/api/v1/analytics/cost-by-teamCost and token totals grouped by team_id
GET/api/v1/analytics/daily-summaryDaily cost and token totals for a date range
GET/api/v1/analytics/hourly-usageHourly cost and token totals for a date range
POST/api/v1/analytics/queryAd-hoc raw query (SQL-like filter expression)

Example: cost-by-model

http
GET /api/v1/analytics/cost-by-model?from=2026-02-01T00:00:00Z&to=2026-02-24T00:00:00Z
Authorization: Bearer tok_live_...

HTTP/1.1 200 OK
{
  "data": [
    { "model_provider": "anthropic", "model_id": "claude-sonnet-4-6", "total_cost_usd": 12.45, "total_tokens": 1250000, "event_count": 842 },
    { "model_provider": "openai",    "model_id": "gpt-4o",            "total_cost_usd": 8.32,  "total_tokens":  830000, "event_count": 521 }
  ],
  "total": 2
}

Example: daily-summary

http
GET /api/v1/analytics/daily-summary?from=2026-02-01T00:00:00Z&to=2026-02-07T00:00:00Z
Authorization: Bearer tok_live_...

HTTP/1.1 200 OK
{
  "data": [
    { "date": "2026-02-01", "total_cost_usd": 3.21, "input_tokens": 450000, "output_tokens": 120000, "event_count": 201 },
    { "date": "2026-02-02", "total_cost_usd": 2.87, "input_tokens": 380000, "output_tokens": 105000, "event_count": 178 }
  ],
  "total": 7
}

Event Field Reference

Do not include input_cost_usd, output_cost_usd, or total_cost_usd. Cost is stamped server-side by the Price Registry. Submitting non-zero cost values causes a 400 rejection.

FieldTypeRequiredNotes
schema_versionintegerrequiredAlways 1
event_idstring (UUIDv7)optionalAuto-generated if omitted. Used for deduplication.
model_providerstringrequired"anthropic", "openai", "google", etc.
model_idstringrequired"claude-sonnet-4-6", "gpt-4o", etc.
input_tokensintegerrequiredPrompt tokens consumed
output_tokensintegerrequiredCompletion tokens generated
total_tokensintegerrequiredAuthoritative billing total (server rejects cost fields)
timestamp_clientstring (RFC 3339)optionalClient-side event time. Defaults to server receipt time.
application_idstringoptionalLogical application name for attribution
user_idstringoptionalEnd-user identifier. Hashed before storage.
team_idstringoptionalTeam or cost-centre identifier
environmentstringoptional"production", "staging", "development"
metadataobjectoptionalArbitrary key/value dimensions. Max 64 keys, string values only.
tagsobjectoptionalKey-value labels. Max 32 entries, string keys and values.

Error Codes & Retry Guidance

SDKs automatically retry on 5xx errors with exponential backoff (3 attempts). Do not retry 4xx errors — they indicate a payload or auth problem.

CodeTitleDescription
202AcceptedEvent durably written to Redpanda. Safe to discard your copy.
400Bad RequestMalformed JSON or invalid field values. Fix the payload; do not retry.
401UnauthorizedMissing or invalid API key. Check the Authorization or X-API-Key header.
403ForbiddenAPI key is valid but lacks permission for this operation.
413Payload Too LargeBatch body exceeds 5 MB. Split into smaller batches.
422Unprocessable EntitySchema validation failed. Check the "details" array in the response.
429Too Many RequestsRate limit exceeded. Check the Retry-After header (seconds).
5xxServer ErrorTransient server error. SDKs retry automatically (3 attempts, exponential backoff).

Example error response

json
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": "validation failed",
  "details": [
    { "field": "total_tokens", "message": "must be >= input_tokens + output_tokens" }
  ]
}