Responses API
/v1/responsesThe OpenAI Responses API — the path Codex CLI is configured against.
Request
modelstringbodyrequiredinputstring|arraybodyrequiredstreambooleanbodymax_output_tokensintegerbodyResponses
Response
{
"id": "resp_…",
"object": "response",
"status": "completed",
"model": "gpt-5.6-sol",
"output": [
{ "type": "message", "role": "assistant",
"content": [{ "type": "output_text", "text": "Hello!" }] }
],
"usage": { "input_tokens": 9, "output_tokens": 12, "cost_usd": "0.0000465" }
}Details
Needs a key in Authorization: Bearer or x-api-key.
Forwarded to an OpenAI-compatible upstream exactly like chat: only the fields the gateway itself reads are listed, and the rest of the body (tools, reasoning, instructions, text and so on) goes through untouched. It gets its own entry because this is the route Codex CLI is pointed at: wire_api = "responses" in ~/.codex/config.toml.
This request's cost arrives inside the answer: the x-teamtoken-cost-usd header and usage.cost_usd in the body. The value is a decimal string ("0.0000465"), not a number: a number would be re-displayed by the client language's own float rules (Python would show 4.65e-05), while a string reaches your code exactly as written. The cost can also fail to arrive at all — then it is in neither the header nor the field: on a non-streaming answer when the upstream did not report it, on a stream when the model has no catalog tariff. Streams here use named SSE events. The gateway tries to write the cost into the usage-bearing frame (the Responses API nests it in response.completed), but it only rewrites a frame that starts with data: — a frame with its own event: line is relayed untouched, and then no cost arrives in the stream. On a non-streaming answer the cost comes both in the header and in the field.
A byte-identical request with the same key inside a short TTL (60 s by default) does not reach the model twice — the gateway returns the first answer's body and charges nothing for the second. Only a successful answer of at most 256 KB is cached; anything else goes upstream again. A replay from the cache carries no x-teamtoken-cost-usd header (nothing was charged), and the cost_usd in its body belongs to the first, paid answer. Hence the corollary: an already-paid answer is served even on an empty wallet — the balance check sits AFTER idempotency. Every error arrives in one envelope (the code field is not on every status): { "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }.
Moderation, when it is enabled, reads the request text and blocks only on a real verdict: a moderator that is down or erroring lets the request through. The answer always carries back the logical model name you asked for. If it does not fit the gateway's wait (300 s read by default, 60 s without bytes on a stream) you get a 504; the gateway marks that non-streaming attempt, and if the upstream finished and billed anyway, the reconciler credits the amount back to your balance as a compensating grant. Upstream host names are scrubbed out of error bodies, so an error's text can differ from what the upstream sent.
This path has no route of its own: one gateway handler takes every POST /v1/*, and its list of accepted paths is closed — any other /v1/* answers 404.