Guides
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.
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.
| Store | What it holds | How long | Governed by the content switch |
|---|---|---|---|
| Request ledger | Metadata 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 responses | The 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 continuations | The 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 files | The 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 window | The 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.
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.
403 and reason: needs_subscription.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.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 API with the organization's own xpl_ key: GET / PUT /api/orgs/<org_id>/telemetry-settings with body {"capture_prompt_content": false} (the org id comes from GET /api/whoami). The same plan gate applies: a Free organization's request to turn it off is refused with 403 and code needs_subscription; turning it on has no gate.
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.
Settings → Data controls (/settings/data-controls) for every organization: admins write the policy, members read it. The same surface is GET / PUT / DELETE /api/orgs/<org_id>/provider-policy with the organization's xpl_ key or an admin session; PUT replaces the whole document and every change is audit-logged as provider_policy.set. require_zdr and require_no_training need no plan. Only allowed_providers, a provider allowlist, is an Enterprise capability (data_controls): send null to keep every provider eligible, or the request is refused with 403 and code enterprise_required. A change reaches the gateway on its next catalog refresh, within seconds, without a restart.
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.
| Provider | Zero data retention | No training |
|---|---|---|
| bedrock | Yes, except wire ids matching *anthropic.claude-fable* and *openai.gpt-5* | Yes |
| fireworks | Yes | Yes |
| experiential_cloud | Yes | Yes |
| zai | Yes | Yes |
| novita | Yes for models Novita hosts itself; not for its relays to closed-weights vendors (wire ids matching pa/*, anthropic/*, openai/*) nor *claude-fable* / *claude-mythos* | Yes |
| modal, local (your own server) | Yes | Yes |
| openai | No | Yes |
| azure_openai | No | Yes |
| anthropic | No | Yes |
| gemini | No | Yes |
| vertex | No | Yes |
| tencent | No | Yes |
| qwen | No | Yes |
| cerebras | No | Yes |
| wafer | No | Yes |
| xai | No | Yes |
| openrouter | On request: with require_zdr on, every OpenRouter dispatch is bound to OpenRouter's published zero-data-retention endpoint list (provider.zdr + data_collection=deny) and the selected upstream is recorded; without the toggle its default routing is not zero-retention | No |
| deepseek | No | No |
require_zdron, every OpenAI, Azure OpenAI, Anthropic, Gemini and Vertex rung is removed, along with the other providers marked “No” above. An OpenRouter rung is kept and dispatched constrained: the request carries provider: {"zdr": true, "data_collection": "deny"}, so OpenRouter serves it from its published zero-data-retention endpoint list or refuses, natively zero-retention rungs are dialed first, and the upstream OpenRouter selected is recorded on the attempt. A single request can demand the same thing without the org toggle: send a top-level provider: {"zdr": true} (OpenRouter's own field shape) on /v1/chat/completions, /v1/responses or /v1/messages (through the Anthropic SDK's extra_body); the request is then judged exactly like require_zdr, answers x-gateway-zdr: true, and is refused with the same 403 (naming provider.zdr) when no rung qualifies. It only tightens: it can never loosen an organization policy. 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, and GET /v1/models carries data_policy per model (zdrtrue only when every rung of the model's routing pool is zero-data-retention by default, zdr_on_request true when a require_zdrorganization is served through it, plus each rung's provider, zdr, zdr_on_request and no_training). Every rung a require_zdr organization is served through, with its capability flags, is listed at GET /api/v1/endpoints/zdr, a public read at the same path OpenRouter publishes its list.Three layers decide whether a request is zero-retention. Each has its own switch or fact, and all three are readable and settable with the organization's xpl_ key.
capture_prompt_content). Off means no prompt, response or continuation is stored here; the content-free ledger is kept on every plan.require_zdr / require_no_training. A lane presented as Experiential Cloud takes its posture from its REAL host: our own serving plane (vLLM on our GKE cluster, attested content-free; the experiential_cloud row) or a house lane shown under that name, which keeps its host's row and exceptions. The label never changes the verdict.pa/*, anthropic/*, openai/*) are classed not-ZDR for that reason while Novita's own open-weights serving is ZDR under its terms. OpenRouter picks an upstream per request, so its default routing is classed not-ZDR; under require_zdr, or a per-request provider.zdr, the gateway dispatches the constrained OpenRouter rung (provider.zdr plus "data_collection": "deny"), so OpenRouter serves from its published zero-retention endpoint list or refuses. A customer-run Modal deployment (the modalprovider) is ZDR only on Modal's Server, Auto Endpoint or Inference surfaces; a Modal Web Function retains inputs and outputs for up to seven days, which is the customer's deployment choice, not ours.Every rung the gateway routes for it resolves zero-data-retention in the matrix: data_policy.zdr: true on GET /v1/models, retention: zdr_all_rungs on GET /api/models. A model with some zero-retention rungs (zdr_enforceable) is served only through those under require_zdr; a model with none is refused. Models with no zero-retention route anywhere today: Claude Fable 5 / 5.1 and Claude Mythos 5 / 5.1 (Anthropic requires 30-day retention on every serving platform). Providers whose rows stay not-ZDR pending an account-level application or an owner decision: OpenAI, Azure OpenAI, Anthropic, Gemini, Vertex, xAI, Tencent, Cerebras; Bedrock's GPT-5.x rows flip only once the house account's data_retention_mode is verified none; Wafer's per-request header needs engine support.
The account-wide switch (Settings → Organization → Privacy → Zero data retention, or PUT /api/orgs/<org_id>/zero-data-retention with {"enabled": true}) sets all three facts in one audited action (zdr.enabled): storage off, require_zdr and require_no_training on, in one database transaction: a failure anywhere leaves every fact as it was. GET returns enabled (true only while all three hold), partial (true when the switch was turned on yet a fact no longer holds, so a granular drift is visible), enforced_at, the three facts and models_with_zdr_route: the public-catalog models that keep a zero-retention, no-training rung this organization's own policy permits, its allowlist applied, judged by the router's verdict. It is enforcement, not preference: with it on, a model with no zero-retention route is refused with the 403 above. The storage leg is part of the Pro plan, so a Free organization is refused with needs_subscription and can set the two routing toggles on their own instead. Turning the switch off clears the two routing toggles and hands storage back only to an organization that is not Pro (Pro is zero-retention by default); a Pro admin turns storage on deliberately through the telemetry route. The two granular toggles keep working beside it.
# Read: enabled (all three facts hold), partial, enforced_at, facts, models_with_zdr_route (this org's allowlist applied)curl -sS "https://api-pr-2007.preview.experientiallabs.ai/api/orgs/$ORG_ID/zero-data-retention" -H "Authorization: Bearer $EXPLABS_API_KEY"# Enforce account-wide (storage off + require_zdr + require_no_training, one audited action)curl -sS -X PUT "https://api-pr-2007.preview.experientiallabs.ai/api/orgs/$ORG_ID/zero-data-retention" \-H "Authorization: Bearer $EXPLABS_API_KEY" -H "Content-Type: application/json" \-d '{"enabled": true}'
A single request demands the same thing without any organization toggle: put a top-level "provider": {"zdr": true}(OpenRouter's own field shape) on the body of /v1/chat/completions, /v1/responses or /v1/messages (through the Anthropic SDK's extra_body). It only tightens. With no organization policy the request is judged as if require_zdr were on; with one, require_zdris forced on for that request and an allowlist stays in force. The filter is the toggle's: natively zero-retention rungs first, then the constrained OpenRouter rung, which receives provider.zdr and "data_collection": "deny". A served response answers x-gateway-zdr: true. When no rung qualifies the request is refused with 403 and code model_not_granted, the body naming provider.zdr and the excluded providers. Other keys inside provider are forwarded verbatim to an OpenRouter Chat rung and dropped everywhere else.
provider before dispatch with 400 and the message The parameter 'provider' is not supported by this gateway profile. It is never silently ignored: that 400 means the flag is not live yet, and the organization policy above is the enforcement until it is.# Demand zero-data-retention providers for this request only (tightens; never loosens the org policy)curl -sS -i "https://api-pr-2007.preview.experientiallabs.ai/v1/chat/completions" \-H "Authorization: Bearer $EXPLABS_API_KEY" -H "Content-Type: application/json" \-d '{"model": "<slug>", "messages": [{"role": "user", "content": "hi"}], "provider": {"zdr": true}}'# served: x-gateway-zdr: true among the response headers# no qualifying rung: 403 model_not_granted naming provider.zdr and the excluded providers# engine pin without the field: 400 "The parameter 'provider' is not supported by this gateway profile."
Everything on this page is reachable with only the organization's xpl_ key, no human in the loop. The machine-readable copy, including the live provider matrix, is https://platform.experientiallabs.ai/.well-known/ai-data-policy.json (also linked from /.well-known/api-catalog and /llms.txt). Three calls cover discovery, the toggle, and verification.
# Which org the key acts forORG_ID=$(curl -sS "https://api-pr-2007.preview.experientiallabs.ai/api/whoami" -H "Authorization: Bearer $EXPLABS_API_KEY" | jq -r .org_id)# Per model: data_policy.zdr and the providers behind each rungcurl -sS "https://api-pr-2007.preview.experientiallabs.ai/v1/models" -H "Authorization: Bearer $EXPLABS_API_KEY" \| jq '.data[] | {id, data_policy}'# Every zero-data-retention rung with its capability flags (public, no key needed)curl -sS "https://api-pr-2007.preview.experientiallabs.ai/api/v1/endpoints/zdr"# The provider matrix the toggle is judged against, and the current policycurl -sS "https://api-pr-2007.preview.experientiallabs.ai/api/orgs/$ORG_ID/provider-data-controls" -H "Authorization: Bearer $EXPLABS_API_KEY"curl -sS "https://api-pr-2007.preview.experientiallabs.ai/api/orgs/$ORG_ID/provider-policy" -H "Authorization: Bearer $EXPLABS_API_KEY"
# Remove every non-zero-data-retention provider from routing for this orgcurl -sS -X PUT "https://api-pr-2007.preview.experientiallabs.ai/api/orgs/$ORG_ID/provider-policy" \-H "Authorization: Bearer $EXPLABS_API_KEY" \-H "Content-Type: application/json" \-d '{"allowed_providers": null, "require_zdr": true, "require_no_training": false}'
# x-gateway-provider names the rung that answered; x-gateway-zdr is its verdictcurl -sS -D - -o /dev/null "https://api-pr-2007.preview.experientiallabs.ai/v1/chat/completions" \-H "Authorization: Bearer $EXPLABS_API_KEY" \-H "Content-Type: application/json" \-d '{"model": "deepseek-v4-flash", "messages": [{"role": "user", "content": "ping"}]}' \| grep -i '^x-request-id\|^x-gateway-provider\|^x-gateway-zdr\|^x-gateway-route-depth'
After step 2 a request to a model with no zero-retention rung is refused with 403, code model_not_granted, and a message naming the model, require_zdr and the providers that were excluded; pick a model whose data_policy shows a zero-retention provider (or any row of /api/v1/endpoints/zdr), or remove the policy with DELETE /api/orgs/<org_id>/provider-policy. To also stop the platform storing content, send {"capture_prompt_content": false} to PUT /api/orgs/<org_id>/telemetry-settings (Pro, or already off for a Pro organization).
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.
| Header | Value |
|---|---|
| x-request-id | The gateway request id. Pass it to the lookups below. |
| x-gateway-provider | The 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-zdr | true or false: whether the rung that served the response is zero-data-retention in the matrix below, resolved at provider and model-pattern granularity from the same rung the provider header names. Absent only when the gateway cannot resolve the rung. |
| x-gateway-route-depth | The zero-based position of the serving rung in the route the gateway computed for your organization. 0 is the first rung. |
| x-gateway-route-reason | How 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-model | The exact model identity the requested slug resolved to. |
| x-gateway-alias / x-gateway-alias-revision | The 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.
curl -sS -D - -o /dev/null "https://api-pr-2007.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-'
x-request-id: <request id>x-gateway-alias: gpt-5.5x-gateway-alias-revision: <catalog revision id>x-gateway-canonical-model: <exact model id>x-gateway-provider: azure_openaix-gateway-zdr: falsex-gateway-route-depth: 1x-gateway-route-reason: direct
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.
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.# provider_name is the catalog provider of the rung that answeredcurl -sS "https://api-pr-2007.preview.experientiallabs.ai/api/v1/generation?id=$REQUEST_ID" \-H "Authorization: Bearer $EXPLABS_API_KEY"# provider, lane and attempt_count per request, newest firstcurl -sS "https://api-pr-2007.preview.experientiallabs.ai/api/gateway/usage/events?org_id=$ORG_ID&limit=20" \-H "Authorization: Bearer $EXPLABS_API_KEY"
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.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.
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.
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.x-gateway-provider and the body's provider name your provider. The provider policy applies to rungs on your key as well.x-gateway-zdr and x-gateway-providerare the gateway's own reading of the matrix for the rung that served (the per-model exceptions included), not a statement signed by the provider.require_zdron the gateway binds every OpenRouter dispatch to OpenRouter's published zero-data-retention endpoint list and records the upstream it selected per request; the guarantee itself is OpenRouter's, not a provider-signed statement. Wafer's per-request header is not sent, so its lane is classed not-ZDR.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.