---
title: Codex CLI
description: A provider in config.toml, the key from an environment variable — Codex CLI runs through the gateway without changing a single habit of yours.
keywords: codex cli, config.toml, model_provider, base_url, env_key, CODEX_HOME
group: integrations
---

## Quick {#quick keywords="config.toml, provider, key"}

Codex CLI is an OpenAI-compatible client, so its base URL ends in `/v1`.

Add a provider to the configuration and select it. Do not put the key in the file — Codex reads it from an environment variable whose name you supply.

:::code-group
```toml title=config.toml
# ~/.codex/config.toml
model = "<model>"
model_provider = "kumo"

[model_providers.kumo]
name = "Kumo"
base_url = "https://api.kumorouter.com/v1"
env_key = "KUMO_API_KEY"
wire_api = "responses"
```
```bash title=Shell
# ~/.zshrc — the key lives here, not in config.toml
export KUMO_API_KEY="kumo_sk_..."

codex
```
:::

## Where the configuration lives {#config keywords="~/.codex, CODEX_HOME, wire_api"}

The file is `~/.codex/config.toml`. Create it if it is missing. If the `CODEX_HOME` environment variable is set, the configuration lives there instead of in your home directory.

The file may hold several providers. The `[model_providers.kumo]` block is **added** to whatever is already there; `model_provider = "kumo"` only decides which of them is used by default.

- `base_url` — the gateway address, `https://api.kumorouter.com/v1`, including the `/v1`.
- `env_key` — the name of the environment variable Codex takes the key from. Not the key itself.
- `wire_api` — the request shape; `responses` selects the Responses protocol, the only one the current Codex CLI reference admits.
- `model` — an identifier from the catalog, `<model>` in the example above.

Key names differ between releases. If yours names them differently, follow the docs of the release you have installed — the meaning of the fields is the same.

:::note
Older Codex releases also accepted `wire_api = "chat"`. Follow the reference of the release you have installed: if it still spells `chat`, the gateway answers on that too — both protocols are live on Kumo.
:::

## Verify {#verify keywords="verify, curl, codex exec"}

```bash title=Verify
# 1. The gateway answers and the key is alive
curl https://api.kumorouter.com/v1/responses \
  -H "Authorization: Bearer $KUMO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model>",
    "max_output_tokens": 64,
    "input": [
      {
        "type": "message",
        "role": "user",
        "content": [{ "type": "input_text", "text": "reply with OK" }]
      }
    ]
  }'

# 2. The client goes to the same place — new terminal, empty directory
codex exec --skip-git-repo-check "reply with OK"
```

Do the second step in an empty directory: Codex then has nothing to read and nothing to edit, and you are testing the connection alone. That is exactly why `--skip-git-repo-check` is here: outside a Git repository Codex stops with `Not inside a trusted directory and --skip-git-repo-check was not specified` and never reaches the gateway. Either keep the flag, or run the check from a Git repository you trust.

## When it does not work {#troubleshooting keywords="401, 404, environment variable, model"}

- **401** — the key never arrived. The variable named in `env_key` is not exported in the shell you launch Codex from; or the key is revoked.
- **404** — the address. `base_url` is missing the `/v1`, or carries an extra path segment.
- **Model not found** — the identifier is not from the catalog. Ask the gateway for the list at `https://api.kumorouter.com/v1/models`.
- **The old provider is still used** — `model_provider` was not switched, or a profile or a command-line flag overrides it.

> [How a model is named →](/en/models) [Every status the gateway returns →](/en/errors)

:::cards
- [Claude Code](/en/claude-code) — the same thing on the Anthropic protocol.
- [Other tools](/en/other-tools) — Aider, Zed, OpenClaw.
- [Every recipe](/en/integrations) — the overview and the agent prompt.
:::
