Skip to main content
Claude Code speaks the Anthropic Messages API, and Relay serves it a dedicated base URL, 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

Set CLAUDE_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’s model: 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 under env in ~/.claude/settings.json (or a project-level .claude/settings.json):
Two more are worth setting: 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 the Authorization 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 routed through Relay in proxy modeClaude Code routed through Relay in proxy mode

Claude Code keeps its own Anthropic credential; Relay observes and forwards.

Prerequisites

  1. Proxy mode enabled on your relay:
  2. A relay key allowed to forward upstream credentials. Proxy requests are refused with 403 passthrough_forbidden unless the relay key has spec.passthroughAllowed: true. Set it when you create the key:
    or set it on an existing key with PUT /api/keys/by-id/{id}. Inference tokens never carry this flag, so proxy mode needs a relay key.
  3. A policy that grants the Claude models Claude Code uses (Sonnet, Opus, Haiku) on that key. Without an explicit host header, Relay reads the model from 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.
  4. (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 your PATH (e.g. wcc) so you opt in per-invocation:
Run 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 under env 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.
Model discovery works here too, and lists the same thing it lists in routed mode: the models your relay key’s policy grants from Relay’s catalog. The request itself still goes to the host your own credential belongs to.

What you get

Once a session runs through Relay:
  • Usage: every Claude Code request appears in GET /api/logs and 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-id and x-claude-code-parent-agent-id headers, plus x-claude-code-request-class, -agent-type, -compaction and -context-compacted when CLAUDE_CODE_GATEWAY_HINT_HEADERS=1, are recorded on the usage event as extras, alongside client=claude-code. That is what lets you group and cost a whole session, or one subagent inside it.
See Usage for reading it back and Payload logging for capture. On the routed path, Relay emits the Anthropic shape’s own 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.
Keep your relay key out of shared dotfiles and version control; treat the wrapper script or settings.json as secret-bearing. Payload capture records prompt and completion text, so scope it to the policies or keys you mean to debug.

Notes

  • Setting ANTHROPIC_BASE_URL disables Claude Code’s MCP tool search by default; re-enable it with ENABLE_TOOL_SEARCH=true if 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-After and 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 --tools set or --bare shrinks the prompt if a slow machine is all you have.
  • Cross-shape prompt caching. When Relay translates to a non-Anthropic upstream, the system array is flattened into one instruction block, and cache_control markers 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.
  • /context before the first turn. Relay answers count_tokens exactly only where the upstream has a counting endpoint (Anthropic); elsewhere the figure is calibrated from earlier turns of the session or estimated, and the X-WR-Token-Count response header says which.