Docs
Status
OverviewQuickstartSetup promptsThe core loopAuthenticationOverviewModelsThe waterfallAdding modelsData controlsAnthropic 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
  • Data controls
  • Anthropic API
  • Errors

Integrations

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

Billing & usage

  • Credits & billing
  • Telemetry

Reference

  • API reference
PreviousAdding modelsNextAnthropic API

Guides

Data controls

What the gateway keeps about your requests, the switch that turns content storage off, the policy that restricts routing to zero-retention providers, and the response headers that name the provider behind every answer.

What we store and when

The serving path is content-free. The request ledger that meters every call records metadata and refuses prompt or response bodies by database constraint. Content exists only in the stores below, each with its own window. Captured prompts, captured responses and Responses API continuations are governed by one organization-wide switch; batch files and the replay window are not.

StoreWhat it holdsHow longGoverned by the content switch
Request ledgerMetadata only: request id, model slug, the provider and deployment of each attempt, route depth, token counts, cost, timestamps, status and failure class. The tables refuse content by database constraint.Indefinitely (it is the billing record).No. Always recorded.
Captured prompts and responsesThe request's messages (up to 1 MiB) and the entire response as it was relayed to you (up to 4 MiB, every streamed frame included).30 days from capture; an hourly job deletes older rows.Yes. Written only while the switch is on, and never for a request served on your own provider key.
Responses API continuationsThe prior conversation behind a response id, so previous_response_id can be honored on any worker (up to 4 MiB).30 days from the last turn that referenced it.Yes. Nothing is stored while the switch is off.
Batch filesThe input, output and error JSONL files of /v1/batches jobs.48 hours after upload if no job used the file; otherwise until 24 hours after the referencing job's expiry.No. Batch files follow their own window.
Idempotency-Key replay windowThe original response bytes for a repeated Idempotency-Key.24 hours, in the memory of the worker that served the request. Never written to disk.No.

Requests served on your own provider key (bring your own key) never leave content on the platform on any plan; see Customer-managed keys. The privacy policy lists every store, who can read it, and where it is held.

Turning prompt and response storage off

One flag per organization, capture_prompt_content, controls captured prompts, captured responses and Responses API continuations together. It lives in the app under Settings → Organization → Privacy, where an organization admin toggles “Go 100% private”. Members see the state read-only.

Defaults by plan

  • Free:the switch is on for every new organization. Turning it off requires the Pro plan or an organization that was verified before capture became the default; a free organization's request to turn it off is refused with 403 and reason: needs_subscription.
  • Pro: the database turns the switch off the moment the organization becomes Pro and deletes what was captured before. An admin may turn it back on deliberately; nothing re-flips that choice while the plan lasts. A lapse followed by a new subscription turns it off again.

What changes when it is off

  • Turning it off is retroactive: the organization's captured prompts, captured responses and stored continuations are deleted in the same transaction as the flip. Nothing kept under the old setting stays readable.
  • No new prompt, response or continuation is written. The ledger keeps recording content-free metadata (tokens, cost, latency, provider, outcome) for billing.
  • previous_response_id on /v1/responses cannot be resolved, because the prior turns are not stored. The request gets an explicit error asking you to resend the full conversation. Send the whole input on every turn instead.
  • Features built on captured content (the per-request prompt view, prompt grouping) show nothing for new requests.

Every actual flip is written to the organization audit log as telemetry.capture_enabled or telemetry.capture_disabledwith the admin as the actor; the automatic Pro flip uses the same action string with a system actor. The switch is also readable and writable through the app's own route, GET / PUT /api/orgs/<org_id>/telemetry-settings with body {"capture_prompt_content": false}. That route takes a signed-in session; it is not admitted for xpl_ API keys.

Zero data retention across providers

The content switch governs what the platform keeps. A separate provider policy governs which upstream providers your requests may reach. It is one row per organization with three fields: allowed_providers (a list of provider names, or null for all), require_zdr and require_no_training. With no row, every route in the catalog is eligible.

