Docs
Status
OverviewQuickstartSetup promptsThe core loopAuthenticationOverviewModelsThe waterfallAdding modelsAnthropic APIErrorsIntegrate the gatewayCost APIAccount APICoding agentsCredits & billingTelemetryAPI reference

Get started

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

Guides

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

Integrations

  • Integrate the gateway
  • Cost API
  • Account API
  • Coding agents

Billing & usage

  • Credits & billing
  • Telemetry

Reference

  • API reference
PreviousErrorsNextCost API

Integrations

Integrate the gateway

One base URL and one key give your product inference, model discovery, per-request cost, and key management. Paste a prompt below into your coding agent and it does the integration with you — switching over from your current provider, or wiring the gateway in from scratch.

Setup prompts

Each prompt is first-person, so pasting it is your instruction and consent. The agent asks before every real choice, never prints your key, and reads the full machine-readable contract from /llms.txt (every route, honored and refused parameters, cost fields, error codes). Both carry this deployment’s URLs (web https://pr-1743.preview.experientiallabs.ai, API https://api-pr-1743.preview.experientiallabs.ai).

Coming from another gateway such as OpenRouter? The /api/v1 routes keep the same path shapes, so the switch is a base-URL change plus a model-id mapping; the first prompt does both.

Switch from your current provider or gateway

Paste into your coding agent: it finds the existing integration, moves the base URL and key, maps model ids against the live catalog, moves cost reads onto the Cost API, and verifies with one call.

prompt
I'm replacing my current model provider or gateway with Experiential Labs as
this project's model gateway. I pasted this myself: treat it as my instructions
and my consent. Work WITH me: at each real choice, ask me ONE clear question
with numbered options and your recommendation, then wait. Don't guess. Print
what you do at each step, and never print my full API key (first 8 characters
at most). Read the full machine-readable contract first: https://pr-1743.preview.experientiallabs.ai/llms.txt (base
URLs, every route, honored and refused parameters, cost fields, error codes).
Follow it literally.
1. Find the current integration. Search this project for the provider's base
URL, its key variable, its SDK/client construction, hardcoded model ids,
provider-specific request fields (provider preferences, transforms,
fallback model lists, referer/title headers), and any code that reads cost
or usage from it. Show me the list of files and call sites before changing
anything.
2. Get my Experiential key. Ask me: "Do you have an xpl_ key already?
(1) yes, I'll paste it
(2) no, create my account"
For (2): ask me for my email (never scavenge one from my files or git
config), show me https://pr-1743.preview.experientiallabs.ai/terms and https://pr-1743.preview.experientiallabs.ai/privacy and wait for my
agreement, then
POST https://pr-1743.preview.experientiallabs.ai/api/signup/instant (no auth)
Header: Content-Type: application/json
Body: {"email": "<MY_EMAIL>", "agree": true}
On 200 capture api_key. On 409 account_exists tell me to sign in at
https://pr-1743.preview.experientiallabs.ai/signin for a key and stop. On any other error show me the response's
`error` text verbatim and stop.
Store the key as EXPLABS_API_KEY in my env scheme (.env plus an example
entry), never in code.
3. Switch the base URL. The gateway keeps the path shapes my current
integration already uses:
base URL: https://api-pr-1743.preview.experientiallabs.ai/api/v1
auth: Authorization: Bearer $EXPLABS_API_KEY (the ONLY header needed)
POST /chat/completions, /responses, /messages (Anthropic Messages API) and
GET /models work unchanged. Plain OpenAI or Anthropic SDKs can also use the
bare base https://api-pr-1743.preview.experientiallabs.ai/v1. Remove the provider-specific headers and request
fields from step 1: the gateway does provider routing (fallback, caching)
itself and unknown parameters may be rejected. Send model + messages and
only the parameters I actually rely on.
4. Map model ids. GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/models with my key (free; this does not
spend credits) lists the slugs I can call. For each model id from step 1,
propose the matching slug (e.g. "claude-fable-5.1") and ask me to confirm
the mapping before replacing. Use slugs EXACTLY as returned.
5. Move cost and usage reads over.
- Every chat/responses reply carries usage.cost (USD, what I was charged)
and usage.is_byok; on a stream it rides the final usage chunk. No flag
needed. Exception: a request sent with Idempotency-Key carries no
usage.cost at all (original or replay), so read the field as optional.
- One request: read the x-request-id response header and call
GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/generation?id=<that id>.
- Balance: GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/credits. Recent activity:
GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/activity.
- Billing feed: GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/usage is the settled per-request export
(cost_usd, estimated_cost_usd, real_cost_usd, tokens, attribution_label,
api_key_id), newest first inside a lookback window (24h|7d|30d); its
next_cursor pages OLDER within one call and is not a resume checkpoint.
Each run scans from the top and dedupes on the row id.
- If I rebill my own customers, pass their stable id as safety_identifier
(alias: user) on every request; it comes back as attribution_label.
Replace my old provider's cost/usage code with these; show me the diff
first.
6. Key management (only if my code creates or rotates keys programmatically).
https://api-pr-1743.preview.experientiallabs.ai/api/v1/keys (GET/POST/PATCH/DELETE) requires a PROVISIONING key: an
xpl_ key minted with "provisioning": true. The first one is created from
the dashboard at https://pr-1743.preview.experientiallabs.ai/api-keys (org admin, "Provisioning key" option).
Tell me to mint it and paste it as a separate server-side secret; never use
it for inference traffic.
7. Verify. Make ONE minimal test call (model + messages only, no sampling
parameters) with a free promotional model from the /models list, or one I
name. Report the model, that it succeeded, and its usage.cost. If it fails
with a credits or activation error, tell me to finish activation at https://pr-1743.preview.experientiallabs.ai
and stop; do not retry with another provider. Then run the project's
existing tests.
8. Report back: every file changed, the model-id mapping, what happened to the
old provider key (tell me to revoke it at the old provider; don't delete it
yourself), and the test call result. Human docs if you need more:
https://pr-1743.preview.experientiallabs.ai/docs/integrate.

