Integrations
For products that wrap Experiential's inference and rebill their own end-users — a coding tool, an agent platform, a vertical SaaS. Route model calls through Experiential, attribute each call to one of your customers, and meter or rebill it.
Two base URLs, both under one host. Everything below uses a normal xpl_ inference key unless it says otherwise.
https://api-pr-1584.preview.experientiallabs.ai/v1 (OpenAI / Anthropic clients). Also served at https://api-pr-1584.preview.experientiallabs.ai/api/v1.https://api-pr-1584.preview.experientiallabs.ai/api/v1.If you are simply migrating an existing OpenRouter integration, start with OpenRouter compatibility.
Mint a key and make your first call, then wire the billing sync. The fastest path is to paste a setup prompt into a coding agent — it creates the account from your email and wires the gateway with no browser. Or do it by hand:
xpl_ key (see Quickstart), or POST https://api-pr-1584.preview.experientiallabs.ai/api/signup/instant with {"email": "...", "agree": true} to create an account and receive an xpl_ key in the response.https://api-pr-1584.preview.experientiallabs.ai/v1 and call models by their catalog slug (e.g. claude-fable-5.1).GET /v1/models and telemetry work, but a paid completion is refused.The core of a reseller integration is a billing sync job: pull settled per-request cost, map each row to the end-customer it belongs to, and push a usage event into your own billing system.
Three ways to read cost, cheapest-to-wire to most complete:
usage.cost (USD) off the response — stamped on every Chat/Responses reply (final usage chunk when streaming), no request flag needed. BYOK rows carry usage.is_byok: true and cost: 0. Good for showing a cost immediately; not a durable feed on its own.x-request-id header (the gateway request id); pass it to GET /api/v1/generation?id= for cost and token detail. It accepts the bare id or the gen-<request_id> form. (See OpenRouter compatibility.)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.GET /api/v1/usage returns settled rows, newest first, with a keyset cursor:
curl "https://api-pr-1584.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-1584.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.
GET /api/v1/usage on a schedule, resuming from your stored cursor.attribution_label.The pattern is the same regardless of billing vendor: Metronome, Orb, and Stripe metered billing each take a customer id and a metered quantity, which is exactly what a mapped usage row provides. Because the feed is settled cost, you can rebill at your own margin — bill your customer whatever multiple of real_cost_usd your pricing calls for.
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.For programmatic key management — minting a key per end-customer or rotating keys — use an opt-in provisioning key rather than your everyday inference key. It is still an ordinary xpl_key; “provisioning” is a capability flag set at creation ("provisioning": true).
/api/v1/keys* family is gated behind a provisioning key — including the GET reads. A signed-in org admin satisfies the same gate, so mint the first one from the dashboard; from then on that key can mint further keys over the API.POST /api/v1/keys to create (body name, optional daily limit USD cap, and provisioning: true to mint another provisioning key — the plaintext key is returned exactly once), GET /api/v1/keys to list, GET/PATCH/DELETE /api/v1/keys/{hash} to read, update, or revoke. {hash} is the key’s hash field (its uuid id), never the secret.403 on every /api/v1/keys* route.Key-per-customer is an alternative to safety_identifierattribution: mint a distinct inference key per end-customer, and each customer’s usage is naturally isolated to their key with independent revocation and per-key limits. safety_identifier is simpler (one key, per-request label); you can also combine them.