How cost per successful output works
This page explains the cost per successful output (CPSO) figure on the Unit economics page, served by GET /platform/v1/roi/cpso?period=YYYY-MM on Growth and above.
What it is
CPSO is measured spend on a route divided by the number of outputs on that route that met its success rule. Failed calls, retries, and duplicates cost money, so they stay in the numerator and never enter the denominator. It is reported per (route, calling key) pair for one UTC calendar month.
Every route has a rule. Unless you set one, a route uses the default rule: a 2xx response — the request completed without an error. That is the plainest reading of "a successful output", and it means the table has real numbers from your first metered request rather than waiting on configuration. Set a rule on a route to override it with something stricter, such as a specific finish-reason.
How it is computed
Inputs are metered receipt rows, one per request proxied through the gateway. Money is integer micro-USD, 1,000,000 to the dollar. No float touches the path.
- Per-request cost. The meter prices provider-reported tokens against a frozen price list, currently
rpl-2026-08-05, pinned on the receipt asrefPriceVersion. Rates are micro-USD per million tokens. Every rate-times-tokens product is summed ini128and floor-divided by 1,000,000 exactly once, so no per-component truncation accumulates. Cached tokens bill at the cached rate. Reasoning tokens are inside completion tokens. A model absent from the list prices to null. - Window. Half-open
[first of month, first of next month)in UTC, on the receipt timestamp. - Numerator.
SUM(cost_micro_usd)over receipts in the window, grouped byrouteandkey_id. - Denominator.
COUNT(*)over the same group, filtered by the route's rule fromsuccess_rules.status_2xxadmits HTTP 200 through 299.finish_reasonadmits an exact match on the reported finish reason. - Division.
spend_micro_usd / successes, integer, truncated toward zero. Zero successes returns null, not a ratio. - Display.
cpsoMicroUsdis the integer.cpsois that integer at six decimals, exact. Spend renders in whole cents, half away from zero.
Worked example. One route, one key, rule status_2xx.
| request | status | cost (micro-USD) |
|---|---|---|
| A | 200 | 4,520 |
| B | 200 | 6,800 |
| C | 200 | 15,300 |
| D | 500 | 1,200 |
A is 1,200 prompt tokens of which 400 cached, plus 340 completion, at $2.00 / $0.50 / $8.00 per million: (1200-400) x 2,000,000 + 400 x 500,000 + 340 x 8,000,000 = 4,520,000,000, floored by 1,000,000, is 4,520. Spend is 27,820, successes 3, CPSO 27,820 / 3 = 9,273, shown as 0.009273. Spend shows as $0.03.
Row hashes use recovea-chain-v1, the recipe every persisted row and export carries. A second recipe, recovea-receipt-v1, is published with golden vectors, but nothing in the API writes it. Both join canonical field strings with U+001F and take a lowercase-hex SHA-256.
What it does not include
- Requests not proxied through the Recovea gateway.
- Nothing is excluded for want of a rule. A route with no rule of its own is measured on the 2xx default, not dropped. (Before 2026-07-25 an unruled route was absent from the table entirely, which meant the table was empty until a rule was written through the API.)
- Routes ruled by
tag_present. The API accepts that type, but per-receipt tags are not stored, so those routes are dropped rather than guessed. - Cost of unpriced requests. A 2xx request with a null cost counts as a success and adds zero spend, which understates CPSO.
- Provider discounts, credits, committed-use terms, taxes, and your Recovea subscription fee.
- Output quality. The rule says a response completed, not that it was good.
- Long-context tiers, one-hour cache writes, regional uplifts, and fast-mode SKUs, which sit outside the price-list schema and price at the base rate.
Limits and current status
- CPSO. Figures are at list price. The
statebadge is a calendar comparison only: a row readssettledthe moment the month ends, whether or not anything reconciled. The close engine is written and tested, but it runs from a fixture against in-memory stores, no close clock is scheduled, and no correction row has ever settled a real period. Invoice reconciliation is planned, not wired. - Budgets. Only workspace-scoped caps reach the gateway. Key-scoped and project-scoped budgets can be created and stored, but nothing enforces them yet.
- Alerts. The sweep runs about once a minute, so a fast burst can cross a threshold and reach a cap between checks. Alerts warn on the way up. They are not an ordering guarantee.
- Counterfactual. Savings are measured and reported with
applied: false. Levers are not activated in any deployed environment, so no lever has changed a request. - Score. The engine is not shipped. The endpoint answers a null score. No workspace has a score today.
- Spend and attribution drift. Three layers read spend from different tables: CPSO and route spend from
receipts, attribution cuts fromrollup_cuts, daily totals fromrollup_daily. The rollups are written inside the ingest transaction, so they should agree, but nothing continuously proves it, and CPSO ignores thecost_statecolumn onrollup_cuts. The feed'senvfield is hardcoded tolive, becausereceiptshas no environment column.
Where to check us
- Export your chain with the key-authed
GET /v1/ledger/export?period=YYYY-MM&format=csv. Rows carryrecipe,previous_hash,row_hash,cost_micro_usd,price_list_version, and tokens. Re-derive eachrow_hashwith SHA-256 and confirm the links. Verification never calls home. The session routeGET /platform/v1/ledger/exportserves the same rows in camelCase —previousHash,rowHash,costMicroUsd,priceListVersion— and the field map for both is on the ledger page. - Re-price each row from its tokens and pinned
price_list_version. It must equalcost_micro_usdexactly. - The export carries no
status,finish_reason, orkey_id, so it cannot rebuild the denominator alone. Take those from the request feed and receipt detail, group by route and key, apply your rule, divide. Both sides are integer arithmetic over the same rows. If the numbers differ, send us the request ids.