https://your-relay/claude-code, that carries the messages endpoint plus what the client calls around it: model discovery (GET /claude-code/v1/models), token counting (POST /claude-code/v1/messages/count_tokens) and the startup connection probe (HEAD /claude-code/api/hello).
There are two ways to use it. In routed mode a relay key is the credential and Relay picks the upstream from the key’s policy, so Claude Code can drive any model in your catalog. In proxy mode Claude Code keeps its own Claude login and Relay records and forwards its requests.
Routed mode
Claude Code authenticates with a relay key, and Relay resolves the model through the key’s policy like any other client, including non-Anthropic models (a self-hosted Ollama build, OpenAI, Gemini), which Relay translates into and out of the Anthropic shape on the way. Set the base URL and the key; Relay accepts the key as a bearer token:Model discovery
SetCLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 and /model lists the models the key’s policy grants, each row subtitled with the hosts serving it and their input/output rates per million tokens where the binding is priced.
Non-Anthropic models appear as claude/<model>. Claude Code keeps only picker entries whose id contains claude or anthropic, so Relay lists everything else under that prefix and strips it again on the way in: --model claude/gemma4-e4b and --model gemma4-e4b both reach the same model.
Subagents
A custom agent file’smodel: is sent verbatim, so any model in your catalog can be pinned per agent: put model: gemma4-e4b in the frontmatter of .claude/agents/reviewer.md and that subagent runs on it while the main loop stays where it is. To remap the built-in aliases instead, set ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_OPUS_MODEL, or ANTHROPIC_DEFAULT_HAIKU_MODEL.
Settings
To route every session, put the same values underenv in ~/.claude/settings.json (or a project-level .claude/settings.json):
CLAUDE_CODE_MAX_CONTEXT_TOKENS to match the context window of the model you route to (the built-in default assumes a Claude window), and CLAUDE_CODE_GATEWAY_HINT_HEADERS=1 so requests carry the client’s request-class, agent-type and compaction headers; see attribution below.
Proxy mode
Proxy mode keeps Claude Code authenticating to Anthropic with its own credentials, which pass through Relay untouched in theAuthorization header. Relay identifies the traffic by your relay key, sent in X-WR-API-Key, and forwards the request unchanged to Anthropic, so your Claude login or ANTHROPIC_API_KEY keeps working as before and Relay records each request.
Claude Code keeps its own Anthropic credential; Relay observes and forwards.
Prerequisites
-
Proxy mode enabled on your relay:
-
A relay key allowed to forward upstream credentials. Proxy requests are refused with
403 passthrough_forbiddenunless the relay key hasspec.passthroughAllowed: true. Set it when you create the key:or set it on an existing key withPUT /api/keys/by-id/{id}. Inference tokens never carry this flag, so proxy mode needs a relay key. -
A policy that grants the Claude models Claude Code uses (Sonnet, Opus, Haiku) on that key. Without an explicit host header, Relay reads the
modelfrom the request and picks the upstream host from the key’s policy, so the models must be granted. The policy’s rate limits apply to proxied requests too. - (Optional) Payload logging turned on, if you want to capture the full request and response bodies. See Payload logging.
Setup
Set three things through environment variables: the relay’s Claude Code base URL, and two custom headers (X-WR-API-Key and X-WR-Proxy-Mode). Claude Code’s own login stays as it is and passes through as the upstream credential.
Option A: a wrapper command
Drop a small script on yourPATH (e.g. wcc) so you opt in per-invocation:
wcc instead of claude when you want the session tracked; plain claude stays direct.
Option B: Claude Code settings
To route every session, set the same values underenv in ~/.claude/settings.json (or a project-level .claude/settings.json):
ANTHROPIC_BASE_URL is host + the /claude-code prefix; Claude Code appends /v1/messages and the discovery and probe paths itself. ANTHROPIC_CUSTOM_HEADERS is newline-separated Name: Value pairs.What you get
Once a session runs through Relay:- Usage: every Claude Code request appears in
GET /api/logsand the/api/usage/*endpoints of the control API, attributed to your relay key, with token counts, cost and timing. - Payloads: with payload logging enabled, the full prompts and completions are captured and readable at
GET /api/logs/{request_id}: what Claude Code sent and what the model returned. - Attribution: the client’s own
x-claude-code-session-id,x-claude-code-agent-idandx-claude-code-parent-agent-idheaders, plusx-claude-code-request-class,-agent-type,-compactionand-context-compactedwhenCLAUDE_CODE_GATEWAY_HINT_HEADERS=1, are recorded on the usage event asextras, alongsideclient=claude-code. That is what lets you group and cost a whole session, or one subagent inside it.
ping frame while an upstream is silent, so a long generation doesn’t trip the client’s idle watchdog. The interval is 15 s by default (RELAY_STREAM_KEEPALIVE_S); see Configuration for it and the matching upstream idle timeout.
Notes
- Setting
ANTHROPIC_BASE_URLdisables Claude Code’s MCP tool search by default; re-enable it withENABLE_TOOL_SEARCH=trueif you use it. - Proxy mode does no translation: Claude Code’s Anthropic requests are forwarded byte for byte.
- When an upstream rejects a request on every key, Relay forwards that response as it stands (original status,
Retry-Afterand body) instead of replacing it with a generic gateway error, so the client backs off on the provider’s own terms.
Known limits
- Silence before the first byte. Relay’s keepalive frames start once the upstream has sent its response headers. Some servers, Ollama among them, hold the headers until the first token is ready, so a model that spends longer than Claude Code’s idle limit (300 s by default) processing the prompt is cut off by the client before Relay can send anything. This happens with CPU-bound inference; on a GPU or a hosted upstream, prompt processing finishes in seconds. A smaller
--toolsset or--bareshrinks the prompt if a slow machine is all you have. - Cross-shape prompt caching. When Relay translates to a non-Anthropic upstream, the
systemarray is flattened into one instruction block, andcache_controlmarkers have no equivalent there: OpenAI-compatible upstreams cache prefixes automatically and Gemini upstreams ignore them. Same-shape Anthropic upstreams receive both untouched. See Prompt caching. /contextbefore the first turn. Relay answerscount_tokensexactly only where the upstream has a counting endpoint (Anthropic); elsewhere the figure is calibrated from earlier turns of the session or estimated, and theX-WR-Token-Countresponse header says which.