Usage, cost and model spend
Per-org usage metering plus model-spend metering: what the agents an org runs (Claude Code, Codex,
Grok and Muse lanes, QA Commander) spent on model calls. It is on by default (platform setting usage.metering,
the kill switch for every route and tool below). Metering only: nothing is charged, nothing is blocked, and no
billing flag or payment provider is involved.
What you can do
| Need | REST | MCP tool |
|---|---|---|
| This month's usage and cost, every source, and the advisory budget state | GET /v1/orgs/{orgId}/usage?period=YYYY-MM | usage_get |
| Set or lower the advisory monthly budget | PUT /v1/orgs/{orgId}/usage/budget | none (a person loosens a budget) |
| Record what agents spent on model calls | POST /v1/orgs/{orgId}/usage/model-events | usage_model_record |
| Read model spend grouped by model, engine, agent, lane or day | GET /v1/orgs/{orgId}/usage/models?period=&groupBy= | usage_model_summary |
The usage page (Settings > Usage and cost) shows the same numbers: its Agent models row and the by-model table are this ledger, and the month's total includes it, so the budget warning sees it.
Recording model spend
POST /v1/orgs/{orgId}/usage/model-events takes { "events": [ ... ] }, 1 to 500 events per call:
{ "events": [ {
"externalRef": "claude:3f2a:msg_01AbC",
"provider": "anthropic",
"model": "claude-opus-5-5",
"engine": "claude-code",
"effort": "high",
"inputTokens": 1200,
"outputTokens": 340,
"cacheTokens": 5000,
"costMicroUsd": 21500,
"agentLabel": "Claude orchestrator",
"laneSlug": "gq-usage-model-spend-ingest",
"occurredAt": "2026-10-06T21:04:11Z"
} ] }
externalRefis your idempotency key: a provider message id or a transcript line id. Use a value that is unique across everything you send (a provider message id, a UUID). A replay by the same org (the same ref again, in a later call or twice inside one call) comes backrecorded: falseand changes nothing, so a retry is always safe. The first record wins even if the replay carries different numbers. Replays may race: batches are applied one at a time per org.costMicroUsdis an integer in micro-USD (1 USD = 1,000,000) and you price the call. GoodQA never guesses a price; a subscription-billed agent ships its notional cost. It is required: a silent zero is the problem this ledger exists to end.provider,model,engineandeffortare stored lower-case.occurredAtdefaults to now; it may not be before 2020-01-01 or more than a day ahead.- The call is atomic: one bad event (400) rejects the whole batch, so nothing is half-written.
- Response:
{ org, recorded, duplicates, events: [{ externalRef, recorded }] }in request order.
A ref another org already recorded is refused with 409 usage_model_ref_other_org and nothing in the batch is
written, the same rule as the build-farm ledger: the ref is one global key. So a scheme two orgs could both produce
(line-1) collides by design; prefix such refs with something unique to your org. The cost of the rule is that an org
with a write key could pre-claim refs another org is about to use, and learns only that a ref exists; provider message
ids are unguessable, so this is a denial-of-metering concern and nothing is charged or exposed.
Each month is capped per org: a batch that would take the org's summed cost, or any summed token count, for a UTC month
above 1,000,000,000,000,000 is refused whole with 422 usage_model_month_ceiling, so a served total is always an exact
integer.
Who may record
| Credential | May record? |
|---|---|
| Org admin, signed in | yes |
User token (write ability) whose owner is an org admin | yes (stored as user_token) |
Org API key holding write and the explicit-only usage:write family | yes (stored as api_key) |
Org API key without usage:write (including the default read+write key and a key narrowed to usage or issues) | no, 403 |
| Read-only key, user-scoped key, key capped below admin | no, 403 |
| Persona key | no, persona_key_forbidden (a persona's own model calls are already metered by GoodQA) |
| Runner key | no, runner_key_forbidden (and a runner key cannot name usage:write at mint) |
| Org member who is not an admin, or someone outside the org | 403, 404 |
usage:write is its own key family so an ingest-only key can hold usage:write without usage and never read the
cost figures back; a key that names no family does not reach it. Add it when you mint the key:
scopes: ["write", "usage:write"].
Reading it back
GET /v1/orgs/{orgId}/usage/models?period=2026-09&groupBy=model (org admin; key family usage):
{ "org": { "id": "..." }, "period": "2026-09", "groupBy": "model", "truncated": false,
"totals": { "events": 6, "inputTokens": 7300, "outputTokens": 1380, "cacheTokens": 5260, "totalTokens": 13940, "costMicroUsd": 307000 },
"groups": [ { "key": "claude-opus-5-5", "providers": ["anthropic"], "events": 3, "inputTokens": 3100, "outputTokens": 610,
"cacheTokens": 5010, "totalTokens": 8720, "costMicroUsd": 243000 } ] }
groupBy is model (default), engine, agent, lane or day. A calendar month is [first of the month, first of the next) in UTC. day reads oldest first; the others read biggest spend first. A null key is an event with no label.
At most 1000 groups are returned (truncated: true when more exist); totals always covers every event.
GET /usage gains a models object (the same totals) and totalCostMicroUsd includes it, so the advisory budget
(budget.state: none, ok, warning, over) reflects it. The budget only warns: a write is never refused because the
budget is over.
Switching it off
Turn usage.metering off (platform setting, default on): every route above answers an unknown route's 404 before
authentication, the MCP tools are not registered, and the served OpenAPI leaves the four paths out.
Not done here
No charging, invoicing or Stripe. No price table: callers price their own calls. No retention job yet (the ledger is one small row per call). A shipper that reads agent transcripts and calls this API is a separate piece of work.
Reference
- API reference: tag
orgs(/orgs/{orgId}/usage,/orgs/{orgId}/usage/budget,/orgs/{orgId}/usage/models,/orgs/{orgId}/usage/model-events) in/v1/openapi.json. - MCP tools:
usage_get,usage_model_summary,usage_model_record; every key lists them and the route decides. See the MCP tool reference. - Proof script:
apps/api/scripts/usage-model-loopback.mjsposts 1,000 synthetic events in batches, replays one batch (it must be a no-op) and checks the summary against what it sent.
Model spend dashboard
Settings > Usage and cost adds a Model spend section for org admins. The month picker applies to the section and uses UTC calendar months. Cost is rounded once to cents with integer arithmetic; tokens include input, output and cache tokens. Choose model, engine, agent, lane or day and activate a column heading to sort the returned groups. When the API caps a grouping at 1,000 groups, the page labels that limit and keeps the API's complete total.
The daily chart stacks costs by engine. groupBy=day additionally returns engines: [{ key, costMicroUsd }] on each
day, derived in the same SQL statement as the day total. Existing summary fields and other groupings keep their
contract. The chart uses the six engines with the largest monthly cost and combines the remainder as Other engines;
zero-spend days remain visible. The expandable Daily spend data table supplies the same exact values to keyboard
and screen reader users. A server with the older daily response shows a single All engines series.
The warning banner uses the server's advisory budget state across all priced sources, and never blocks model calls.
An empty month links to these ingest instructions. The existing default-on usage.metering setting gates the entire
usage API and dashboard; this view adds no separate switch.