Add a Custom Model to Codex — user-models.json & Generic Providers
Custom models in Codex mean adding your own endpoints to the Codex picker through Codex Router’s custom provider and generic providers. Every other provider owns one address; custom owns none — each model names its own endpoint, its own auth, and its own metadata.
What the custom provider is
One custom provider can hold a free community endpoint, a friend’s self-hosted server, and a paid API key at once. A model that needs a key says so on its own row. The provider is never selected for you and never part of the default set, because what it holds is whatever somebody put in it.
Enable it — this costs nothing and asks for nothing:
./bin/model-router codex providers enable custom
On Windows use ./model-router.ps1 codex with the same commands.
The no-credential allowlist boundary
An endpoint reached with no credential is the one thing a registry fragment cannot introduce on its own. Its address has to be allowlisted in src/model-registry.mjs, exactly as an anonymous provider’s is. Otherwise adding a JSON file under config/custom/ would be enough to send your prompts to any host on the internet with nothing to authenticate them.
An endpoint that carries a key, or one that stays on loopback, needs no allowlist entry — the key or the address is already the boundary. That is the deliberate line between “convenient” and “unsafe”.
Control Center now supports this flow directly: Models → Custom → Add endpoint. Enter a name, base URL, Chat Completions or Responses protocol, and key. Saving checks GET /models; its chip then lets you add discovered models or a private ID By name, edit the endpoint, remove a model, or remove the endpoint with its key and models.
Each user-models.json entry can now select its own endpoint.protocol: openai for Chat Completions (the default), anthropic for Messages, or openai-responses for Responses. The protocol and credential belong to the individual model rather than the custom container. If supportedEndpoints is present, it must include /chat/completions or /responses for the selected OpenAI protocol; Messages models do not declare OpenAI supportedEndpoints.
An explicitly configured endpoint keeps its own destination even if a built-in preset uses the same upstream model ID; it is not silently redirected to the preset’s public cloud endpoint.
An optional per-model goalContinuationGuard flag stops automatic goal continuation locally after three short, substantially repeated completed goal replies without tool activity. It is off by default; fresh instructions, tool activity and objective changes reset the check.
Another opt-in per-model setting, repetitionGuard: true, stops sustained repeated prose on GLM-5.3 without replaying the provider request. JSON fixtures, code, tools and reasoning are excluded from its heuristic; it is off by default.
The per-model reasoningTagPolicy controls cleanup of reasoning-like text in visible answers. Unknown routes now default to preserve, keeping literal XML examples and quoted <think> tags intact. Use legacy-inline only when you have confirmed that a route leaks inline reasoning tags; hy4-nonce is limited to verified Hy4 Preview routes. The known Qwen Plan Flash route and Hy4 Preview retain their route-specific compatibility behavior, while explicit preserve overrides it.
Generic providers
Generic providers let you register an OpenAI-compatible service as a provider without waiting for a registry entry. For example, a Perplexity search sidecar:
./bin/model-router codex providers generic add perplexity-search \
--name "Perplexity Search" \
--base-url https://api.perplexity.ai \
--adapter openai-chat
./bin/model-router codex providers generic credential perplexity-search set
Generic Responses providers may opt into pooled upstream WebSockets with --adapter openai-responses --transport websocket; native traffic remains on HTTP unless MODEL_ROUTER_NATIVE_TRANSPORT=websocket is set. A failure before sending can fall back to HTTP, but an ambiguous in-flight turn is never replayed.
The credential command never accepts the key as an argument; it prompts for it. The descriptor, credential reference and any per-model binding are private, atomic state.
Chat models and tool APIs
The Models picker adds conversational models; choosing one changes the assistant in Codex. A tool API needs its own client. For example, connecting OpenRouter makes the hidden openrouter-decisions/jev-latest route available at the local /v1/decisions endpoint, but a Decisions client must be configured to call it. Adding OpenRouter’s typesafe/jev-router chat model to the picker neither activates that hidden route nor installs a Codex tool-output pruning hook. If setup leaves the Router offline, use Status → Service health → Fix in Control Center, check its health, and return to a known working chat model. Fix repairs and restarts the installed Router and may interrupt routed chats; it does not enable a Decisions client.
Search sidecars
For a routed model with no verified search, Codex can use an explicit search sidecar. The binding names one exact routed model and one provider, and is refused for a model that already owns a search capability:
./bin/model-router codex search-sidecar set PROVIDER/MODEL perplexity-search
./bin/model-router codex search-sidecar status PROVIDER/MODEL
Fully quit and reopen Codex after changing a binding so its model catalog refreshes. Removing the generic provider also removes its credential and every dependent sidecar binding.
Curation and verification commands
Discovery, curation and verification build on the same protected user-model state:
| Command | Purpose |
|---|---|
./bin/discover-models <provider> | List what a provider’s live catalog exposes |
./bin/curate-models <provider> | Select models and metadata to publish |
./bin/test-model 'PROVIDER/MODEL' --live --yes | Live, quota-consuming check |
./bin/refresh-catalog | Rebuild the merged catalog Codex reads |
The live check now marks streaming as compatible only when the expected marker appears in framed assistant output and the response reports a successful completion. An echoed prompt, quoted completion word, reasoning-only text or a failed terminal event does not pass the streaming check.
Curated entries are stored in user-models.json, survive updates, stay editable in place, and are removable by re-running curation and deselecting.
Interactive curation now honors an explicit “no image” choice even if the provider catalog advertises image input. For discovered models, the router uses the smallest advertised serving context limit rather than a larger upstream capability figure. Discovery and quota reads use the configured API-key pool; an unusable pool does not silently fall back to a different legacy account.
An example free custom model
The registry ships one free custom example: Qwen3.8-27-free-victor on a shared Hugging Face Inference Endpoint. It offers a 262,144-token context, image input, tool calling and roughly 30 requests per minute per IP. Its owner says it will be retired once launch interest fades, so treat it as a model to try, not one to depend on.
The endpoint carries no credential. Use these free routes at your own risk: nobody has agreed to serve you, access is a published exception rather than an entitlement, and it can be narrowed, rate-limited or withdrawn without notice.
FAQ
What is the custom provider in Codex Router? The custom provider owns no fixed address. Each of its models names its own endpoint, auth and metadata, so one provider can hold a free community endpoint, a self-hosted server and a paid API key at once.
Why do some custom endpoints need an allowlist? An endpoint reached with no credential must be allowlisted in src/model-registry.mjs. Otherwise adding a JSON file could send prompts to any host with nothing to authenticate them. A key-carrying or loopback endpoint needs no allowlist entry.
How do I verify a custom model? Use ./bin/test-model ‘PROVIDER/MODEL’ —live —yes for a live, quota-consuming check, then fully quit and reopen Codex so the catalog refreshes.
Related: models overview, installation, how it works, picker troubleshooting.