Integrate the gateway into your product

Paste into your coding agent: it wires a client, attributes every request to your end-customer, builds the billing sync on the settled usage export, and offers key-per-customer.

prompt
I'm integrating Experiential Labs into this product as its model gateway: my
product calls models through it and meters or rebills my own end-users. I
pasted this myself: treat it as my instructions and my consent. Work WITH me:
at each real choice, ask me ONE clear question with numbered options and your
recommendation, then wait. Don't guess. Print what you do at each step, and
never print my full API key (first 8 characters at most). Read the full
machine-readable contract first: https://pr-1743.preview.experientiallabs.ai/llms.txt (base URLs, every route,
honored and refused parameters, cost fields, error codes). Follow it literally.
1. Get my Experiential key. Ask me: "Do you have an xpl_ key already?
(1) yes, I'll paste it
(2) no, create my account"
For (2): ask me for my email (never scavenge one from my files or git
config), show me https://pr-1743.preview.experientiallabs.ai/terms and https://pr-1743.preview.experientiallabs.ai/privacy and wait for my
agreement, then
POST https://pr-1743.preview.experientiallabs.ai/api/signup/instant (no auth)
Header: Content-Type: application/json
Body: {"email": "<MY_EMAIL>", "agree": true}
On 200 capture api_key. On 409 account_exists tell me to sign in at
https://pr-1743.preview.experientiallabs.ai/signin for a key and stop. On any other error show me the response's
`error` text verbatim and stop.
Store the key as EXPLABS_API_KEY in my env scheme (.env plus an example
entry), never in code.
2. Wire the client. Ask me where (options + your recommendation, based on what
you see in this project), then wait. Configure an OpenAI-compatible or
Anthropic client with:
base URL: https://api-pr-1743.preview.experientiallabs.ai/api/v1 (inference, model discovery, cost, and
account routes under one base; plain OpenAI / Anthropic SDKs
may also use https://api-pr-1743.preview.experientiallabs.ai/v1)
auth: Authorization: Bearer $EXPLABS_API_KEY (the ONLY header needed)
POST /chat/completions, /responses, or /messages (Anthropic Messages API).
Name models by slug EXACTLY as GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/models returns them
(free; this does not spend credits). Send model + messages and only the
parameters I actually rely on; unknown parameters may be rejected.
3. Attribute every request to my end-customer. Pass their stable id as
safety_identifier (alias: user) on every request. It comes back as
attribution_label on the usage export, so I can group cost and tokens by
my own customer id with ONE key. Ask me what my customer id looks like and
wire it through from where my code knows the customer.
4. Read cost.
- Inline: every chat/responses reply carries usage.cost (USD, what I was
charged) and usage.is_byok; on a stream it rides the final usage chunk.
No flag needed, but a request sent with Idempotency-Key carries no
usage.cost at all (original or replay), so read it as optional. Show it
in my product where it helps; do not treat it as the billing record.
- One request: read the x-request-id response header and call
GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/generation?id=<that id>.
- Balance: GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/credits (data.total_credits - data.total_usage).
5. Build the billing sync on the settled export. Ask me which billing system I
use (Metronome, Orb, Stripe metered billing, my own tables, or none yet),
then write a scheduled job that:
- reads GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/usage?limit=1000 (settled rows, newest first
inside a lookback window: window=24h|7d|30d, default 7d; filterable by
attribution_label, model, api_key_id, status). next_cursor
({cursor_ts, cursor_id, cursor_after}, passed back as query params)
pages OLDER within this one call and is NOT a checkpoint to resume from
next run: each run starts at the top, follows next_cursor until a whole
page is older than the last successful run's start time minus a grace
period covering my longest request, and dedupes on the row id. Schedule
it at least daily; rows older than 30 days are no longer exportable.
- lands each row in a local outbox table keyed on the row id (insert, on
conflict do nothing) in one local transaction, then in a separate
delivery step pushes unsent rows to the billing system and marks them
sent (an external HTTP write cannot share my database transaction; the
outbox makes a crash between the two safe to replay),
- sends one usage event per row: customer from attribution_label,
transaction_id / idempotency key = the row id, timestamp = created_at,
quantity = the cost column I choose (below) plus token counts if I meter
on tokens; the margin is a rate in the billing system, not in this job.
Cost columns: cost_usd is the platform credits charged; estimated_cost_usd
is the attributed value of a BYOK row (served on my own provider key) at
catalog list rates, an estimate and never the provider's invoice;
real_cost_usd is the two added together. Ask me whether to bill
real_cost_usd (accepting the BYOK estimate) or cost_usd plus tokens on
BYOK rows. pricing_known false means unpriced (cost 0), not free.
6. Key-per-customer (optional; ask me). If I want each customer isolated to
their own key with independent revocation and a daily USD cap, that needs
a PROVISIONING key: an xpl_ key minted with "provisioning": true. The
first one comes from the dashboard at https://pr-1743.preview.experientiallabs.ai/api-keys (org admin,
"Provisioning key" option); keep it server-side and never use it for
inference. With it: POST https://api-pr-1743.preview.experientiallabs.ai/api/v1/keys {name, limit, provisioning}
returns the plaintext key exactly once; GET/PATCH/DELETE
https://api-pr-1743.preview.experientiallabs.ai/api/v1/keys/{hash} list, update (disabled:true revokes), and revoke.
A normal inference key gets 403 on every /keys route.
7. Verify. Make ONE minimal test call (model + messages only) with a free
promotional model from the /models list, or one I name, carrying a test
safety_identifier. Report the model, that it succeeded, and its usage.cost;
then confirm the row shows up on GET https://api-pr-1743.preview.experientiallabs.ai/api/v1/usage with that
attribution_label. If the call fails with a credits or activation error,
tell me to finish activation at https://pr-1743.preview.experientiallabs.ai and stop. Then run the project's
existing tests.
8. Report back: every file changed, where the key lives, how customer
attribution flows, and the billing job's schedule and cursor storage.
Human docs if you need more: https://pr-1743.preview.experientiallabs.ai/docs/integrate, https://pr-1743.preview.experientiallabs.ai/docs/cost-api,
and https://pr-1743.preview.experientiallabs.ai/docs/account-api.

