Roll out an LLM gateway
An admin checklist for putting your own LLM gateway in front of Claude Code, from routing checks and developer keys to managed settings and upgrades.
This is the admin's runbook for putting an existing gateway product in front of Claude Code across a team. It assumes the gateway is already installed per its vendor's documentation and meets the requirements below. Developers configuring their own machine want Connect Claude Code to an LLM gateway; the full list of what Claude Code sends lives in the compatibility guide.
Before you start
You need three things:
- A gateway on HTTPS at its final address. Serve the exact URL you will hand out, not one that redirects. Redirects can drop the request body or strip the credential header, and model discovery treats any redirect as a failure so the credential cannot leak to the target.
- A provider credential for the gateway. For the Anthropic API that is a Console API key; for a cloud provider, cloud credentials with model access (see Amazon Bedrock, Google Vertex AI or Microsoft Foundry).
- A way to push files to laptops, such as MDM or configuration management. Set up Claude Code for your organisation compares options if you have none.
What the gateway must do
| Requirement | Why it matters |
|---|---|
| Accept a supported API format | This runbook assumes Anthropic Messages at POST /v1/messages, which most products offer |
| Stream server-sent events as they arrive, keep-alive pings included | Buffering makes sessions look hung and can trip timeouts |
Map Claude model names (for example claude-sonnet-4-6) to upstream models | Unmapped names return 404 to whoever selects them |
Pass anthropic-beta, anthropic-version and the body through unchanged, both ways | Stripped values silently break the features that need them |
| Return upstream errors as-is | Claude Code's recovery logic matches on error wording. Wrapping errors in your own envelope breaks it, unless the message carries one of the capability_rejected: tokens described in Claude apps gateway configuration |
| Exempt the path from WAF body inspection | Prompts contain source code and XML-like tags that trip cross-site-scripting rules, so short tests pass and real sessions get 403 |
Serving GET /v1/models is optional but lets Claude Code fill the model picker from the gateway.
Three credentials, three placeholders
Most failed rollouts I have debugged came down to confusing these. The checkpoints below name them explicitly.
| Credential | Held by | Shown below as |
|---|---|---|
| Provider credential | The gateway only, sent upstream | Never appears in client commands |
| Gateway admin or test credential | You, if your product has one | <admin-key> |
| Developer key | Each developer, issued by the gateway | <dev-key> |
Step 1: prove the gateway routes your models
Send a minimal request with whatever key currently works (an admin key, a test key, or your own developer key if the product has no admin credential):
curl "https://claude-gw.northwind.internal/v1/messages" \
-H "Authorization: Bearer <admin-key>" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-4-6","max_tokens":1,"messages":[{"role":"user","content":"hi"}]}'
Checkpoint. 200 with a content field: the gateway reached the provider. 404: that model name is not routed. 401 from the provider: the gateway's provider credential is wrong. Repeat for every model name in your routing table, because anyone selecting an untested name gets a 404.
Step 2: issue a key per developer
Create one credential per person using your product's key management. Shared keys defeat both per-person attribution and clean offboarding. Re-run the step 1 request with a freshly issued <dev-key>.
Checkpoint. 200 means the developer key works end to end. A 401 here, when step 1 passed, means the key is wrong or not yet active.
Note which header the gateway reads, because it decides the client variable: Authorization: Bearer means ANTHROPIC_AUTH_TOKEN, x-api-key means ANTHROPIC_API_KEY.
Step 3: run Claude Code through it yourself
Type these straight into a terminal (not a .env or settings file) so closing the terminal undoes them:
export ANTHROPIC_BASE_URL=https://claude-gw.northwind.internal
export ANTHROPIC_AUTH_TOKEN="<dev-key>"
claude -p "Answer with the single word: routed"
Checkpoint. You get an answer, and the gateway log shows a POST to /v1/messages with 200. Claude Code appends a query string such as ?beta=true, so match on the path.
If it fails:
Not logged in. Check the gateway log. Empty log: no credential reached the session, so re-export in this shell. A401whose body mentionsx-api-key: the gateway wants that header, so useANTHROPIC_API_KEYinstead.Failed to authenticate. API Error: 401. A credential was sent and refused. If the log shows the401coming fromapi.anthropic.comor your provider, the developer key was fine and the gateway's provider credential is the problem.- Apparent hang. A wrong or unreachable base URL causes retries with backoff that can run for minutes. If no request appears in the gateway log,
ANTHROPIC_BASE_URLis not pointing at the gateway.
Step 4: distribute the configuration
What to send
| Variable or setting | Purpose | When to include |
|---|---|---|
ANTHROPIC_BASE_URL | Sends requests to the gateway instead of api.anthropic.com | Always |
apiKeyHelper, ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY | Authenticates to the gateway | Always, exactly one |
ANTHROPIC_CUSTOM_HEADERS | Extra header on every request | Gateway needs a tenant or routing header |
CLAUDE_CODE_GATEWAY_HINT_HEADERS | Sends the gateway hint headers for routing and scheduling (v2.1.273+) | Gateway reads them |
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY | Populates /model from the gateway's /v1/models | Gateway serves that endpoint |
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS | Stops pre-release headers and fields | Upstream is Bedrock or Agent Platform and rejects beta fields |
CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS or CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK | Restores fast mode when its direct check to api.anthropic.com fails or is skipped | You use fast mode and auth is gateway-only, or direct egress is blocked |
ANTHROPIC_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL | Names requested for the main session and background work | Gateway names differ from Claude Code's defaults. Route the built-in IDs too, since some background sub-calls request them regardless |
ANTHROPIC_BEDROCK_BASE_URL, ANTHROPIC_VERTEX_BASE_URL, ANTHROPIC_FOUNDRY_BASE_URL or ANTHROPIC_AWS_BASE_URL plus that provider's variables | Provider-specific route | Gateway fronts that provider in its native format; see the connect page |
Costs explains which background functions use the Haiku-class model.
Through managed settings (recommended)
Put the values in the env block of a managed settings file and push it:
{
"env": {
"ANTHROPIC_BASE_URL": "https://claude-gw.northwind.internal",
"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"
},
"apiKeyHelper": "/opt/northwind/bin/claude-gw-key"
}
A managed ANTHROPIC_BASE_URL beats a developer's shell export and every lower-precedence settings file. The apiKeyHelper approach is the tidy one: a single command that authenticates to your secrets store as the logged-in user, so each laptop fetches its own key and nothing per-person goes in the file. The alternative is delivering keys through your existing secrets process and having developers set ANTHROPIC_AUTH_TOKEN.
Things to avoid and edge cases:
- Never combine a gateway credential with
forceLoginMethod,forceLoginOrgUUIDorforceLoginGatewayUrl. The first two, with any value, blockANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKENandapiKeyHelperat startup. Developers seeThis machine's managed settings require a first-party login, orAdministrator policy requires a Cloud gateway sign-inwhenforceLoginMethodis"gateway"orforceLoginGatewayUrlis set. - Server-managed settings will not reach these sessions. That delivery route needs a direct connection to
api.anthropic.com. Use file-based managed settings, which enforce the same keys. See Server-managed settings. - The desktop app reads its own third-party inference configuration, not managed settings; push that file via MDM as well.
- CI runners need the base URL and credential in the runner environment.
- WSL on managed Windows reads Windows managed settings only when
wslInheritsWindowsSettingsistrue.
To go further and forbid any other destination, see the allowedProviders example in Other LLM gateways.
HIPAA and gateways
Gateway-routed sessions are not eligible for the HIPAA configuration; HIPAA setup lists which sign-in and connection methods are. You can still restrict features with the managed keys covered in Monitoring usage, but they neither make a session eligible nor cover everything the configuration changes. No managed key turns cloud sessions off, and no setting alone strips Anthropic credentials from child processes. Do not reach for CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC as a substitute: it also kills the auto-updater and leaves WebFetch on.
Without settings distribution
Send each developer the gateway URL, their personal key, which variable to put it in (ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY) and any conditional variables with values. Point them at the connect page. Naming the variable saves them guessing.
Checkpoint. On a developer machine, claude opens straight into a session with no login screen. In /status, the Anthropic base URL line shows the gateway and, for managed delivery, Setting sources includes managed settings.
Step 5: verify from a developer machine
Test from a laptop, not the gateway host, so the real network path is exercised. A streaming request checks the endpoint, streaming pass-through and routing in one go:
curl -N "https://claude-gw.northwind.internal/v1/messages" \
-H "Authorization: Bearer <dev-key>" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-4-6","max_tokens":16,"stream":true,"messages":[{"role":"user","content":"list three colours"}]}'
data: lines should trickle in. Everything arriving at once after a pause means the gateway is buffering, which will stall Claude Code. On Windows, pipe the body into curl.exe with --data-binary '@-' to avoid PowerShell quoting problems.
Then open claude and send a message:
- Login prompt. In
/status, ifSetting sourceslacks managed settings, the file never arrived. If it is there, the credential is what is missing. Failed to authenticate. The gateway log says which credential failed: its own rejection names the developer key; a401from the provider means the provider credential.- One-time key approval prompt. Expected on first use with
ANTHROPIC_API_KEY.ANTHROPIC_AUTH_TOKENtakes over silently.
If you use fast mode, run /fast too. Its availability check bypasses the gateway, so it can report unavailable or disabled while inference is fine; the skip variables in the table fix that.
Finally, find your message in the gateway logs. The credential identifies the developer and the x-claude-code-session-id header groups requests into sessions.
Keeping it healthy
| What changes | What you will see | What to do |
|---|---|---|
| New Claude Code releases add beta values and body fields | 400 errors naming a new field after people update | Forward anthropic-* headers and bodies verbatim rather than allowlisting; test releases before they reach the fleet |
| New Claude models ship | 404 on the new name; it is missing from /model | Add the name to routing, re-run step 1, and update any distributed model variables |
| Credentials expire | Every request starts failing with 401 from upstream | Rotate the provider credential on its own schedule; apiKeyHelper handles developer key rotation without redistributing files |
When sizing per-key rate limits, remember the client retries transient failures, 429 included, up to 10 times with backoff and honours Retry-After.
Controlling upgrades
Some behaviour is baked into each Claude Code version, so an upgrade can change things with no gateway change at all. Pin a tested version with requiredMaximumVersion in settings, or DISABLE_UPDATES if you ship Claude Code yourself (see Setup). Before raising the pin, read the release notes and repeat step 3.
| Area | What can shift on upgrade | How to hold it steady |
|---|---|---|
| Feature-flag defaults | Sessions that do not fetch flags from Anthropic (cloud providers, telemetry off) use the defaults compiled into the version | The version pin |
| Capability assumptions for unknown IDs | An alias such as team-opus runs on default assumptions for adaptive reasoning, effort and context window until recognised | Route Anthropic model IDs at the gateway, or map the ID to your alias with modelOverrides; on cloud provider connections, declare capabilities instead |
| Default model and aliases | What new sessions start on and what opus or sonnet resolve to | ANTHROPIC_DEFAULT_MODEL (v2.1.236+) and the ANTHROPIC_DEFAULT_*_MODEL variables |
Model configuration covers each of these variables.