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.
One call evaluates every applicable policy for a subject. All items commit or none do — no partial consumption.
strict counts inside a PostgreSQL transaction (money-like
budgets); fast counts in Redis with an exactly-once ledger
write-behind.
Reserve an estimate before an expensive call, commit the actual after. Uncommitted holds count against quota and expire automatically.
Append-only usage ledger and admin audit log, rollup reporting, and HMAC-signed threshold-alert webhooks.
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.
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"}
}
]
}
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 {}
| Method & path | Scope | Purpose |
|---|---|---|
POST /v1/consume | consume | Atomic check-and-consume |
POST /v1/check |
consume | Dry run — would this be allowed? |
POST /v1/refund | consume | Return usage to the quota |
POST /v1/reserve | consume | Hold 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.
Every failure is application/problem+json (RFC 7807) with a
stable code. Retryable responses carry Retry-After.
| Status | Code | Meaning |
|---|---|---|
| 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". |
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.