Response headers
Every X-Stav-* header Stav emits, on which surface, what it tells you, and how to use them for audit, cost attribution and debugging.
Stav's response headers are the per-request receipt: which model served, from which Inference Provider, at what sovereignty level, and why.
All three chat surfaces — /v1/chat/completions, /v1/messages and /v1/responses — send the routing headers. A few are chat-completions only — see By surface.
On every response
| Header | Example | Meaning |
|---|---|---|
X-Stav-Request-Id | chatcmpl-8f3a1c… | The request id. Same value as the completion |
X-Stav-Model | anthropic/claude-sonnet-4-5 | The model that served — not necessarily what you asked for |
X-Stav-Provider | anthropic | The Inference Provider that served |
X-Stav-Sovereignty-Level | L3 | The sovereignty level of the serving Provider Endpoint: |
X-Stav-Route-Time-Ms | 31.4 | Streaming: handler start to provider dispatch. Non-streaming: total handler time including the provider round-trip. Do not compare the two |
X-Stav-Route-Lock | none | Which continuity lock held the route to your previous turn's model: |
Conditional
| Header | Sent when | Meaning |
|---|---|---|
X-Stav-Route-Reason | a routing reason was recorded | One-line, human-readable rationale. Do not parse it |
X-Stav-Router | a Router handled the request |
|
X-Session-Id | a conversation session was resolved | Send it back as |
X-Stav-Session-Id | a prompt-cache affinity hash was computed | Internal cache-affinity hash. Not the conversation id |
X-Stav-Reasoning-Effort | the request carried a reasoning control, or the router chose one | The level that reached the provider after translation, or |
X-Stav-Reasoning-Chosen-By | always on a chat surface | Who chose that level: |
X-Stav-Upstream-Surface | the request reached a provider | The provider API that served it: |
X-Stav-Fallback-From | a fallback model of your workspace's rule answered (non-streaming) | The model you named, e.g. |
X-Stav-Dropped-Params | something you sent was removed | Comma-separated names, e.g. |
X-Stav-Clamped-Params | a value you sent was lowered to the Provider Endpoint's own limit |
|
X-Stav-Endpoint-Status | the serving endpoint is in its deprecation window |
|
X-Stav-Endpoint-Sunset | the serving endpoint has a sunset date | ISO 8601 timestamp |
X-Stav-Replacement-Endpoint | a replacement is set | The recommended endpoint id |
X-Stav-BYOK | your workspace's own provider key was involved |
|
X-Stav-Stage-Ms | streaming responses | Per-stage timing breakdown, for debugging |
X-Stav-Audio-Seconds |
| The seconds of audio the request was billed for. See Audio transcriptions |
X-Stav-Compute-Countries | the serving endpoint's country is known | Every country the request may have run in, comma-separated ISO 3166-1 alpha-2, e.g. |
X-Stav-Compute-Country | the serving endpoint runs in exactly one known country | That country, ISO 3166-1 alpha-2. Absent when the endpoint runs in one of several countries — read |
X-Stav-Sovereignty-Badge | the model has a legacy badge | Deprecated. Read |
Not generally available
Stav Tunnel is not enabled for workspaces in production. These appear only when a workspace's own node served the request:
| Header | Meaning |
|---|---|
X-Stav-Served-By | own-resource. Non-streaming only |
X-Stav-Node-Id | The node that served |
By surface
✓ = streaming and non-streaming. "Non-streaming" = not on a stream: stream headers go out before dispatch. Each surface covers both its paths (proxied and translated).
| Header | /v1/chat/completions | /v1/messages | /v1/responses |
|---|---|---|---|
X-Stav-Request-Id, X-Stav-Model, X-Stav-Provider | ✓ | ✓ | ✓ |
X-Stav-Sovereignty-Level, X-Stav-Route-Lock | ✓ | ✓ | ✓ |
X-Stav-Router | ✓ | ✓ | ✓ |
X-Stav-Compute-Countries, X-Stav-Compute-Country | ✓ | ✓ | ✓ |
| Endpoint lifecycle (3 headers) | ✓ | ✓ | ✓ |
X-Stav-Reasoning-Effort, X-Stav-Reasoning-Chosen-By, X-Stav-Upstream-Surface | ✓ | ✓ | ✓ |
X-Stav-Dropped-Params | ✓ | Translated only | Translated only |
X-Stav-Clamped-Params | ✓ | Translated only | Translated only |
X-Stav-BYOK, X-Stav-Served-By, X-Stav-Node-Id | Non-streaming | Non-streaming | Non-streaming |
X-Session-Id | ✓ | ✓ | — |
X-Stav-Route-Reason | ✓ | auto and Routers | auto and Routers |
X-Stav-Route-Time-Ms, X-Stav-Session-Id | ✓ | — | — |
X-Stav-Stage-Ms | Streaming only | — | — |
/v1/audio/transcriptions has no stream. It sends X-Stav-Request-Id, X-Stav-Model, X-Stav-Provider, X-Stav-Sovereignty-Level, X-Stav-Route-Lock (always none), X-Stav-Compute-Countries / X-Stav-Compute-Country and X-Stav-Audio-Seconds. No Router headers — Routers do not serve it.
Browsers can read all of these and the rate-limit headers: they are in the CORS Access-Control-Expose-Headers list.
On errors
Every JSON error response carries X-Stav-Request-Id, and the body repeats it as request_id. See Errors.
A 429 from Stav's per-key rate limiter also carries:
| Header | Meaning |
|---|---|
Retry-After | Seconds to wait |
X-RateLimit-Limit | Requests per minute for this key |
X-RateLimit-Remaining | 0 |
X-RateLimit-Reset | Unix timestamp of the window reset |
A provider 429 passed through carries none of these.
Request headers Stav reads
| Header | Purpose |
|---|---|
Authorization: Bearer … | Your API key |
x-api-key | Alternative to Authorization (Anthropic SDK) |
X-Session-Id | Conversation id for sticky routing. A session_id in the body wins |
Reading them from SDKs
Python — OpenAI
raw = client.chat.completions.with_raw_response.create(
model="auto",
messages=[{"role": "user", "content": "hei"}],
)
print(raw.headers["x-stav-model"])
print(raw.headers["x-stav-sovereignty-level"])
resp = raw.parse()
TypeScript — OpenAI
const { data, response } = await client.chat.completions
.create({ model: "auto", messages })
.withResponse();
console.log(response.headers.get("x-stav-model"));
console.log(response.headers.get("x-stav-sovereignty-level"));
In the browser, cross-origin JavaScript can read only these: X-Stav-Request-Id, X-Session-Id, X-Stav-Model, X-Stav-Provider, X-Stav-Sovereignty-Level, X-Stav-Sovereignty-Badge, X-Stav-Route-Lock, X-Stav-Route-Reason, X-Stav-Route-Time-Ms, X-Stav-Reasoning-Effort, X-Stav-Reasoning-Chosen-By, X-Stav-Dropped-Params, X-Stav-Clamped-Params. Server-side clients see all of them.
Patterns worth adopting
- Log the receipt. Store
X-Stav-Request-Id,X-Stav-ModelandX-Stav-Sovereignty-Levelnext to your own trace id. - Alert on unexpected levels. With an L3 floor, a routed request should never report
L0. TreatUNCLASSIFIEDas an alert too. - Fail CI on lifecycle headers.
X-Stav-Endpoint-StatusandX-Stav-Endpoint-Sunsetare the earliest warning that a pinned model is going away.
Streaming caveat
Streaming headers are sent before the first chunk. If Stav fails over on the first chunk, X-Stav-Model and X-Stav-Reasoning-Effort describe the first endpoint tried, while the chunks name the model that answered. Trust the chunk model field.