# Response headers

Response headers identify a request, explain model selection and expose cache or control decisions. Read the presence condition for each header: a missing value does not necessarily mean the feature is disabled.

## Request identity

Use the two request IDs for different forms of correlation.

| Header | Meaning | Presence and constraints |
|---|---|---|
| `X-Request-ID` | Log correlation ID | Accepts a caller value of 1–128 letters, digits, `.`, `_` or `-`; otherwise the gateway generates one |
| `X-Pointsman-Request-ID` | Gateway-generated request UUID | Set before early refusals, including 401 and accounting/intake 503s; joins usage, provider dispatch and reservation records |
| `X-Requested-Model` | Requested model after whitespace stripping | Describes the request, not the serving model |

A supplied `X-Pointsman-Request-ID` is ignored. Reusing `X-Request-ID` does not reuse the gateway UUID. If the client disconnects before receiving headers, it cannot recover that attempt's UUID from the response.

<a id="the-ones-that-are-always-there"></a>

## Cache outcome

Cache headers describe the path taken by the request.

| Header | Value | Presence |
|---|---|---|
| `X-Cache` | `MISS`, `HIT-L1` or `HIT-L2` | Cache outcome on the response path; L1 is exact, L2 is semantic |
| `X-Cache-Threshold` | Required semantic similarity, default `0.92` | Only when a semantic tier is configured for the request modality |
| `X-Cache-Semantic` | Reason listed below | Explains whether semantic lookup ran or was skipped |

### Semantic reason vocabulary

Interpret the reason together with `X-Cache`.

| Reason | Meaning |
|---|---|
| `consulted` | Semantic lookup ran |
| `not-consulted` | Exact cache already answered |
| `skipped-short` | Text is below the semantic minimum length |
| `skipped-temperature` | Sampling policy excluded reuse; default `exact-only` excludes nonzero or omitted temperature |
| `skipped-tools` | Request declares tools |
| `skipped-window` | Embedding-window eligibility could not be established; also includes bounded eligibility-work refusal |
| `skipped-mutable-image` | Remote image URL is not cacheable |
| `skipped-circuit-open` | L2 breaker skipped embedding and index lookup |
| `replay-refused` | Cached serving model was outside the caller's allow set; request routed as a miss |
| `bypassed` | Request directive skipped lookup |
| `disabled` | Cache path disabled |
| `unready` | Required cache capability not ready |
| `error` | Cache operation failed |

See [cache behavior](../semantic-cache.md) for eligibility and failure policy.

<a id="the-ones-that-appear-only-when-something-happened"></a>

## Model resolution and routing

These headers appear when the corresponding decision occurred.

| Header | Meaning | Presence |
|---|---|---|
| `X-Resolved-Model` | Annotated resolution result | Resolution changed the requested name |
| `X-Model-Resolution` | How the request was served or substituted | Resolution/fallback path supplied a reason |
| `X-Routing-Mode` | Selected smart-routing mode | Smart-routed request |
| `X-Semantic-Group` | Group selected by prompt meaning | No explicit model or applicable key default; match cleared the threshold |
| `X-Semantic-Score` | Winning cosine similarity, four decimal places | With `X-Semantic-Group` |
| `X-Attempted-Models` | Models attempted in order | Request entered the fallback ladder; pre-attempt substitution alone has no attempted models |

`X-Resolved-Model` is annotated, for example:

```text
X-Resolved-Model: gpt-4o-mini (normalized from "GPT-4O-MINI")
```

Reasons include `alias`, `prefix`, `normalized`, `redirect`, `default`, `fallback`, `substituted`, `smart:<mode>` and `semantic`. Do not treat the complete value as a bare model name. `X-Provider-Model`, when present, describes the provider-reported name instead.

A health exclusion before any attempt can report `fallback(<model> -> <served>, pre-attempt, unavailable)` without `X-Attempted-Models`. The reason does not disclose whether another caller's traffic opened a breaker or caused cooldown.

## Limits and guardrails

Control headers are conditional on a matching rule or action.

| Header | Meaning | Presence |
|---|---|---|
| `X-RateLimit-Remaining-Requests` | Tightest remaining request headroom across applicable rules | A request-count rule matched |
| `X-RateLimit-Remaining-Tokens` | Tightest remaining token headroom across applicable rules | A token-count rule matched |
| `X-Degraded` | Limit-rule substitution, `<from>-><to>` | A degrade action selected another model |
| `X-Guardrail-Result` | Guardrail action | A rule acted |

The request and token headroom headers are independent. Absence means no rule of that metric matched this request, not a promise of unlimited capacity.

## Outcome-learning feedback

Only an outcome-learning routing decision supplies feedback headers. A direct model request has none. A non-learning group such as weighted-random can log a decision without returning a feedback handle.

| Header | Purpose | Attribution boundary |
|---|---|---|
| `X-Decision-Id` | Bare decision identifier | Uses the originating gateway's in-process memo |
| `X-Decision-Receipt` | Signed receipt for the same decision | Carries attribution so another gateway instance can process feedback |

Send either value as `decision_id` when rating an answer; prefer the receipt when requests can reach different instances. If neither header exists, there is no outcome-learning decision to rate.

Treat a receipt as opaque. Its security and lifetime properties are:

- **Signed, not encrypted:** holders can decode sampling key, served arm, routing provenance and trace IDs.
- **Caller-bound:** the requesting API key is bound into the signature; another key cannot use it.
- **Time-bounded:** it expires with `pointsman.bandit.decision-memory-ttl-min`, default 15 minutes.
- **Revision-aware:** `v3.` includes decision time and sealing time so receipt and bare-ID feedback refer to the same decision. Older `v2.` receipts remain accepted until expiry.

## Serving identity

Use serving evidence separately from the originally requested name.

| Header | Meaning | Presence and limits |
|---|---|---|
| `X-Served-Provider` | Provider that answered | Synchronous provider response or first live-stream chunk; absent on cache hits |
| `X-Served-Generation` | Admitted `reasoning_effort` or `thinking_mode` setting | Model row fixes that setting; synchronous, buffered and first live-stream decoration. Also set on synchronous cache hits; that cache case lacks separate test coverage |
| `X-Provider-Model` | Sanitized provider-reported model | Synchronous provider-served response; absent on live streams and cache hits |

`X-Provider-Model` retains at most 96 characters and replaces characters outside `[A-Za-z0-9._:/@+-]`. It is identity evidence, not the billing identity. An unexpected reported name leaves usage unverified; see [compatibility](compatibility.md).

## Experimental quality routing

These headers apply only to the corresponding opt-in quality feature.

| Header | Meaning | Presence |
|---|---|---|
| `X-Quality-Route` | Served model was `qualified` or the declared `baseline`, with reason, profile and policy version | Quality-guarded group; `outside` is an invariant breach |
| `X-Quality-Receipt` | Signed, unencrypted binding of organization, decision, model, profile, policy and serving conditions | With quality routing when the key belongs to an organization; cite in `/v1/quality/assessments` |
| `X-Quality-Audit` | `selected; lane=audit; p=<rate>` | Response selected before its outcome; missing labels leave the sealed cohort incomplete |
| `X-Quality-Cascade` | `primary_accepted; stages=1; check=format` or `escalated_accepted; stages=2; check=format` | Every released answer in an enabled cascade group |

`check=format` establishes a format check, not semantic correctness. An unverified cascade refuses with `502 quality_cascade_unverified`. See [quality-guarded routing](../how-to/quality-guarded-routing.md) and its [bounded cascade](../how-to/quality-guarded-routing.md#7-the-bounded-cascade-experimental-off).