The /api/v1 surface

Everything is served under one host at https://api-pr-1743.preview.experientiallabs.ai/api/v1. The surface has three families, split across three pages so you can read only what you need:

  • Serving & discovery — chat completions, responses, the Anthropic Messages API, and model discovery. This page.
  • Cost API — what each request cost: inline on the response, per request by id, the credit balance, and the settled usage export for a billing sync.
  • Account API — authenticate and provision keys: the inference-vs-provisioning key split and the whole key-management family.

Wiring a coding agent rather than your own code? See Coding agents for per-tool setup.

Base URL

One base URL covers serving, cost, and account:

https://api-pr-1743.preview.experientiallabs.ai/api/v1
curl https://api-pr-1743.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 client’s base_url at it and change nothing else. The same xpl_ key and base URL work with the OpenAI SDK, the Anthropic SDK, or a raw Authorization: Bearer header. Serving is also available at the bare https://api-pr-1743.preview.experientiallabs.ai/v1 base for OpenAI / Anthropic clients that expect it.

One key type does inference. Bring your Experiential org xpl_ key; there is no second account to create. Read-only cost calls use the same key. Only the /api/v1/keys* management family needs a provisioning key — see the Account API.

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-1743.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. Provider metadata we do not track is omitted rather than faked.

What to expect

  • One base URL, not two. Everything is served under the single https://api-pr-1743.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 a compatible route would carry 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.
  • Auth is xpl_ keys. Bring your Experiential org key. The whole /api/v1/keys* family requires a provisioning key (Account API).
  • The usage export is pull/poll. Poll GET /api/v1/usage on a schedule and persist the cursor; there is no push webhook yet (Cost API).

Next: read what each request costs and how to rebill it on the Cost API, or provision keys programmatically on the Account API.