Before you start
$ export ANTHROPIC_BASE_URL=http://localhost:20128 # ← our gateway Base URL
$ export ANTHROPIC_AUTH_TOKEN=omniroute-********** # ← our key (masked)
$
$ curl -X POST "$ANTHROPIC_BASE_URL/v1/messages" \
-H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-sonnet-4-6","max_tokens":1,"messages":[{"role":"user","content":"hi"}]}'
→ HTTP 200
{"id":"chatcmpl-resp_6aa6b3…","type":"message","role":"assistant",
"model":"muse-spark-1.2-contributor-free", ← router picked the serving model
"content":[{"type":"text","text":"routed ok"}],"stop_reason":"end_turn"}401curl fails using your token, or Claude Code says “not authorized”▶
Which credential variable did you set? ANTHROPIC_AUTH_TOKEN sends Authorization: Bearer; ANTHROPIC_API_KEY sends x-api-key. A credential in the wrong variable reaches OmniRoute in a header it doesn’t read.
Switch to the other variable (or use x-api-key: $ANTHROPIC_API_KEY in the curl command) and retry. Verify the token string itself was copied completely — no line breaks, no trailing space.
NETConnection refused / “Can’t reach the API server” (ENOTFOUND)▶
Nothing is answering at the Base URL: the address is wrong, or a VPN/firewall blocks the path to the gateway. The curl test fails immediately with the same cause.
Confirm the exact Base URL from the OmniRoute dashboard (look for typos, http vs https, trailing slash) and confirm your network can reach it — try in your browser first.
200API returned an empty or malformed response (HTTP 200)▶
The gateway or an intermediate proxy answered with a non-API response — often an HTML page or a login screen, served with status 200.
Run the curl test and inspect the raw body. Fix the route that answers with something other than a Claude API response (wrong path, proxy in the way, auth wall).
LOGINClaude Code asks you to log in even though curl succeeds▶
The CLI has no credential of its own. A reachable Base URL alone isn’t a credential — and a project-level env block applies only after the first-run wizard and trust prompt.
Set ANTHROPIC_AUTH_TOKEN somewhere Claude Code reads before first-run setup: a shell export, or the env block in ~/.claude/settings.json. Then relaunch.
403“403 Forbidden” — OmniRoute’s own logs show no request received▶
A web application firewall or reverse proxy in front of the gateway blocked the request body before it reached the gateway. Claude Code prompts contain XML-style tags and source code that match cross-site-scripting body rules, so a short curl passes while a real session doesn’t.
Exempt the gateway’s /v1/messages path from request-body inspection (e.g. AWS WAF’s CrossSiteScripting_Body managed rule, or the equivalent OWASP CRS rules on nginx/ModSecurity). This is an OmniRoute-side/hosting change.
TLSCertificate / TLS errors, but curl succeeds▶
Claude Code’s runtime isn’t trusting the same certificate authority curl uses — common behind corporate TLS-inspection proxies.
Set NODE_EXTRA_CA_CERTS to your CA bundle path and restart Claude Code. See the CA certificate store section of the Claude Code docs.
415curl returns HTTP 415 “Content-Type must be application/json”▶
curl’s -d flag defaults to application/x-www-form-urlencoded; this gateway requires JSON. (Observed live on an OmniRoute gateway, 2026-09-13.)
Add -H "Content-Type: application/json" to the curl command and retry. The fixed command in Module 03 includes it.
MODELThe model in the response differs from the one you requested▶
The router redirected your request to a contributing/serving model (observed live: claude-sonnet-4-6 request answered by muse-spark-1.2-contributor-free). This is routing working as designed, not an error.
No fix needed. Verify against the routing intent in your OmniRoute dashboard; change the model/route you request if you want a specific target.
MODELModel name not found / route unknown▶
The model string you’re requesting isn’t one OmniRoute serves from that credential, or model discovery isn’t enabled so the picker shows only built-ins.
Use the exact model/route id from your dashboard; enable CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 and check the picker for “From gateway” entries.
HEADERA custom routing header isn’t being applied▶
ANTHROPIC_CUSTOM_HEADERS formatting: one Name: Value per line; inside JSON, pairs separated by \n. Routing/tenant headers count as headers that need approval in the approval dialog.
Verify the header string exactly (spell the header name as OmniRoute expects), relaunch, and approve the header when prompted.