Attribution Data API
Get session cost breakdown
Returns per-session cost totals, ordered by total cost descending. Call it with a GET request to /attribution-data/sessions, authenticated with a restricted API key and the "costs:read" scope.
Returns per-session cost totals, ordered by total cost descending. Include `session_id` on your usage events to group them into sessions; optionally include `session_type` (any string label, e.g., "support-agent", "batch-summarizer"). Events without `session_id` are excluded. Events without `session_type` appear here with `session_type` "unknown". The `summary` object reflects only the sessions returned in this response (after limit/offset), not the full date range. `population_sessions` is the range-wide count: when it exceeds the length of `sessions`, request the next page. `limit` is capped at 500, so a larger value returns 500 rows rather than the whole range. 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). If start_date is not before end_date the response is empty rather than an error. |
end_date | query | string | Yes | End of date range (ISO 8601, exclusive). |
session_type | query | string | No | Exact-match filter on the session_type value (e.g., "support-agent"). |
limit | query | number (double) | No | Maximum sessions to return, ordered by total cost descending (default 100, capped at 500). Compare `population_sessions` with the returned array length to detect a truncated page. |
offset | query | number (double) | No | Number of sessions to skip for pagination (default 0). |
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 |
|---|---|---|
sessions | object[] | Sessions in this page, ordered by total cost descending. |
sessions.session_id | string | The session_id value shared by this session's usage events. |
sessions.session_type | string | Freeform session category from the session_type event field (e.g., "support-agent", "batch-summarizer"); "unknown" when the events carry no session_type. |
sessions.total_cost | string | Total cost for the session (USD, decimal string). |
sessions.event_count | number (double) | Number of usage events in the session. |
population_sessions | number (double) | Sessions in the WHOLE date range, not just this page. This is how you tell that you did not receive everything: when `population_sessions` exceeds the length of `sessions`, there are more pages. `limit` is capped server-side at 500, so asking for a larger page gets you 500 rows and this number, never the whole range in one response. Silent truncation reads as completeness, which is the reason the field exists rather than being left for the caller to infer. Named `population_sessions` and NOT `total_sessions` because `summary.total_sessions` on this same payload means the PAGE length. |
summary | object | Aggregates over the sessions returned in this response only (after limit/offset), not over the full date range. |
summary.avg_cost_per_session | string | Average total_cost across the sessions in this response (USD, decimal string). |
summary.total_sessions | number (double) | Number of sessions in this response; always equals the length of the sessions array, never a range-wide total. Page until fewer than limit sessions are returned. |
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 and end_date are valid ISO 8601 dates and fix the field named in error.message. |
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.