TokenGate

Multi-tenant quota management as a service. Define metered resources, attach quota and rate-limit policies to subjects, and atomically check-and-consume usage — with reservations, an append-only ledger, and threshold alerts.

Interactive API docs · /llms.txt (agent guide) · OpenAPI schema · Status

Coding agents: fetch https://tokengate.rodmena.co.uk/llms.txt for the complete integration contract in plain text — auth, request shapes, the reserve/commit flow, and every error code — then work from /openapi.json for exact schemas.

What it does

Atomic enforcement

One call evaluates every applicable policy for a subject. All items commit or none do — no partial consumption.

Two enforcement modes

strict counts inside a PostgreSQL transaction (money-like budgets); fast counts in Redis with an exactly-once ledger write-behind.

Reservations

Reserve an estimate before an expensive call, commit the actual after. Uncommitted holds count against quota and expire automatically.

Auditable

Append-only usage ledger and admin audit log, rollup reporting, and HMAC-signed threshold-alert webhooks.

Authentication

Every request carries a tenant-scoped API key as a bearer token. Keys are stored only as a SHA-256 hash and shown exactly once, at mint time.

Authorization: Bearer tg_<key_id>_<secret>

Scopes: consume (data plane), admin:read, admin:write (catalog and keys), ops:jobs (operator job endpoints). Ask your TokenGate operator for a key — this page cannot mint one.

Consume in one call

curl -X POST https://tokengate.rodmena.co.uk/v1/consume \
  -H "Authorization: Bearer $TOKENGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "user_42",
    "items": [{"resource": "llm.tokens", "amount": 1200}],
    "idempotency_key": "req-8f2c1a"
  }'

A success returns 200 with the post-state of every policy that applied:

{
  "allowed": true,
  "event_id": "01KYB...",
  "degraded": false,
  "results": [
    {
      "resource": "llm.tokens", "policy_name": "monthly-budget",
      "kind": "quota", "mode": "strict", "allowed": true,
      "limit": 100000, "used": 1200, "remaining": 98800,
      "window": {"id": "mo1:1780...", "reset_at": "2026-08-01T00:00:00+00:00"}
    }
  ]
}

Reserve → commit

When the true cost is only known afterwards, hold an estimate first. The hold counts against quota until you commit the actual, release it, or its TTL expires.

# 1. hold an estimate
POST /v1/reserve  {"subject": "user_42", "ttl_seconds": 120,
                   "items": [{"resource": "llm.tokens", "amount": 4000}]}
# -> {"reservation_id":"01KYB...","status":"held", ...}

# 2a. settle with the real amount   |   2b. or give the hold back
POST /v1/reservations/{id}/commit   {"actuals":[{"resource":"llm.tokens","amount":3271}]}
POST /v1/reservations/{id}/release  {}

Endpoints

Method & pathScopePurpose
POST /v1/consumeconsumeAtomic check-and-consume
POST /v1/check consume Dry run — would this be allowed?
POST /v1/refundconsumeReturn usage to the quota
POST /v1/reserveconsumeHold an estimate
POST /v1/reservations/{id}/commit consume Settle a hold with actuals
POST /v1/reservations/{id}/release consume Drop a hold
GET /v1/subjects/{subject}/usage consume Current usage per policy
GET /v1/subjects/{subject}/entitlements consume Policies in effect
POST /v1/resources, /v1/policies, /v1/plans admin:write Define the catalog
PUT /v1/subjects/{subject}/assignment admin:write Attach a plan to a subject
GET /v1/ledger, /v1/usage/summary admin:read Ledger and rollup reporting

The full set, with schemas, is in the interactive docs.

Errors

Every failure is application/problem+json (RFC 7807) with a stable code. Retryable responses carry Retry-After.

StatusCodeMeaning
429 quota_exceeded A quota would be exceeded. Honor Retry-After; reset_at says when the window rolls.
429 rate_limited A token-bucket rate limit is empty. Retry after the header value.
409 idempotency_key_reuse Same key, different payload. Use a fresh key.
409 idempotency_in_flight An identical request is mid-flight. Retry shortly.
422 unknown_resource The resource is not defined for this tenant.
503 backend_unavailable A backend is down and policy says deny. Retry; never treat as "allowed".

Rules worth knowing

Always send an idempotency key on consume, reserve, commit, and refund. A retry with the same key returns the original outcome instead of consuming twice; the response carries X-TokenGate-Replayed: true.

A batch is all-or-nothing. If any item's policy would be exceeded, nothing is consumed and the response names the most-constraining policy.

Failures never oversubscribe. Under a partial outage TokenGate may deny early or serve flagged degraded: true per policy configuration, but it will not silently let usage through uncounted.