Model Routing
When Codex asks for a model, router.ts resolves it to exactly one configured provider. The rules are
checked in order; the first match wins.
For OpenAI, a configured <selector>/gpt-* id maps through codexAccountNamespaces to exactly one
stored Codex account before combo or provider namespaces are considered. Bare gpt-* ids select
the canonical openai provider instead. Its codexAccountMode chooses Pool (default, main plus
added accounts) or Direct (current caller/main bearer) without changing the model id.
openai-apikey/<model> explicitly selects API-key transport. These credential routes do not fall
through to one another.
Precedence
Section titled “Precedence”-
Exact Codex account selector — if the id is
<selector>/<native-openai-model>and the selector is configured incodexAccountNamespaces, the request uses only the mapped stored account and sends the bare native model upstream. Unavailable exact targets fail closed instead of continuing through Pool, Direct, or provider routing.side/gpt-5.6-sol → provider "openai", model "gpt-5.6-sol", account selector "side" -
Combo id or alias — while at least one combo is configured, a canonical
combo/<id>or configured combo alias selects its concrete target before provider namespaces are checked. With no configured combos, a legacy physical provider literally namedcomboremains a normal provider namespace. See Combos for target selection and failover behavior. -
Explicit
provider/model— if the id contains/and the part before it is the name of a configured provider, that provider is used and the id is stripped to the part after the slash.anthropic/claude-opus-5 → provider "anthropic", model "claude-opus-5"ollama-cloud/glm-5.2 → provider "ollama-cloud", model "glm-5.2"openrouter/openai/gpt-5.6-sol → provider "openrouter", model "openai/gpt-5.6-sol"This is the explicit routed-provider form, and the one Codex’s model picker uses for routed models. If the same public id is a configured combo alias, rule 2 wins. If the named provider is disabled, this explicit form throws instead of routing.
-
Bare native OpenAI-family id — an id such as
gpt-*,o1-*,o3-*, oro4-*uses the canonical enabledopenaiprovider and its configured Pool or Direct account mode. -
A provider’s
defaultModel— if any provider’sdefaultModelequals the id, that provider is used (id passed through unchanged). -
Built-in prefix patterns — the id is matched against known model-family prefixes, then routed to a configured provider of that name (or name-prefix):
Prefixes Provider claude-,claude-sonnet-,claude-opus-,claude-haiku-anthropicllama-,mixtral-,gemma-groqThis matcher is name-based and, unlike the
defaultModel/models[]scans, currently does not filter a matching provider whosedisabledflag is true. -
A provider’s
models[]— if no prefix rule won and an active provider lists the id in itsmodels[], that provider is used. Rule 4 already sends a baregpt-*id to the canonical enabledopenaiprovider before another provider’smodels[]claim can match. -
Default provider — if nothing matched, the id is sent to
config.defaultProviderunchanged. (If no default provider is configured, or it is disabled, routing throws.)
API keys and environment variables
Section titled “API keys and environment variables”Whatever route is chosen, the provider’s apiKey is resolved through resolveEnvValue(): a value of
${OPENAI_API_KEY} or $OPENAI_API_KEY is expanded from the environment at request time, so secrets
never need to live in config.json.
Catalog visibility and context caps
Section titled “Catalog visibility and context caps”Routing and catalog visibility are separate controls:
disabledModelshides namespaced routed ids from the Codex catalog and/v1/models; a bare native GPT slug is kept in the catalog withvisibility: "hide". It does not reject a direct request for that model.- A provider’s non-empty
selectedModelsis another catalog allowlist. Live discovery and direct routing still work; only catalog and/v1/modelsemission are narrowed. - Fresh installs set
modelDiscovery.newModelPolicyto"off". After the first successful live fetch establishes a baseline, later arrivals are appended todisabledModelsand carry a NEW dashboard badge until enabled or acknowledged. Existing installs remain"on"until opted in. Useocx models new-policy offglobally, add--provider <name>for an override, and inspectocx models new-arrivals [--json]. Failed/degraded fetches never change the baseline. Providers with a non-emptyselectedModels(including preset mode) are already curated, so this policy is deliberately inert for them. provider.disabled: trueremoves that provider from catalog discovery. Explicitprovider/modelrequests fail, anddefaultModel/models[]scans skip it.providerContextCapsapplies per-provider Codex-visible context caps.contextCapValueis the dashboard default (350,000 by default), but it does nothing by itself until a provider is present inproviderContextCaps. Changing the dashboard value updates every enabled cap only when “apply to every routed provider” is toggled on; otherwise each provider keeps its own cap. Ordinary known windows can only be lowered; native models that support a longer window can expand up to their own supported ceiling. Caps never change the upstream model’s actual limit. Switching a cap off retains its selection inproviderContextCapValues, including after reload; switching it on restores that selection. A remembered selection never applies a limit while disabled. Sending{ "setAll": true }withoutvalueenables all configured providers at the current global value and replaces their remembered selections.
{ "contextCapValue": 350000, "providerContextCaps": { "anthropic": 350000, "cursor": 350000 }}- Target a Codex account explicitly with
<selector>/<native-openai-model>(rule 1). That route is exact and fails closed; it never silently switches to another account. - Be explicit for routed models. Prefer
provider/model(rule 3) when that exact public id is not a combo alias. It directly names the provider and matches what Codex shows in its picker after a catalog sync. - Seed
models[]ordefaultModelon a provider so short ids (rules 5/7) resolve without theprovider/prefix. - Prefix patterns are a convenience, not a guarantee: they only resolve if a provider with that
name (e.g.
anthropicorgroq) is actually configured.
See Configuration for the provider fields these rules read.

