Codex CLI
Codex CLI talks to model providers over the OpenAI Responses wire, which Sator serves at POST /v1/responses — stateless, which is exactly how Codex uses it: it sends store: false and the whole conversation on every turn. State: verified against Codex CLI 0.149.
Install#
npm install -g @openai/codexConfigure#
Add a provider and make it the default in ~/.codex/config.toml:
model = "muse-spark-1.2"
model_provider = "sator"
web_search = "disabled"
[model_providers.sator]
name = "Sator"
base_url = "https://sator-api.princep.org/v1"
env_key = "SATOR_API_KEY"
wire_api = "responses"Then export the key in the shell Codex runs from:
export SATOR_API_KEY=sk-sator-v1-...wire_api must be "responses"; it is the only value Codex accepts. web_search = "disabled" keeps Codex from sending OpenAI's built-in web-search tool, which no model here can run — on a model that is not Responses-native the request is refused rather than the tool silently dropped. The key stays in the environment — env_key names the variable, and nothing secret goes into the file. Pick any model id from Models; Codex does not know Sator's ids, so model must be set explicitly, and /model inside a session switches between ids you type, not a list.
To keep Sator beside another provider, put model and model_provider under a profile instead and start Codex with --profile sator:
[profiles.sator]
model = "muse-spark-1.2"
model_provider = "sator"Verify#
codex exec "Reply with the single word pong."A working setup prints pong and the request appears on your dashboard with the model you named.
Troubleshooting#
401on the first request —SATOR_API_KEYis not exported in the shell Codex was started from, orenv_keynames a different variable.404on the first request —base_urlis missing its/v1.400 invalid_bodynamingtools of type web_search—web_search = "disabled"is missing fromconfig.toml; Codex sends the tool by default.400 unsupported_parameter— the request namedprevious_response_id; Codex does not send it over HTTP, so this is another client on the same provider entry. See Responses.- Reasoning is not shown — Codex asks for a reasoning summary with
model_reasoning_summary; models that are not Responses-native return a summary only when one is asked for. Setmodel_reasoning_summary = "auto". - Answers stop short — Codex caps output at its own
model_max_output_tokens; a value above the model's max output is lowered to it, never rejected.