Gateway API
The governed inference gateway: the /v1/messages, /v1/chat/completions and /v1/responses contracts, the required x-session-id header, the model list, status codes, and the three profile API URLs.
On this page
TL;DR: The gateway serves three inference endpoints under /v1, one for
each client wire format. Each one authenticates, authorises, governs and
audits the call before it is forwarded upstream. Every request needs a
credential (a personal access token, or a short-lived JWT exchanged from
one) and an
x-session-id header that matches a session the gateway issued. The model
must be one the model catalog or a route names, or the request is refused before
any upstream call. Every model is served by Vertex AI, whichever wire format
the client speaks.
Endpoints
| Method and path | Format | Used by |
|---|---|---|
POST /v1/messages |
Anthropic Messages (model, max_tokens, messages[]) |
Claude Code, the Anthropic SDKs |
POST /v1/chat/completions |
OpenAI Chat Completions | OpenCode and other OpenAI-compatible tools |
POST /v1/responses |
OpenAI Responses | Responses-API clients |
GET /v1/models |
model list | model pickers; lists every advertised model |
All three inference endpoints share one pipeline: authentication, session
check, route and entitlement, governance chain, safety scanners, quota
windows, dispatch, audit. The format a client speaks is independent of the
provider that serves it. An OpenCode request on /v1/chat/completions for
claude-sonnet-5-5 is served by Claude on Vertex, exactly like the same model
requested on /v1/messages.
Base URL for OpenAI SDK clients
An OpenAI-compatible client takes a base URL and appends /chat/completions
(or /responses, /models) itself. Set it to:
https://<host>/v1
There is no /openai/v1 prefix. A client configured with
https://<host>/openai/v1 requests /openai/v1/chat/completions, which does
not exist, and gets 404. Anthropic SDK clients take the bare host
(https://<host>) and append /v1/messages.
Request contract
| Header | Purpose | If missing or wrong |
|---|---|---|
Authorization: Bearer <jwt or PAT> or x-api-key: <PAT> |
Caller identity | 401 |
x-session-id: <session id> |
Binds the call to a session for audit and conversation continuity | missing: 400 (missing required x-session-id header); not the caller's session: 401 |
x-gateway-conversation-id: <id> (optional) |
Pins the conversation; otherwise it is derived from the messages | — |
x-systemprompt-scope-<dimension>: <value> (optional) |
Attributes the request to one value in a scope dimension, such as project |
see Scope headers |
The session must be one the gateway issued:
- Exchanged JWT. The session is minted with the token.
x-session-idmust equal the JWT'ssession_idclaim. - Personal access token (the supported Claude Code path:
ANTHROPIC_BASE_URL,ANTHROPIC_AUTH_TOKENset to the PAT, and the session id below). First mint a session withPOST /api/public/gateway/sessions, using the same PAT, then send the returnedsession_id. An unknown or revoked session gets401withunknown or revoked session; mint one at POST /api/public/gateway/sessions.
A CLI admin session token is not a gateway credential. It fails with
kid does not match any known signing key.
Scope headers
Every request is attributed to one value in each scope dimension the instance registers. For each dimension the value is resolved in this order:
- the
x-systemprompt-scope-<dimension>header; - the value the API key is bound to for that dimension;
- the caller's default (first) value for that dimension.
The chargeback dimensions this instance registers:
| Header | Value | Default when absent |
|---|---|---|
x-systemprompt-scope-project: <project id> |
a project the caller is a live member of | the caller's primary project |
x-systemprompt-scope-cost_centre: <code> |
the cost centre of one of the caller's projects | the primary project's cost centre |
x-systemprompt-scope-practice: <name> |
the practice of one of the caller's projects | the primary project's practice |
The dimension name is written as registered, underscore included
(cost_centre). The instance also registers access dimensions (group,
role, organization and others) that a header can name under the same
rules; they exist for access rules and quota windows, not chargeback.
| Answer | Cause |
|---|---|
403 |
the header or key-bound value is not one the caller holds (not a member of <dimension> '<value>'). An unknown value answers the same, so the header cannot probe for existence |
400 |
a header names a dimension the instance does not register, is empty, or appears more than once |
400 scope_required |
a dimension listed in gateway.require_scopes resolves to nothing. This instance requires none |
The resolved values and their source are recorded on the request and can pick a route's per-scope chain.
Models
The model catalog (services/ai/models.yaml, served by the single vertex
provider in services/ai/providers.yaml) and the routes
(services/ai/gateway.yaml) ship in the image. A request is dispatched only
if its model matches a route or is declared in the catalog; nothing else is
forwarded.
| Family | Ids a client can request |
|---|---|
| Claude (served from Vertex AI) | claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5 |
| Gemini on Vertex AI | vertex-gemini-3.8-flash, vertex-gemini-3.5-flash, vertex-gemini-3.5-flash-lite |
| Open models on Vertex | zai.glm-5.2 |
GET /v1/models is the authoritative list for a running instance. These are
the only Claude models the Vertex project serves for this installation.
Retired Claude ids a client may still send are aliased to the 5.5 model of the same tier, never across tiers:
| Requested | Served as |
|---|---|
claude-haiku-4-5, claude-haiku-4-5-20251001 |
claude-haiku-5-5 |
claude-sonnet-5 |
claude-sonnet-5-5 |
claude-opus-5 |
claude-opus-5-5 |
claude-fable-5 and claude-fable-5-1 are not served and not aliased: a
request for them is refused rather than silently answered by a lower tier.
The Claude 4.x Opus and Sonnet ids and other retired ids are refused too.
gateway.default_model (claude-sonnet-5-5) is the default model handed
to Claude Code clients. A request is not rerouted to it. The [1m] suffix is
Claude Code's 1M-context marker, which the gateway accepts on any model and
strips before routing.
Status codes before the upstream call
| Status | Cause |
|---|---|
400 |
missing x-session-id, a malformed body, or a model not permitted by gateway policy (model '<id>' is not permitted by gateway policy) |
401 |
missing, invalid, expired or revoked credential, or a session that does not belong to the caller |
403 |
the caller is not entitled to the matched route (authz denied [<policy>]: <reason>), a scope value the caller does not hold, or a model outside the API key's allowlist (model '<id>' is not allowed for this API key) |
429 |
a quota window is exhausted. Not returned on this instance, where quotas run in warn mode |
503 |
the process is at its server.max_in_flight ceiling (error_key: "overloaded", Retry-After: 1). Unset on this instance, so never returned |
A quota 429 carries a Retry-After header equal to the seconds until the
window resets, and a machine-readable body:
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "...",
"quota": {
"window_seconds": 86400,
"subject": "user",
"dimension": "input_tokens",
"limit": 5000000,
"used": 5000120,
"resets_at": "2026-10-08T00:00:00Z",
"retry_after_seconds": 3600
}
}
}
The values above are illustrative. subject is the window's subject (user,
api_key or a scope dimension) and dimension names the ceiling breached:
requests, input_tokens, output_tokens or cost_microdollars.
Governance and safety denials also answer before the upstream call. Both planes run in warn mode on this instance and do not deny: see Governance Modes.
Governance requirements on routes
Providers and models carry a governance: block (european, no_retain),
and a route can require either with requires:. A route whose target does
not satisfy its requirement fails boot, and dispatch checks the resolved
target again. No route on this instance declares a requirement today.
Profile API URLs
A profile declares three server URLs; pick the right one for the caller:
| Field | Use it for |
|---|---|
api_internal_url |
Service-to-service calls inside the deployment network |
api_server_url |
Server API base URL; the listening interface and port are configured separately through host and port |
api_external_url |
The public, host-facing address; used for OAuth/WebAuthn URL generation and consumed by session login |
When the host-published port differs from the in-container port, an in-container caller must use the in-container address and host callers the published one; a single value cannot satisfy both.