Per-customer margin report (operator only)
const url = 'http://localhost:8080/v1/customers/example/margin';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url http://localhost:8080/v1/customers/example/margin \ --header 'Authorization: Bearer <token>'Rated usage revenue vs stamped provider COGS for a window (ADR-079). A usage unit-economics view — base fees, credits, and taxes are not included. Per-model margin renders only where the price that billed the usage names the model; other revenue is reported unattributed, never allocated by heuristic.
The window is [from, to): from inclusive, to exclusive. A
missing bound defaults to the last 30 days ending at the customer’s
current time, which is the test clock’s frozen time when the
customer is on a test clock. from and to take RFC 3339 instants
only (no bare dates), and from must be before to.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Query Parameters
Section titled “Query Parameters”Window start, inclusive. RFC 3339. Default is 30 days before the customer’s current time.
Window end, exclusive. RFC 3339. Default is the customer’s current time (test clock time for a customer on a test clock).
Responses
Section titled “Responses”Margin report
object
Total rated usage revenue in the window, an estimate at each subscription’s current prices. Only active and trialing subscriptions are counted.
Currency of revenue_cents (the customer’s price rules). Omitted when the window’s revenue spans more than one currency or the customer has no priced meter.
Total stamped provider cost in US cents, as a decimal string (e.g. “275.75”).
Currency of cost_cents. Always USD.
(revenue − cost) / revenue in basis points. Omitted when revenue is 0, when revenue_currency is not cost_currency, when costed_events is 0 (no provider cost known), or when unresolved_events is above 0 (part of the cost unknown, so the margin would read too high).
object
This model’s stamped provider cost in US cents, as a decimal string.
True only when the price that billed the usage names this model — margin is never heuristically allocated.
This model’s (revenue − cost) / revenue in basis points, by the same formula as the report’s margin_bps. Omitted whenever the report’s margin_bps is omitted for currency or unresolved_events, and when the row is not attributed or has no provider cost of its own.
Token events with costable dims but no cost — no rate matched (add a rate to cost new events), or the cost, quantity × unit_cost_cents, was too large to record (about $9.2 trillion or more, either sign).
Events in the window that carry a stamped provider cost (zero cost included). 0 means the cost side is unknown.
Whether the tenant has any provider cost rate. False with costed_events 0 means no rates are set up.
Example generated
{ "customer_id": "example", "from": "2026-04-15T12:00:00Z", "to": "2026-04-15T12:00:00Z", "revenue_cents": 1, "revenue_currency": "example", "cost_cents": "example", "cost_currency": "example", "margin_bps": 1, "by_model": [ { "model": "example", "cost_cents": "example", "revenue_cents": 1, "attributed": true, "margin_bps": 1 } ], "unattributed_revenue_cents": 1, "unresolved_events": 1, "costed_events": 1, "cost_rates_configured": true}from or to is not RFC 3339, or from is not before to.
object
object
One of invalid_request_error, authentication_error,
rate_limit_error or api_error. Every error the API
answers has this shape, including an unknown path (404,
code route_not_found) and a method the path does not take
(405, code method_not_allowed, with an Allow header).
The few exceptions are listed in docs/api-surface.md.
The request field the error is about. Sent only when the error
names one. A request body may hold only the fields its
operation lists, spelled and cased as listed: any other
field, or one the server sets (such as id), is refused
with 422 and param naming it. An operation with no request body lists no fields: send
no body, {} or null. The Stripe and Postmark webhooks and
the LiteLLM callback take their sender’s body as it comes.
Example
{ "error": { "type": "invalid_request_error" }}Customer not found.
object
object
One of invalid_request_error, authentication_error,
rate_limit_error or api_error. Every error the API
answers has this shape, including an unknown path (404,
code route_not_found) and a method the path does not take
(405, code method_not_allowed, with an Allow header).
The few exceptions are listed in docs/api-surface.md.
The request field the error is about. Sent only when the error
names one. A request body may hold only the fields its
operation lists, spelled and cased as listed: any other
field, or one the server sets (such as id), is refused
with 422 and param naming it. An operation with no request body lists no fields: send
no body, {} or null. The Stripe and Postmark webhooks and
the LiteLLM callback take their sender’s body as it comes.
Example
{ "error": { "type": "invalid_request_error" }}The window is longer than one year.
object
object
One of invalid_request_error, authentication_error,
rate_limit_error or api_error. Every error the API
answers has this shape, including an unknown path (404,
code route_not_found) and a method the path does not take
(405, code method_not_allowed, with an Allow header).
The few exceptions are listed in docs/api-surface.md.
The request field the error is about. Sent only when the error
names one. A request body may hold only the fields its
operation lists, spelled and cased as listed: any other
field, or one the server sets (such as id), is refused
with 422 and param naming it. An operation with no request body lists no fields: send
no body, {} or null. The Stripe and Postmark webhooks and
the LiteLLM callback take their sender’s body as it comes.
Example
{ "error": { "type": "invalid_request_error" }}