Skip to content
Router One

Codex CLI and the Responses API: why most relays 404

OpenAI's Codex CLI doesn't call the classic Chat Completions API — it speaks the newer Responses API wire format. Most OpenAI-compatible relays only implement Chat Completions, so Codex pointed at them fails with 404s or malformed-request errors even though the same key works in curl. Router One implements the Responses wire format natively (wire_api = "responses"), so Codex works with a two-minute config change.

The one-line root cause

Codex sends requests to /responses-style routes with a request schema that Chat Completions servers don't recognize. A relay that only speaks Chat Completions answers with 404 (unknown path) or 400 (unknown fields). No amount of key or model fiddling fixes a wire-format mismatch — the gateway itself has to support it.

The working configuration

Point Codex at Router One in ~/.codex/config.toml and export your key in the same shell that runs codex:

config.toml
# ~/.codex/config.toml
model_provider = "router"

[model_providers.router]
name = "router"
base_url = "https://api.router.one/v1"
env_key = "ROUTER_ONE_API_KEY"
wire_api = "responses"

# in your shell:
# export ROUTER_ONE_API_KEY=sk-your-router-one-key

Error checklist

If Codex still fails after the config change:

SymptomCauseFix
404 on every requestbase_url points at a relay without Responses API support, or has a typoSet base_url to https://api.router.one/v1 exactly and keep wire_api = "responses".
401 unauthorizedROUTER_ONE_API_KEY isn't visible in the shell running codexExport the key in the same session (or your shell profile), then restart the terminal and re-run codex.
Model not foundModel ID doesn't match the catalogCopy the exact ID from the /models page — IDs are case-sensitive.
402 insufficient balanceWallet or key spend cap exhaustedTop up or raise the key's maxSpend in Dashboard → API Keys.
Config seems ignoredCodex reads a different config file than the one you editedConfirm the file lives at ~/.codex/config.toml (Windows: %USERPROFILE%\.codex\config.toml).
400 mentioning background or image_generationCodex asked for a background response or the image_generation tool — the two Responses features the gateway does not runDisable background mode; generate images through POST /v1/images/generations instead. Hosted tools and previous_response_id keep working on natively served Responses models.
400 saying the model must be called via /v1/messagesA Claude-family model ID was sent to the Responses endpoint; Claude is served on the Anthropic-compatible endpoint and on /v1/chat/completions, never on /v1/responsesSwitch Codex to a model whose detail page lists POST /v1/responses (the GPT family or DeepSeek V4). To work with Claude, use Claude Code instead.
Model not found for codex-auto-reviewCodex CLI sends the model ID codex-auto-review for its review line (/review and automatic review) unless review_model in config.toml overrides it; relays that only know their own model list, or only speak Chat Completions, reject the IDNothing to change on Router One — codex-auto-review is in the catalog and is served on /v1/responses and /v1/chat/completions; pricing is on /models/codex-auto-review. To review with a different model, set review_model in ~/.codex/config.toml.

What the Responses endpoint accepts — and the two things it rejects

Router One implements the Responses wire format itself, for the models whose detail page lists POST /v1/responses (the GPT family and DeepSeek V4). The table lists current behavior by request feature; anything accepted is forwarded as sent and billed at the model's standard rate.

Request featureStatusNotes
Function tools (Codex's default tool shape)AcceptedStructured tool calls come back as first-class Responses output items.
Custom tools (Codex's freeform apply_patch and similar)Accepted on natively served Responses modelsUse a model whose page lists POST /v1/responses; other models get 400 invalid_request.
previous_response_id / conversation / prompt (server-side context references)Accepted on natively served Responses modelsSame model rule as custom tools; the reference is resolved on the native path.
Hosted tools: file_search, code_interpreter, computer_use, mcp, web_searchAccepted on natively served Responses modelsForwarded as sent and billed at the model's standard rate — Router One runs none of them itself.
input_file parts with file_id or file_urlAccepted on natively served Responses modelsSame model rule; the file reference travels with the request.
service_tier (any value)AcceptedForwarded as sent; billed at the model's standard rate regardless of tier.
stream / instructions / temperature / max_output_tokensAcceptedForwarded as sent; see the Responses API reference.
image_generation tool and image_generation_call input itemsRejected — 400 invalid_requestImage generation is not offered through the Responses endpoint; use POST /v1/images/generations (see /image-generation-api).
background: trueRejected — 400 invalid_requestNo background execution and no GET /v1/responses/{id} to fetch a result later; each request completes within its HTTP connection.

Verify from the trace

Once requests reach the gateway, every Codex call shows up in Dashboard → Logs with model, tokens, cost, and status. If the log stays empty while Codex errors, the failure is still local — config path or env var. If entries appear with 4xx, the trace names the exact limit.

FAQ

Why does my key work in curl but not in Codex?

curl exercises the Chat Completions surface; Codex uses the Responses API wire format. A relay can pass the curl test and still 404 for Codex. The gateway must implement the Responses wire format — Router One does.

What does wire_api = "responses" actually do?

It tells Codex which protocol shape to speak with the provider block. With "responses", Codex sends Responses-API-formatted requests; the endpoint on the other side has to understand them natively.

Does the base URL need /v1?

Yes — for Codex the OpenAI-compatible base URL is https://api.router.one/v1 including /v1. That's the opposite of Claude Code, whose Anthropic-compatible base URL has no /v1 suffix.

Which models can Codex use through the gateway?

The models served natively over the Responses wire format: the GPT family and DeepSeek V4 (deepseek-v4-pro, deepseek-v4-flash). Use the exact model ID from /models — each model page lists whether it exposes POST /v1/responses. Claude models are not on this endpoint: sending one gets a 400 back before any model is called.

I pointed Codex at a Claude model and got a 400. What now?

The gateway refuses that request before any model runs, and the message spells out where the model lives: model 'anthropic/claude-opus-5' must be called via /v1/messages or /v1/chat/completions. Claude is served on the Anthropic-compatible endpoint (the path Claude Code uses) and on /v1/chat/completions, not on /v1/responses. Point Codex at a GPT-family model or DeepSeek V4, and use Claude Code when you want to work with Claude — see /claude-code-china.

What is the codex-auto-review model Codex keeps requesting?

It is the model ID Codex CLI sends for its review line (/review and automatic review) unless review_model in ~/.codex/config.toml points elsewhere. Relays that validate model names against their own list, or only implement Chat Completions, answer model not found. Router One lists codex-auto-review in the catalog, so the request is routed and billed like any other model — it works on /v1/responses and /v1/chat/completions, and the current price is on /models/codex-auto-review.

Do previous_response_id, hosted tools and service_tier work through the gateway?

Yes, on models served natively over the Responses wire format — the model page's endpoint list shows which. Requests are forwarded as sent and billed at the model's standard rate; only background: true and the image_generation tool are rejected with 400.

Why does Codex get 400 when it tries image generation?

Image generation is not offered through the Responses endpoint, so an image_generation tool or image_generation_call item is refused with 400 invalid_request before any model is called. Generate images with POST /v1/images/generations instead — see /image-generation-api.

Can I run a response in the background?

No. background must be omitted or false; background: true returns 400 invalid_request. The gateway does not run detached responses and has no GET /v1/responses/{id}, so a result cannot be fetched after the connection closes.

Does this work from Mainland China?

Yes. The gateway is reachable from Mainland China without a VPN; the config.toml above is identical everywhere.