Docs
Status
OverviewQuickstartSetup promptsThe core loopAuthenticationOverviewModelsThe waterfallPlansAdding modelsData controlsOpenAI compatibilityEmbeddingsAnthropic APIErrorsIntegrate the gatewayCost APIAccount APICoding agentsCredits & billingSpend & intelligenceSpend APITelemetryBecome a providerProvider guideAPI reference

Get started

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

Guides

  • Overview
  • Models
  • The waterfall
  • Plans
  • Adding models
  • Data controls
  • OpenAI compatibility
  • Embeddings
  • Anthropic API
  • Errors

Integrations

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

Billing & usage

  • Credits & billing
  • Spend & intelligence
  • Spend API
  • Telemetry

Providers

  • Become a provider
  • Provider guide

Reference

  • API reference

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. Follow its approval checkpoints before changes or spend. The agent asks before real choices, 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-74.preview.experientiallabs.ai, API https://api-pr-74.preview.experientiallabs.ai).

Coming from 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 for any provider or gateway; the dedicated one-paste transfer prompt knows that gateway’s exact surface and needs nothing else read.
Switch from Cloudflare AI Gateway starts with your rules, customer plans, model-group allowances and custom providers, not just a base-URL swap. It checks the live deployment, asks before writes, spend, privacy changes or cutover, and stops when a required behavior is unsupported or unverified.

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-74.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-74.preview.experientiallabs.ai/terms and https://pr-74.preview.experientiallabs.ai/privacy and wait for my
agreement, then
POST https://pr-74.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-74.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-74.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-74.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-74.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-74.preview.experientiallabs.ai/api/v1/generation?id=<that id>.
- Balance: GET https://api-pr-74.preview.experientiallabs.ai/api/v1/credits. Recent activity:
GET https://api-pr-74.preview.experientiallabs.ai/api/v1/activity.
- Billing feed: GET https://api-pr-74.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-74.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-74.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-74.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-74.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-74.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-74.preview.experientiallabs.ai/terms and https://pr-74.preview.experientiallabs.ai/privacy and wait for my
agreement, then
POST https://pr-74.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-74.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-74.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-74.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-74.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-74.preview.experientiallabs.ai/api/v1/generation?id=<that id>.
- Balance: GET https://api-pr-74.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-74.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-74.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-74.preview.experientiallabs.ai/api/v1/keys {name, limit, provisioning}
returns the plaintext key exactly once; GET/PATCH/DELETE
https://api-pr-74.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-74.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-74.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-74.preview.experientiallabs.ai/docs/integrate, https://pr-74.preview.experientiallabs.ai/docs/cost-api,
and https://pr-74.preview.experientiallabs.ai/docs/account-api.

Model-bound custom endpoints

POST /api/orgs/{org_id}/custom-endpoints/check tests an endpoint without saving. POST /api/orgs/{org_id}/custom-endpoints rechecks and creates it. Both take model_slug, base_url, credential, an explicit unique setup_alias, and optional provider_model_id and rate_card. Checks may incur provider charges. An existing account name returns 409; creation never rotates it.

A rate card has schema_version: 1, currency: "USD", an aware effective_at timestamp, and prices. Rates use integer nano-USD per million tokens, at most 1,000,000,000,000. The six fields are input_nano_usd_per_million_tokens, output_nano_usd_per_million_tokens, cached_input_nano_usd_per_million_tokens, cache_creation_input_nano_usd_per_million_tokens, cache_creation_1h_input_nano_usd_per_million_tokens and reasoning_nano_usd_per_million_tokens. Optional long_context repeats the whole schedule with input_threshold_tokens;flex and priority each repeat the six rate fields. Selected tiers replace the whole schedule. Missing rates stay unknown, not zero.

No card means unknown custom pricing, never rates inferred from the public provider. BYOK still has no wallet charge, but monetary customer allowances refuse unknown required pricing. Attempts freeze the selected schedule, effective date and content-addressed pricing_source. These are attributed estimates, not provider invoices. For a new key or rate card, create a new account name through Serve it yourself, then select Use on the model. Generic provider rotation and checks refuse custom accounts before sending credentials to the official provider. Keep the old account for rollback. This does not establish arbitrary HTTP, Tinfoil attestation or Modal billing parity.

TypeSafe native decisions

Jev uses POST https://api-pr-74.preview.experientiallabs.ai/v1/systemone, not the chat routes below. Use it as a typed decision helper in your application or coding agent, while keeping the agent's conversational model unchanged. The examples and agent prompt use your Experiential key and this deployment's origin.

