Integrations
Point an existing OpenRouter integration at Experiential and keep working. One base URL serves inference and model discovery 1:1, and the account, usage, and key-management routes answer in OpenRouter's shapes.
One base URL covers both serving and account:
curl https://api-pr-1584.preview.experientiallabs.ai/api/v1/chat/completions \-H "Authorization: Bearer xpl_..." \-H "Content-Type: application/json" \-d '{"model": "claude-fable-5.1","messages": [{"role": "user", "content": "hello"}]}'
Point your OpenRouter (or OpenAI) client’s base_url at it and change nothing else. The same xpl_ key and base URL work with the OpenAI SDK, the OpenRouter SDK, or a raw Authorization: Bearer header. Serving is also available at the bare https://api-pr-1584.preview.experientiallabs.ai/v1 base for OpenAI / Anthropic clients that expect it.
Experiential matches OpenRouter’s split between an inference key and a provisioning key. Both are ordinary xpl_keys — “provisioning” is a capability flag on the key (is_provisioning), not a different prefix or a separate account. You set it when you mint the key ("provisioning": true on POST /api/v1/keys); a key without it is a normal inference key.
/credits, /key, /generation, /activity, /usage, GET /models, GET /providers).GET/POST/PATCH/DELETE /keys). Every /api/v1/keys* route — including the GET reads — is gated behind it; a normal inference key gets a 403 on all of them.POST /api/v1/keys. Keep the provisioning key server-side; do not use it as your day-to-day inference key.OpenRouter paths map to the same path under https://api-pr-1584.preview.experientiallabs.ai/api/v1.
These are transparent relays into the single gateway worker: the request and response bodies are the OpenAI / Anthropic wire formats you already send. All four are rewritten to their /v1 twins before auth, so the /api/v1 base behaves exactly like the bare /v1 base for them.
| Route | Notes |
|---|---|
| POST /api/v1/chat/completions | OpenAI Chat Completions, streaming or not. |
| POST /api/v1/responses | OpenAI Responses API. HTTP or the WebSocket upgrade on the same path. |
| POST /api/v1/messages | Anthropic Messages API (what Claude Code speaks). |
| GET /api/v1/models | Model discovery, relayed 1:1 to the worker's key-scoped /v1/models — the slugs your key can call (public catalog plus your org's own models). Every listed model is callable at https://api-pr-1584.preview.experientiallabs.ai/api/v1. |
| Route | Notes |
|---|---|
| GET /api/v1/models/{author}/{slug}/endpoints | Per-model provider endpoints (the “waterfall”). The {author} segment is accepted for URL compatibility but not used to resolve the model — resolution is by {slug}. |
| GET /api/v1/providers | Provider list. Some OpenRouter provider metadata we do not track is omitted. |
| Route | Notes |
|---|---|
| GET /api/v1/credits | {data:{total_credits, total_usage}} in USD; balance is total_credits − total_usage. |
| GET /api/v1/key | Usage/limit metadata for the presented key. usage, limit, and limit_remaining are real (the cap is a daily one, so limit_remaining is measured against today's spend). Requires an xpl_ key credential. |
| GET /api/v1/generation?id= | Cost/token detail for one request by id (see below). |
| GET /api/v1/activity | Recent activity rollup. |
| GET /api/v1/usage | Experiential extension: cursor-paginated settled per-request export for billing. |
The {hash} path segment is the key’s hash field (the row’s uuid id, as returned by list/create), never the secret.
| Route | Notes |
|---|---|
| GET /api/v1/keys | List keys ({data:[...]}); revoked hidden unless ?include_disabled=true. |
| POST /api/v1/keys | Create a key; returns {data, key:"xpl_..."} — the plaintext once. Body: name (required), limit (daily USD cap), provisioning (bool). |
| GET /api/v1/keys/{hash} | Read one key. |
| PATCH /api/v1/keys/{hash} | Update name / disabled / limit. disabled:true revokes (terminal). |
| DELETE /api/v1/keys/{hash} | Revoke one key; returns {data:{success:true}}. |
On the key list/CRUD object, usage and limit_remaining are intentionally null today (there is no cheap per-key lifetime-spend reader for a list; a fabricated 0 would mislead). Real per-key spend lives on GET /api/v1/key (the presented key) and GET /api/v1/usage (filter by api_key_id). This is a documented follow-up, not a permanent gap.
Experiential stamps the settled cost inline on the usage object of every Chat Completions and Responses reply, in USD — a strict superset of OpenRouter’s usage extension. No request flag is needed: usage.cost is stamped whenever the engine emits a usageobject, matching OpenRouter’s “usage is always included” semantic (both usage: { include: true } and stream_options: { include_usage: true } are deprecated no-ops).
usage (and cost); on a stream the cost rides the final usage chunk when the engine emits one, 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.)For a per-request cost feed suitable for a billing sync job, use the usage export rather than scraping inline usage.cost from each response.
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-1584.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.
Deliberate, honest divergences from OpenRouter:
https://api-pr-1584.preview.experientiallabs.ai/api/v1; you do not configure a second prefix. (Serving is also available at the bare /v1 base.)null or omitted rather than filled with a placeholder. If a value is present, it is real.GET /api/v1/key (the presented key) but not yet on the key list/CRUD objects — a documented follow-up.GET /api/v1/usage on a schedule and persist the cursor; there is no push webhook yet./api/v1/keys* family requires a provisioning key.Embedding Experiential in your own product and rebilling your end-users? Read Integrating Experiential next — it covers per-end-customer attribution and the billing export.