The policy is applied when a request is routed, at rung granularity and to every lane, including rungs on your own provider keys. Each rung's posture is resolved from a curated matrix keyed by provider and a pattern over the provider's model id, most specific pattern first. A provider or model the matrix does not cover counts as non-compliant. Rungs that fail the policy are removed from the route before any dispatch, so a non-compliant provider is never attempted, not even as a fallback. When no rung survives, the request is refused with HTTP 403, code model_not_granted, type permission_error, and the message “No route for this model satisfies your organization's provider policy” naming the requirement that removed every rung.

Where to set it

Settings → Provider policy (/settings/data-controls) is an Enterprise surface: it exists only for organizations with the data_controls capability and returns 404 otherwise. Admins write the policy; members read it. The same surface is GET / PUT / DELETE /api/orgs/<org_id>/provider-policy in the app (signed-in session, not xpl_ keys); PUT replaces the whole document and every change is audit-logged. Licensing gates management only: a policy that exists keeps being enforced by the gateway whether or not the capability is still licensed. A change reaches the gateway on its next catalog refresh without a restart.

What require_zdr removes today

The matrix below is the platform's reading of each provider's published data-handling terms for its default API, not an agreement we hold with the provider. Each row carries the source it is based on and an effective date where the provider publishes one; the full matrix with source notes is GET /api/orgs/<org_id>/provider-data-controls in the app for any member.