How to use this model for coding agents

Jev is a decision helper for your coding agent, not a chat model.

Agent prompt
Add jev-latest (TypeSafe Jev) as a decision helper/tool for my coding agent.
Keep my coding agent on its current chat model. Jev is not a primary coding/chat model and does not generate chat completions.
Optional official skill setup (ask before installing; choose one route):
For Claude Code:
claude plugin marketplace add typesafe-ai/skills
claude plugin install typesafe@typesafe-ai
Then invoke /typesafe:typesafe-ai.
For other supported agents:
npx skills add typesafe-ai/skills --skill typesafe-ai
Then ask the agent to use the TypeSafe skill.
Skill reference: https://docs.typesafe.ai/agent-skill
The vendor skill/examples assume a direct TypeSafe key and origin. Override both for this integration:
- POST only to https://api-pr-74.preview.experientiallabs.ai/v1/systemone.
- Read the Bearer key only from EXPERIENTIAL_API_KEY. If missing, stop and ask me to set it locally; never ask me to paste or log it.
- Do not request a TypeSafe API key or call the provider's endpoint. If the skill cannot use the gateway, write a small native HTTP helper instead.
- Send model "jev-latest" exactly, preserving any selected :free spelling.
Implement a native JSON request with model, state (text, object, or array), and questions (a map keyed by my question IDs). Each question needs type and instructions:
- choice: criteria maps options such as accept/review to their descriptions.
- noul: judge a proposition; optional criteria describe true and false.
- score: criteria is an ordered list of level descriptions.
Use a concise diff and test summary as state, not the entire repository. Ask independent questions; an answer is not fed into another question in the same request.
Approximately 32,000 input tokens shared by state and all question definitions; not a total context window or an output limit.
Gateway limits: at most 32 questions per request, 64 options per choice, and 2-10 levels per score. These are platform admission bounds, not provider limits.
Native request reference: https://docs.typesafe.ai/introduction/quickstart
Input-budget source: https://docs.typesafe.ai/primitives
Read answers by question ID (for example answers.review), and show the decision and reported usage to me. Probabilities and confidence are not guarantees of correctness. Never execute a returned choice, merge a change, or bypass review automatically.
Use a bounded timeout, surface HTTP and transport errors, and do not add automatic retries or Idempotency-Key: an unknown outcome may already be charged. This endpoint does not stream and has no Chat Completions, Responses, or Messages facade.
Prepare the helper and a mocked test first. Ask before making a potentially charged live call; do not claim it worked without a real response. Tell me which files you changed.

Official agent skillNative API quickstartAbout Jev

The /api/v1 surface

The compatibility routes below share one host at https://api-pr-74.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, text embeddings, 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-74.preview.experientiallabs.ai/api/v1
curl https://api-pr-74.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-74.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)

The POST routes are transparent relays into the single gateway worker: request and response bodies use the supported OpenAI / Anthropic wire formats. They are rewritten to their /v1 twins before auth, so the /api/v1 base behaves like the canonical /v1 base for serving. Model discovery returns the same callable set in the vendor-prefixed shape described below.

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/embeddingsOpenAI embeddings from text or token IDs, singly or in batches. Omit stream or send false. No streaming, vector-response cache, or Idempotency-Key replay.
POST /api/v1/messagesAnthropic Messages API (what Claude Code speaks).
GET /api/v1/modelsModel discovery in the vendor-prefixed id shape: the same callable set as the key-scoped /v1/models, each id spelled <vendor>/<slug> (tencent/hy4-preview) or <vendor>/<base>:free for a free-tier lane (and <vendor>/<slug>:free for a model whose free lane is a promotion your organization may use right now: served only while the promotion funds it, refused with free_limit_reached and never charged otherwise), with canonical_slug naming the /v1/models slug. Listed models use their supported API surface; chat models use https://api-pr-74.preview.experientiallabs.ai/api/v1 or https://api-pr-74.preview.experientiallabs.ai/v1, while TypeSafe decisions use POST https://api-pr-74.preview.experientiallabs.ai/v1/systemone.

For vectors, use the Embeddings guide. It covers model availability, text and token-ID inputs, tokenizer requirements, SDK base64 decoding, and why retries can be billed again.

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. The compatibility routes share https://api-pr-74.preview.experientiallabs.ai/api/v1 and the bare /v1 base. Native TypeSafe decisions use https://api-pr-74.preview.experientiallabs.ai/v1/systemone on that same API host.
  • 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.

PreviousErrorsNextCost API