Attribution Data API
Get cost per declared outcome
Returns the true cost per unit of value: for every outcome your spans declare (see the tracing guide), completed, failed, and unresolved units side by side. Call it with a GET request to /attribution-data/by-outcome, authenticated with a restricted API key and the "costs:read" scope.
Returns the true cost per unit of value: for every outcome your spans declare (see the tracing guide), completed, failed, and unresolved units side by side. Use this to price against real unit economics; the cost of runs that failed stays visible as its own line instead of inflating the completed average. A unit is one execution of an outcome-declaring span. Its cost is the full cost of that span's call subtree, plus the session's untraced events when the outcome span is the session's only root span. Retried units (same unit_key across attempts) count once while their cost sums across attempts; retried_unit_count reports how many units needed more than one attempt. The date range filters on when a unit RESOLVED (its completion or failure event), or on last activity for unresolved units, so a unit's cost may predate the range. Units that declared an outcome, never resolved, and have been inactive longer than unresolved_after_hours are reported as unresolved; terminal-less units younger than that threshold are treated as still running and excluded. Requests count toward your per-API-key rate limit; sustained overruns return 429 with the standard error envelope.
Authentication and scopes
Authenticate with a restricted API key in the Authorization: Bearer <key> header. This endpoint requires the costs:read scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
start_date | query | string | Yes | Start of date range (ISO 8601, inclusive). Applies to unit resolution time, not to when the cost was incurred. |
end_date | query | string | Yes | End of date range (ISO 8601, exclusive). |
unresolved_after_hours | query | number (double) | No | Hours of inactivity after which a terminal-less unit is reported as unresolved rather than still running. Whole number between 1 and 8760; default 24. |
Response fields
Fields returned in the 200 response body. Nested objects are listed under their parent. A field marked optional may be absent from the body; one marked nullable is always present but its value may be null.
| Field | Type | Description |
|---|---|---|
items | object[] | One item per outcome value, ordered by total unit cost descending. |
outcome | string | The declared outcome value (the span's outcome option, e.g., "ticket_resolved"). |
completed_count | number (double) | Distinct units that completed inside the date range. Retried units (same unit_key across attempts) count once; a unit counts as completed when any of its attempts completed. |
completed_cost | string | Total cost of the completed units (USD, decimal string). Sums every attempt's full call-subtree cost, including failed attempts of a unit that later completed. A unit's cost may predate the date range; the range filters on when the unit RESOLVED, not on when it spent. |
cost_per_completed_unit | string nullable | completed_cost divided by completed_count (USD, decimal string). Null when completed_count is 0; a real "0" would mean completed units were free, which is a different fact than "nothing completed". |
failed_count | number (double) | Distinct units whose every attempt failed inside the date range. |
failed_cost | string | Total cost of the failed units (USD, decimal string). |
unresolved_count | number (double) | Units that declared this outcome but never reported completion or failure (for example, the process died) and have been inactive longer than unresolved_after_hours. Units still inside that inactivity window are treated as running and are not counted anywhere. |
unresolved_cost | string | Total cost of the unresolved units (USD, decimal string). |
retried_unit_count | number (double) | Units with more than one terminal (completion or failure) event under the same unit_key. An attempt that died without emitting a terminal event (process death) contributes its cost but does not increment this counter; it surfaces through the unresolved bucket instead. |
total_outcome_count | number (double) | Number of distinct outcome values in the range; equals items.length. |
Error codes
Errors return the standard error envelope with a code, message, and a correlationId you can quote to support.
| Status | When it happens |
|---|---|
400 | Invalid request data. Check that start_date is before end_date, both are valid ISO 8601 dates, and unresolved_after_hours is a whole number between 1 and 8760. |
401 | Missing or invalid API key. Send your key as Authorization: Bearer YOUR_API_KEY. |
403 | API key lacks the costs:read scope. Create or update an API key with the costs:read scope, then retry. |
429 | Rate limit or plan quota exceeded. Slow down and retry after the current window resets. |
Rate limits and retries
Rate limit or plan quota exceeded. Slow down and retry after the current window resets.