ProviderZero data retentionNo training
bedrockYes, except wire ids matching *anthropic.claude-fable* and *openai.gpt-5*Yes
fireworksYesYes
experiential_cloudYesYes
zaiYesYes
novitaOnly wire ids matching qwen/*, zai-org/*, moonshotai/*, deepseek/*; other Novita rungs are notYes
modal, local (your own server)YesYes
openaiNoYes
azure_openaiNoYes
anthropicNoYes
geminiNoYes
vertexNoYes
tencentNoYes
qwenNoYes
cerebrasNoYes
waferNoYes
xaiNoYes
openrouterNoNo
deepseekNoNo
The trade-off is coverage. With require_zdron, every OpenAI, Azure OpenAI, Anthropic, Gemini and Vertex rung is removed, along with OpenRouter and the other providers marked “No” above. A model whose waterfall has no surviving rung is refused with the 403 above rather than served elsewhere. Check a model before turning the policy on: GET /api/models/<slug>/providers stamps each rung with its resolved zero_data_retention, and GET /api/models carries a retention verdict per model (zdr_all_rungs, zdr_enforceable, not_zdr) over the platform-funded lanes it actually routes. /v1/models does not carry the verdict.

Verifying the provider that served a request

Every completion response carries headers naming the rung that produced it, on /v1/chat/completions, /v1/responses and /v1/messages, streaming or not. The route headers are stamped only after a provider has committed to the request, so they describe the rung that actually answered, never a rung that was tried first and failed.

HeaderValue
x-request-idThe gateway request id. Pass it to the lookups below.
x-gateway-providerThe catalog provider name of the rung that served the response (for example azure_openai, bedrock, novita). A request on your own key names your provider. A platform-hosted lane presented as Experiential Cloud reads experiential_cloud here and in the body.
x-gateway-route-depthThe zero-based position of the serving rung in the route the gateway computed for your organization. 0 is the first rung.
x-gateway-route-reasonHow the route was chosen: direct for a catalog route; reasoning_continuation when a Responses request continued sealed reasoning on the rung that issued it.
x-gateway-canonical-modelThe exact model identity the requested slug resolved to.
x-gateway-alias / x-gateway-alias-revisionThe slug you asked for and the catalog revision that served it.

The deployment id of the rung is a server-internal identifier and is not returned. Read the provider from the header and the lookups below.

Read the served-lane headers
curl -sS -D - -o /dev/null "https://api-pr-1933.preview.experientiallabs.ai/v1/chat/completions" \
-H "Authorization: Bearer $EXPLABS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-5.5", "messages": [{"role": "user", "content": "ping"}]}' \
| grep -i '^x-request-id\|^x-gateway-'
Example response headers
x-request-id: <request id>
x-gateway-alias: gpt-5.5
x-gateway-alias-revision: <catalog revision id>
x-gateway-canonical-model: <exact model id>
x-gateway-provider: azure_openai
x-gateway-route-depth: 1
x-gateway-route-reason: direct

In the body

The same provider name is stamped as a top-level provider field beside id and model on non-streaming Chat, Responses and Messages bodies, and on the final usage-bearing frame of a stream (the message_delta event on the Messages surface), together with usage.cost and usage.is_byok. A request sent with an Idempotency-Key carries the headers but not the body fields, so its replays stay byte-identical.

After the fact, with your API key

  • GET /api/v1/generation?id=<x-request-id>returns one request's provider_name, cost and token detail.
  • GET /api/gateway/usage/events lists your requests newest first; each row carries provider(the winning attempt's provider), lane (platform_funded or pass_through) and attempt_count.
  • GET /api/v1/usage, the settled export for billing syncs, carries provider on every row.
Look up one request
# provider_name is the catalog provider of the rung that answered
curl -sS "https://api-pr-1933.preview.experientiallabs.ai/api/v1/generation?id=$REQUEST_ID" \
-H "Authorization: Bearer $EXPLABS_API_KEY"
# provider, lane and attempt_count per request, newest first
curl -sS "https://api-pr-1933.preview.experientiallabs.ai/api/gateway/usage/events?org_id=$ORG_ID&limit=20" \
-H "Authorization: Bearer $EXPLABS_API_KEY"

Failover and its disclosure

A model slug resolves to an ordered list of rungs. The gateway tries them in order and fails over on capacity and transport errors until one rung commits to the request. Failover happens only before that commitment: once a rung has committed, the response comes from that rung and the headers and body name it.

  • x-gateway-route-depth tells you where the serving rung sat in your route. 0 is the first rung.
  • attempt_count on the usage event tells you how many dispatches the request took. A value above 1 means at least one earlier rung failed on that request before the serving one. A depth above 0 with a single attempt means the gateway placed the first attempt on a later rung rather than failing over.
  • Rungs removed by your provider policy are never attempted, so they never appear as failed attempts.

What the tenant surfaces do not expose today: the provider of each failed attempt, the failure reason per attempt, and the reason a rung was reordered or skipped before dispatch. Those are recorded in the ledger and are readable by platform operators only.

Customer-managed keys

A provider connection stores your provider credential in the database vault; the connection row keeps the provider name, the non-secret configuration you entered (such as a base URL or region) and the last four characters of the credential. No API returns the secret; the connection list shows the last four characters only. The secret is released to the serving process when a rung on that connection is dispatched.

  • A request served on your key settles as lane: pass_through in the usage stream and reports usage.cost: 0, usage.is_byok: trueand the provider's own attributed charge in usage.cost_details.upstream_inference_cost. The provider bills you directly.
  • No prompt or response content is kept for a request served on your key, on any plan and regardless of the content switch. A prompt buffered before the serving rung was known is deleted once the request settles on your key, and the response is never handed to capture.
  • If every rung on your key fails and the waterfall completes the request on a platform-funded rung, that request is platform-funded: it is billed to credits and captured under your content switch like any other.
  • x-gateway-provider and the body's provider name your provider. The provider policy applies to rungs on your key as well.

What we cannot yet attest

  • No tenant-wide ZDR attestation. There is no certificate or signed statement that an organization's traffic was zero-retention end to end. The provider flags are our reading of published terms, rechecked at least every thirty days, not contractual commitments from the providers.
  • No per-response policy marker. A response does not say whether a provider policy was applied. Verify by reading x-gateway-provideragainst the matrix. Because the served rung's provider model id is not returned, a per-model exception in the matrix (for example the Bedrock or Novita rows above) cannot be resolved from the response alone.
  • No per-attempt disclosure. Failed attempts and pre-dispatch reordering are visible to platform operators, not to tenants.
  • No data region choice. Platform data is held in the United States and providers process requests wherever they operate. The request-location check decides eligibility for some models; it is not a residency control.
  • No per-request ZDR flags to resellers. OpenRouter and Wafer offer per-request zero-retention options; the gateway does not send them, so those lanes are classed not-ZDR.
  • Metadata is always kept. The content-free ledger is recorded regardless of the switch and has no tenant-controlled retention window.

See also

The privacy policy and security page state the same stores and windows as commitments. Models covers the catalog's retention verdict and regional availability, and Cost API covers the per-request lookups in detail.