Docs
Status
OverviewQuickstartSetup promptsThe core loopAuthenticationOverviewCoding agentsModelsThe waterfallAdding modelsAnthropic APIErrorsOpenRouter compatibilityIntegrating ExperientialCredits & billingTelemetryAPI reference

Get started

  • Overview
  • Quickstart
  • Setup prompts
  • The core loop
  • Authentication

Guides

  • Overview
  • Coding agents
  • Models
  • The waterfall
  • Adding models
  • Anthropic API
  • Errors

Integrations

  • OpenRouter compatibility
  • Integrating Experiential

Billing & usage

  • Credits & billing
  • Telemetry

Reference

  • API reference
PreviousErrorsNextIntegrating Experiential

Integrations

OpenRouter compatibility

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.

Base URL

One base URL covers both serving and account:

https://api-pr-1584.preview.experientiallabs.ai/api/v1
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.

Auth model

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.

  • An inference key runs inference and makes read-only account/metering calls (/credits, /key, /generation, /activity, /usage, GET /models, GET /providers).
  • A provisioning key does all of that plus the whole key-management family (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.
The first provisioning key comes from the dashboard. Key creation is a provisioning-key action, so there is a chicken-and-egg: mint the first one from the dashboard (a signed-in org admin satisfies the same gate a provisioning key does), then that key can mint further keys — provisioning or inference — through POST /api/v1/keys. Keep the provisioning key server-side; do not use it as your day-to-day inference key.

Route coverage

OpenRouter paths map to the same path under https://api-pr-1584.preview.experientiallabs.ai/api/v1.

Serving & discovery (1:1 relay)

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.

RouteNotes
POST /api/v1/chat/completionsOpenAI Chat Completions, streaming or not.
POST /api/v1/responsesOpenAI Responses API. HTTP or the WebSocket upgrade on the same path.
POST /api/v1/messagesAnthropic Messages API (what Claude Code speaks).
GET /api/v1/modelsModel 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.

Catalog (control plane)

RouteNotes
GET /api/v1/models/{author}/{slug}/endpointsPer-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/providersProvider list. Some OpenRouter provider metadata we do not track is omitted.

Account & metering (read-only, inference key)

RouteNotes
GET /api/v1/credits{data:{total_credits, total_usage}} in USD; balance is total_credits − total_usage.
GET /api/v1/keyUsage/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/activityRecent activity rollup.
GET /api/v1/usageExperiential extension: cursor-paginated settled per-request export for billing.

Key management (provisioning key required)

The {hash} path segment is the key’s hash field (the row’s uuid id, as returned by list/create), never the secret.

RouteNotes
GET /api/v1/keysList keys ({data:[...]}); revoked hidden unless ?include_disabled=true.
POST /api/v1/keysCreate 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.

Usage & cost accounting

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).

  • Non-streaming Chat/Responses bodies always carry 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.
  • BYOK settles cost 0. A bring-your-own-key request reports 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).
  • Keyed / idempotent replays are byte-exact. A replay of an 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.

Look up one request by id

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:

GET /api/v1/generation?id=
# 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.

Known differences

Deliberate, honest divergences from OpenRouter:

  • One base URL, not two. Everything is served under the single 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.)
  • Omitted fields are null or absent, never faked. Where OpenRouter returns metadata Experiential does not track — provider uptime, some provider descriptive fields — the field is null or omitted rather than filled with a placeholder. If a value is present, it is real.
  • Per-key usage / limit_remaining are null on the key list today. They are populated on GET /api/v1/key (the presented key) but not yet on the key list/CRUD objects — a documented follow-up.
  • The usage export is pull/poll. Poll GET /api/v1/usage on a schedule and persist the cursor; there is no push webhook yet.
  • Auth is xpl_ keys. Bring your Experiential org key; there is no separate OpenRouter account to create. The whole /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.