Integrations
Read what every request cost — inline on the response, per request by id, the credit balance, or as a settled export you poll into your own billing system to rebill end-customers at your margin.
Every route here is read-only and takes an ordinary xpl_ inference key, under the one host:
https://api-pr-1743.preview.experientiallabs.ai/v1 (also https://api-pr-1743.preview.experientiallabs.ai/api/v1).https://api-pr-1743.preview.experientiallabs.ai/api/v1.New to the surface? Start with Integrate the gateway.
Cheapest-to-wire to most complete:
usage.cost (USD) off the response — stamped on every Chat/Responses/Messages reply, no request flag needed. Good for showing a cost immediately; not a durable feed on its own.x-request-id to GET /api/v1/generation?id= for cost and token detail.GET /api/v1/usage is a cursor-paginated feed of settled per-request rows with attribution, real cost, and token counts — the feed a billing sync job should poll.Experiential stamps the settled cost inline on the usage object of every Chat Completions, Responses and Anthropic Messages reply, in USD, as an extension of the standard usage object. No request flag is needed: usage.cost is stamped whenever the engine emits a usage object, so usage is always included (both usage: { include: true } and stream_options: { include_usage: true } are accepted as no-ops). The one exception is a request sent with an Idempotency-Key: it carries no usage.cost on the original or on a replay, so the bytes stay identical; treat the field as optional and read the export for those.
usage (and cost); on a stream the cost rides the final usage chunk when the engine emits one (the message_delta event on the Messages surface), so request that terminal frame the way you already do for OpenAI.cost: 0, sets is_byok: true, and puts the upstream provider’s own attributed charge on usage.cost_details.upstream_inference_cost (present for BYOK only; omitted entirely for platform-funded traffic rather than reported as a wrong or zero value).Idempotency-Keyed completion returns the original bytes with no injected fields; Experiential skips annotation on those requests entirely. (X-Client-Request-Id is correlation/affinity, not a replay key, and is still annotated.)Every completion response carries an x-request-id header — the gateway request id. Read it off the response and pass it back to GET /api/v1/generation?id=to pull that one request’s settled cost and token detail. The endpoint accepts both the bare id and the gen-<request_id> form it echoes:
# The completion response returns the id in the x-request-id header;# pass it (bare, or as gen-<id>) to /generation.curl "https://api-pr-1743.preview.experientiallabs.ai/api/v1/generation?id=$REQUEST_ID" \-H "Authorization: Bearer xpl_..."
The read is org-scoped: a request id from another organization 404s exactly like an unknown one. The Anthropic Messages surface sets the same x-request-idheader (read it with the Anthropic SDK's with_raw_response); Claude Code exposes no response headers, so read a Claude Code run's cost from the usage export filtered on the key the run used.
Two account-level reads for the running totals:
GET /api/v1/credits — {data:{total_credits, total_usage}} in USD; the remaining balance is total_credits − total_usage.GET /api/v1/activity — a recent activity rollup for the org.GET /api/v1/usage returns settled rows, newest first, with a keyset cursor — the feed a billing sync job should poll:
curl "https://api-pr-1743.preview.experientiallabs.ai/api/v1/usage?limit=1000" \-H "Authorization: Bearer xpl_..."
The response is { "data": [ ... ], "next_cursor": { ... } | null }. Each row in data carries:
| Field | Meaning |
|---|---|
| id | The request id. |
| created_at | Settlement timestamp (ISO 8601). |
| model | The model slug. |
| provider | The winning provider (nullable). |
| attribution_label | The safety_identifier / user you set on the request; null when neither was sent. |
| real_cost_usd | Always-real per-call cost in USD (charged credits + attributed BYOK pass-through). 0 with pricing_known false means “unpriced”, not free. |
| cost_usd | Charged platform-credit cost in USD. |
| estimated_cost_usd | Estimated / attributed cost in USD. |
| pricing_known | Whether a price was known for the request. |
| input_tokens · output_tokens · cached_input_tokens · reasoning_tokens | Token counts. |
| status | Terminal status of the request. |
| api_key_id | The key that made the request (nullable). |
Filter by ?attribution_label=<id> (plus optional window, model, api_key_id, status, limit) to pull one end-customer’s requests. When a full page comes back, next_cursor is { cursor_ts, cursor_id, cursor_after }; pass those three values back as the cursor_ts, cursor_id, and cursor_after query params for the next page, and persist them so the next run resumes where you stopped. next_cursor is null on the last page. This is a pull/poll model today; there is no push webhook yet.
Pass the OpenAI-standard safety_identifier on every request (legacy alias: user). Experiential records it as the request’s attribution label and rolls usage up per end-user — no per-customer key required.
curl https://api-pr-1743.preview.experientiallabs.ai/v1/chat/completions \-H "Authorization: Bearer xpl_..." \-H "Content-Type: application/json" \-d '{"model": "claude-fable-5.1","messages": [{"role": "user", "content": "..."}],"safety_identifier": "cust_8842"}'
The label then appears as attribution_label on the matching /api/v1/usage row, so you can group cost and tokens by your own customer id without running a separate key per customer. safety_identifier is one of two attribution models — see key-per-customer on the Account API.
The common shape: you sell consumption-based credits, you charge each customer for what their inference actually cost you (plus your margin), and a billing system such as Metronome, Orb, or Stripe metered billing turns metered usage into invoices. The usual integration is a pull-based sync off the settled usage export, not bookkeeping on each response. This is how it fits together.
safety_identifier (alias user) on each call. One org key serves all of them; the id comes back as attribution_label on the export row. If you need per-customer revocation or daily caps as well, mint a key per customer instead (Account API) and group by api_key_id.usage.cost on the reply is for showing the number; it is not a durable ledger (a client can disconnect mid-stream, a retry can double-count, and a request sent with an Idempotency-Key carries no usage.cost at all, on the original or a replay). The export is the settled ledger: every request settles exactly once, rows never change after they settle, and failed requests appear with their status and whatever cost settled for them.window=24h|7d|30d, default 7d), and next_cursor pages older within that one call; it is not a checkpoint to resume from on the next run. Each run therefore starts at the top: call GET /api/v1/usage?limit=1000, follow next_cursorpage by page, and stop once a whole page is older than your last successful run’s start time minus a grace period that covers your longest request (a few minutes is plenty). Dedupe on the row id. Run at least daily: rows older than the 30-day window are no longer exportable.id (insert, on conflict do nothing) in one local transaction, then have a separate delivery step push unsent rows to the meter and mark them sent. A meter call is an external HTTP write and cannot share your database transaction; the outbox is what makes a crash between the two safe to replay.customer_id from your mapping of attribution_label, transaction_id = the row id (the ingest API deduplicates on it, so a redelivery is a no-op), timestamp = created_at, and properties inference_cost_usd = real_cost_usd plus model, provider, the token counts, status, pricing_known, and whether the row was BYOK. Orb and Stripe meters take the same shape: idempotency key = row id, value = cost.inference_cost_usd per customer per period, and put your credit rate (the margin) on that metric. The sync job ships raw cost; the markup and the conversion to your credits live where your pricing already lives, so a price change never touches the integration.cost_usd column (charged credits only) summed over a day should agree with the movement of total_usage on GET /api/v1/credits over that day; both are settled charged spend. Rows with a null attribution_label are requests your code did not tag: surface them, do not drop them. To audit one customer, pull ?attribution_label=<id>.Which quantity to bill: cost_usd is the platform credits the request was charged; estimated_cost_usdis the attributed value of a BYOK request at catalog list rates, which is an estimate and never the provider’s invoice; and real_cost_usd is the two added together, so a BYOK row shows its estimated spend instead of a $0 charge. Bill real_cost_usd only if you accept that the BYOK part is estimated; otherwise bill cost_usd and meter BYOK rows on tokens (they carry is_byok, tokens, and attribution). A row with pricing_known: false carries a cost of 0 because no price was known, not because it was free; decide up front whether to bill those on tokens or skip them. Because the feed is settled, you can rebill at any margin.
GET /v1/models and telemetry work, but a paid completion is refused.usage.cost / real_cost_usd is the real USD cost. This is the default and what you rebill against.cost: 0 (inline is_byok: true, with the upstream charge on cost_details.upstream_inference_cost) and the export row’s real_cost_usdis the attributed pass-through cost. Those rows still carry tokens and attribution, so you can still meter your customers on usage; just don’t expect a platform charge on them.