How a receipt is built
This page explains the per-request receipt: the token, cost, and hash fields behind every row in the request feed, the receipt view, and the ledger export.
What it is
A receipt is one metered request: what the provider reported, the cost we computed from those numbers against a frozen price list, and a hash binding the fields so the row cannot be edited later. We never estimate. An unreported token count is stored as null, and a model absent from the price list gives a null cost.
How it is computed
1. Usage. We record only what the provider returned: prompt/input tokens, completion/output tokens, cached and cache-creation tokens, reasoning tokens, the finish reason, and the upstream status. Nothing is read from request or response bodies.
2. Cost. Price rows are micro-USD per million tokens (1,000,000 micro-USD = $1). Arithmetic is integer i128: products are summed first, then floor-divided by 1,000,000 exactly once, so per-component rounding cannot accumulate.
| Class | Rule |
|---|---|
| OpenAI input | cached_tokens is inside prompt_tokens: full rate on the difference, cached rate on the rest |
| Anthropic input | input_tokens excludes cache reads and writes, so both are added on top |
| Output | completion_tokens at the output rate; reasoning_tokens sit inside it, never charged twice |
| Unreported detail | Contributes zero, as on the provider invoice |
Worked example: gpt-4.1 under rpl-2026-08-05 (input $2.00/MTok, cached $0.50/MTok, output $8.00/MTok), 5,211 prompt tokens of which 3,647 cached, and 804 completion tokens.
(5211 - 3647) x 2,000,000 = 3,128,000,0003647 x 500,000 = 1,823,500,000804 x 8,000,000 = 6,432,000,000- sum
11,383,500,000, floored by 1,000,000 = 11,383 micro-USD, displayed0.011383
3. Hash. Both published recipes share one encoding: fields joined with U+001F, SHA-256, lowercase hex. Integers are base-10 without leading zeros, null is the empty string, an array is comma-joined, an empty array is the empty string.
recovea-chain-v1 hashes, in order: recipe id, previous row hash, requestId, timestampMs, provider, model, route, tokensIn, tokensOut, tokensCached, integer costMicroUsd, priceListVersion, basis, mandateId. Lever and rowHash are not inputs. recovea-receipt-v1 is a per-row variant without chain linkage; its status is below.
Worked example, the first committed recovea-chain-v1 vector. Join recovea-chain-v1, 64 zeros (genesis), req_9f2c41d87ab34e60, 1750000000000, openai, gpt-4.1, /v1/chat/completions, 1200, 340, 0, 4180, rpl-2026-06-01, measured, and the empty string. SHA-256 gives 2f542e00b0403c4e95403ef7fe6ac40d24c6598e0d100ee6cc6ec8a6d18152e2.
What it does not include
- Recovea fees, taxes, provider minimums, commitment discounts, negotiated rates. The meter measures at list price; the provider invoice is the bill.
- Prompts, completions, and secrets. No receipt field can hold request content by schema.
- Dedup followers, which made no provider call and carry a null cost.
- Unpriced rows in the export: a row chains only once it has both token counts and a cost.
- Any deduction for retries or failures. Both stay in cost.
Limits and current status
- Recipes.
recovea-chain-v1is what production computes and stores.recovea-receipt-v1has committed golden vectors, but nothing in the API writes or serves it. The receipt endpoint uses receipt-v1 field names while labelling the rowrecovea-chain-v1and returning the chain hash. Verify against the export, not that view. - Null rendering. The read path coerces unreported tokens and unpriced costs to
0before serialization, so a feed row can show zeros where the stored receipt holds null. The stored row and the export are right; the rendered JSON is not. - Attribution drift. In the deployed read path
projectalways rendersdefault,envalwayslive, andkeyPrefixis the key id, not a masked key. Project and turn attribution rides separate tables, not the receipt row. - Price list. Long-context tiers, one-hour cache writes, regional uplifts, and fast-mode SKUs sit outside the price schema, so those requests price at the base rate. That under-measures, never overcharges.
- Budgets. Only workspace-scoped caps reach the gateway. Key- and project-scoped caps can be stored, but are not enforced.
- Alerts. The sweep targets one pass every 60 seconds, so a fast burst can cross a threshold and reach a cap between passes. Alerts warn on the way up; they are not an ordering guarantee.
- Counterfactual. Measured, never applied. Levers default off and are activated in no deployed environment, so no receipt carries a fired lever.
- CPSO. Figures are at list price. Invoice reconciliation at period close runs only from a hand-supplied fixture into in-memory stores. It is not scheduled or wired to the database.
- Score. The engine is not shipped. The endpoint returns a null score. No workspace has a score today.
Where to check us
- Export a period:
GET /platform/v1/ledger/export?period=YYYY-MM&format=jsonlon a session, orGET /v1/ledger/exportwith anrcv_live_key. The header line carries the recipe id, genesis, chain head, row count, and the price list versions pinned. The two routes serve the same rows under different field spellings; the map is on the ledger page. - Re-derive each row hash from the fourteen
recovea-chain-v1fields. Hash the integer micro-USD value, never a formatted string: that iscostMicroUsdon the session route, andcost(JSON, JSONL) or thecost_micro_usdcolumn (CSV) on the key-authed route. Check eachpreviousHashagainst the priorrowHash, and the last hash againstchainHead. - Re-derive each cost from
tokensIn,tokensOut,tokensCachedand the row'spriceListVersion, applying the rules above and flooring once.
You need only a SHA-256 implementation; nothing here calls back to us. Any row where your number differs from ours is a bug we want reported.