Codex 怎么配置自定义 API endpoint
简答:自定义 API endpoint 写在用户级 config.toml,不要写进仓库。网关自己发 Key 时用独立 provider:base_url 停在 /v1,wire_api 用 responses 或省略,Key 只放进 env_key。仍弹登录、401 或 404 时,先看 provider 是否仍走官方登录,变量是否进入启动进程,以及 URL 是否为 /v1/responses。
定义:Codex 自定义 API endpoint 是把模型请求改到指定的 Responses 兼容地址,相关字段写在用户级 config.toml 的 provider 上,而不是项目配置。
先贴最小配置,再选定一条路线
- base_url 停在
/v1,不要自己再加/responses。 - wire_api 只写
responses,或整行省略;不要写chat。 - Key 只放在
env_key指向的环境变量里,不写进文件或仓库。
网关自己发 Key 时,用下面这份独立 provider。代理仍要 ChatGPT 登录或官方 API Key 时,不要套这份,改用后文的 openai_base_url。两条同时写,请求和凭证会对不上。
先运行 codex --version。截至 2026-10-21,Codex 模型说明写明现行版本不支持 wire_api = "chat",也不支持只有 Chat Completions 的端点。模型名保存前从网关当前列表,或该版本认识的目录里复制,不要保留尖括号。
model = "<MODEL_ID>"
model_provider = "my-gateway"
[model_providers.my-gateway]
name = "My gateway"
base_url = "https://gateway.example.com/v1"
env_key = "CODEX_GATEWAY_API_KEY"
wire_api = "responses"model 和 model_provider 必须写在第一个表头之前。官方网关接入说明写明,写到表头后面的键属于该表,不会被当成顶层设置。my-gateway 在 model_provider、表名,以及若使用时的 auth 表名里必须相同。标识不能用保留名 openai、ollama、lmstudio,高级配置写明这三个内置标识不能被自定义表覆盖。
| 路线 | 谁收请求 | 谁发凭证 | 什么时候用 |
|---|---|---|---|
| openai_base_url | 内置 openai,只换地址 | ChatGPT 登录或官方 API Key | 代理仍要官方认证 |
| 独立 provider 加 env_key | 你的网关 | 网关 Key,来自环境变量 | 自定义 endpoint 用这条 |
| 独立 provider 加 auth 命令 | 你的网关 | 本机命令打印的短期 token | 管理员已安装凭证助手 |
只改内置 OpenAI 地址时,用户级文件里写这一行即可,不要再定义同名 provider:
openai_base_url = "https://proxy.example.com/v1"高级配置把 openai_base_url 用于内置 openai。它和上面的独立 provider 是两条路线,不要为了同一个 endpoint 各写一遍。
配置写在用户目录,改完重启客户端
结论:provider 和 endpoint 写在用户目录,或用户目录里的 profile 文件。写进仓库的 .codex/config.toml 不会改变 endpoint。
macOS 与 Linux 的用户级文件是 ~/.codex/config.toml。Windows 是 %USERPROFILE%\.codex\config.toml。官方网关接入说明写明,WSL 里用 Linux 家目录,除非 CODEX_HOME 指向别处。目录不存在就先建目录。
截至 2026-10-21,配置基础说明把取值优先级写成下面这个顺序,越靠前越优先:
- 当次 CLI 标志和
--config。 - 已信任项目里的
.codex/config.toml,离当前目录越近越优先。 --profile选中的 profile 文件,路径是~/.codex/profile-name.config.toml。- 用户级
~/.codex/config.toml。 - 已登录工作区下发的云端托管默认值。
- 系统配置(Unix 上若存在,为
/etc/codex/config.toml)。 - 内置默认值。
同一页把项目层放在 --profile 之前。这只对项目层允许覆盖的键成立。endpoint 是例外:配置参考写明,项目级文件会忽略 openai_base_url、chatgpt_base_url、model_provider、model_providers 等键。所以自定义地址仍然只来自当次 --config、已选中的 profile 文件或用户级文件。项目文件里写了 base_url,看起来像更优先,实际不会生效。
- 运行
codex --version。0.134.0 及以后不要再把配置嵌进config.toml的[profiles.名字]。高级配置要求改成~/.codex/名字.config.toml,用codex --profile 名字选用,并删掉旧的 profile 表和顶层profile = "名字"。更早版本的旧表本文未展开。 - 把上一节字段合并进已有用户级文件。同一个键不要写第二遍。
- CLI 与桌面版读这份用户级文件。官方网关接入说明要求保存后重启应用。终端里 export 的变量,从桌面启动的进程不一定继承。
- 启动后在 CLI 执行
/status,确认模型名和 provider 标识是my-gateway。官方接入说明也把codex doctor和/status当作连接前检查。
当次试跑可以不改文件:codex --config model_provider='"my-gateway"'。高级配置写明 --config 的值按 TOML 解析,字符串要带引号。这只覆盖这一次,不能代替用户级文件。
base_url 停在 /v1,wire_api 只用 responses
结论:base_url 写成网关文档里的 API 根。官方示例是 https://gateway.example.com/v1,末尾不加斜杠,也不要自己再加 /responses。wire_api 写成 responses,或整行省略。配置参考写明,唯一支持的值就是 responses,省略时默认也是它。
网关兼容要求写明:base URL 为 https://gateway.example.com/v1 时,网关必须接受 POST /v1/responses,并保留客户端使用的请求和响应字段。能通的 Chat Completions 或 Anthropic Messages 接口,不能当成已经兼容 Codex。
先对照网关文档里的完整调用地址,再看访问日志里的最终 URL,不要猜路径:
- 文档给出的调用地址是
https://网关/v1/responses:base_url填到/v1为止。官方示例就是这样接上 Responses 路径的。 - 已经把
base_url写成带/responses或/chat/completions的地址:与示例不一致。改到/v1后,用网关访问日志核对最终 URL。 - 网关只有
/v1/chat/completions:改wire_api解决不了。模型说明写明现行版本不接受chat。 - 末尾多一个斜杠:先改成官方示例那种没有尾斜杠的写法再重试。本文不把尾斜杠对应的状态码写成普遍事实。
模型标识同样不要从旧文章抄。网关兼容要求写明:先看 codex --version,再用该版本认识的模型名,或让网关提供配套 catalog。模型自报姓名不能当作路由证据。截至 2026-10-21,Codex 模型说明写明 gpt-5.3-codex-spark 已于 2026-09-14 从 ChatGPT 桌面版、Codex CLI 和 IDE 扩展退役;gpt-5.5 自 2026-10-14 12:00 PDT 起从 ChatGPT 登录的 Codex 退役,API Key 会话不受影响。这两条只说明旧教程里的 ID 会过期。自定义网关仍以该网关当前列表为准。
认证只留一种,Key 不进仓库
结论:自定义网关的 Key 用 env_key。不要和 requires_openai_auth = true、auth 命令或内置登录态叠在一起。env_key 的值是变量名,不是密钥本身。
认证说明把自定义 provider 分成三种:requires_openai_auth = true 时走 OpenAI 认证,此时 Codex 忽略 env_key;只设 env_key 时读取该变量;两者都不设则视为不需要认证,适合本地服务。高级配置另有命令方式:[model_providers.my-gateway.auth] 的命令把 token 打到标准输出。该表不能和 env_key、experimental_bearer_token、requires_openai_auth 同时使用。
- 网关发独立 Key:只留
env_key = "CODEX_GATEWAY_API_KEY",不要写requires_openai_auth = true。写成 true 时,变量里的网关 Key 不会被使用,客户端仍去找官方登录态。 - 代理要原样转发官方登录:用
requires_openai_auth = true,或内置 openai 加openai_base_url,不要再设env_key。 - 短期 token:只留 auth 命令,路径用本机绝对路径。Codex 不负责安装这个程序。Windows 的 TOML 用单引号保留反斜杠。
- 本地免认证:两种都不设。服务怎么安装,本文不展开。
配置参考把 experimental_bearer_token 标为不推荐,应改用 env_key。密钥不要写进 config.toml、项目文件或仓库。认证说明写明,文件存储时 ~/.codex/auth.json 里是登录缓存;把它当成密码,不要贴到工单或聊天里。它不是 env_key 那个变量。
环境变量说明列出的稳定变量里没有 OPENAI_BASE_URL,也没有一个固定的网关 Key 变量名。变量名由你在 env_key 里指定。官方网关接入说明写明:只在终端里设置的变量,桌面启动的应用不一定看得到。桌面版要把 CODEX_GATEWAY_API_KEY 放到能随应用进程继承的用户环境,保存后重启应用。CLI 则在启动 codex 的那个 shell 里能读到即可。
网关若要的不是 Authorization: Bearer,而是例如 X-API-Key,就删掉 env_key,改成 env_http_headers = { "X-API-Key" = "CODEX_GATEWAY_API_KEY" }。等号右边仍是变量名。头名以网关文档为准,对不上时本文不替任何产品猜测。
三步验收,脚本保存成文件再跑
结论:GET /v1/models 只检查地址和 Key。网关兼容要求写明,健康检查和模型列表不构成一次 Codex 对话,也不能证明工具可用。按这个顺序验收,一次只看一个结果:
GET /v1/models返回 200。这只说明地址和 Key 能读到模型列表。POST /v1/responses的流里出现结束事件response.completed,而且事件是陆续到达的,不是最后一次性吐出整段。- 再测工具回路,并让网关管理员在日志里核对用户、模型别名和上游路由。模型自己说自己的名字不算数。
下面的脚本是可选核对,不是配置的一部分。保存成文件后执行,不要粘贴进交互式 shell:交互式里的 exit 会关掉当前终端。脚本结束前会先打印响应正文,临时文件随后由 trap 删除,所以不要等脚本退出再去 jq 那个文件。需要本机已有 curl 和 jq。不用 -v,避免把 Key 打出来。
在运行脚本的终端里先设置地址和模型,Key 由脚本用 read -s 读取,不要写进文件:
export CODEX_GATEWAY_BASE_URL="https://gateway.example.com/v1"
export CODEX_MODEL_ID="从模型列表复制的 ID"状态码脚本保存为 check-gateway.sh,执行 bash check-gateway.sh。它把正文写入文件再打印,用来看状态码和有没有 response.completed,不能用来看是不是边生成边到达。变量为空时会立刻停在 : 那一行,不会继续发请求。
#!/usr/bin/env bash
# 保存为 check-gateway.sh。不要粘贴进交互式 shell。
set -euo pipefail
: "${CODEX_GATEWAY_BASE_URL:?先设置 CODEX_GATEWAY_BASE_URL}"
: "${CODEX_MODEL_ID:?先设置 CODEX_MODEL_ID}"
if [ -z "${CODEX_GATEWAY_API_KEY:-}" ]; then
read -r -s -p "Gateway API key: " CODEX_GATEWAY_API_KEY
printf '\n'
export CODEX_GATEWAY_API_KEY
fi
base_url="${CODEX_GATEWAY_BASE_URL%/}"
model_id="${CODEX_MODEL_ID}"
models_file=$(mktemp)
responses_file=$(mktemp)
payload_file=$(mktemp)
trap 'rm -f "$models_file" "$responses_file" "$payload_file"' EXIT
models_code=$(curl -sS -o "$models_file" -w "%{http_code}" -H "Authorization: Bearer ${CODEX_GATEWAY_API_KEY}" -H "Accept: application/json" "${base_url}/models")
printf 'models_status %s\n' "$models_code"
printf '%s\n' '--- models body ---'
cat "$models_file"
printf '\n'
if [ "$models_code" != "200" ]; then
printf '%s\n' 'models request failed; responses request was not sent'
exit 1
fi
jq -n --arg model "$model_id" '{model:$model,input:"Reply with exactly: gateway-ok",stream:true}' > "$payload_file"
responses_code=$(curl -sS --no-buffer -o "$responses_file" -w "%{http_code}" -H "Authorization: Bearer ${CODEX_GATEWAY_API_KEY}" -H "Content-Type: application/json" -H "Accept: text/event-stream" --data-binary @"$payload_file" "${base_url}/responses")
printf 'responses_status %s\n' "$responses_code"
printf '%s\n' '--- responses body ---'
cat "$responses_file"
printf '\n'
if grep -q 'response.completed' "$responses_file"; then
printf '%s\n' 'found response.completed'
else
printf '%s\n' 'response.completed not found'
fi看增量流式时,另存 stream-gateway.sh。这条 curl 使用 -N,没有 -o,data: 行直接出现在终端。一次性出现整段、没有 response.completed,不算流式通过。
#!/usr/bin/env bash
# 保存为 stream-gateway.sh。输出直接打到终端。
set -euo pipefail
: "${CODEX_GATEWAY_BASE_URL:?先设置 CODEX_GATEWAY_BASE_URL}"
: "${CODEX_MODEL_ID:?先设置 CODEX_MODEL_ID}"
if [ -z "${CODEX_GATEWAY_API_KEY:-}" ]; then
read -r -s -p "Gateway API key: " CODEX_GATEWAY_API_KEY
printf '\n'
export CODEX_GATEWAY_API_KEY
fi
base_url="${CODEX_GATEWAY_BASE_URL%/}"
payload_file=$(mktemp)
trap 'rm -f "$payload_file"' EXIT
jq -n --arg model "$CODEX_MODEL_ID" '{model:$model,input:"Reply with exactly: gateway-ok",stream:true}' > "$payload_file"
curl -sS -N -H "Authorization: Bearer ${CODEX_GATEWAY_API_KEY}" -H "Content-Type: application/json" -H "Accept: text/event-stream" --data-binary @"$payload_file" "${base_url}/responses"Windows 先在同一个窗口设置这两个变量,再运行后面的脚本。任一为空时,脚本会在 TrimEnd 之前停止:
$env:CODEX_GATEWAY_BASE_URL = 'https://gateway.example.com/v1'
$env:CODEX_MODEL_ID = '从模型列表复制的 ID'把下一段保存为 check-gateway.ps1。Key 用 Read-Host -AsSecureString 读取,请求体先转成 UTF-8 字节,错误体按 UTF-8 读取,用过的非托管字符串会释放。两个请求放在 foreach 里,模型列表失败就 break。这段按 Windows PowerShell 5.1 的 WebException.Response 来写;PowerShell 7 的异常不一定有同名 Response,那时改看 $_.ErrorDetails.Message。本文没有在 5.1 或 7.x 上代跑。Invoke-WebRequest 会等正文结束后再打印,不能判断增量流式。
$bstr = [IntPtr]::Zero
$key = $null
if ([string]::IsNullOrWhiteSpace($env:CODEX_GATEWAY_BASE_URL)) {
throw '先设置 $env:CODEX_GATEWAY_BASE_URL'
}
if ([string]::IsNullOrWhiteSpace($env:CODEX_MODEL_ID)) {
throw '先设置 $env:CODEX_MODEL_ID'
}
$baseUrl = $env:CODEX_GATEWAY_BASE_URL.TrimEnd('/')
$modelId = $env:CODEX_MODEL_ID
$secure = Read-Host -AsSecureString -Prompt 'Gateway API key'
try {
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
$key = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
$headers = @{ Authorization = "Bearer $key" }
$json = (@{ model = $modelId; input = 'Reply with exactly: gateway-ok'; stream = $true } | ConvertTo-Json -Compress)
$bodyBytes = [Text.Encoding]::UTF8.GetBytes($json)
foreach ($step in @('models','responses')) {
try {
if ($step -eq 'models') {
$resp = Invoke-WebRequest -Uri ($baseUrl + '/models') -Headers $headers -Method Get -UseBasicParsing
} else {
$resp = Invoke-WebRequest -Uri ($baseUrl + '/responses') -Headers $headers -Method Post -Body $bodyBytes -ContentType 'application/json; charset=utf-8' -UseBasicParsing
}
Write-Output $resp.StatusCode
Write-Output $resp.Content
} catch {
$errResp = $_.Exception.Response
if ($null -eq $errResp) { throw }
Write-Output ([int]$errResp.StatusCode)
$reader = New-Object System.IO.StreamReader($errResp.GetResponseStream(), [Text.Encoding]::UTF8)
try { Write-Output $reader.ReadToEnd() } finally { $reader.Dispose() }
break
}
}
} finally {
if ($bstr -ne [IntPtr]::Zero) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
}
$key = $null
}Windows 上看增量流式,用 curl.exe -N,不要加 -o,输出直接留在终端。不要用 PowerShell 的 curl 别名,那个别名指向 Invoke-WebRequest。
两条请求都成功后,在 CLI 新开会话输入官方接入说明里的那句 Reply with exactly: gateway-ok,并用 /status 核对 provider。文本成功仍不够。网关兼容要求的工具回路是:Codex 收到 function call,在本地执行,再提交对应的 function_call_output,然后得到最终回答。自查时用一句无敏感数据的请求,例如让 Codex 在本地执行 printf gateway-ok,再看网关是否同时记下调用和结果。只出现一句回复,就还没验证工具。
流中断时,网关兼容要求先检查代理是否缓冲了 SSE,以及有没有把失败事件和 response.completed 原样转发。截至 2026-10-21,配置参考写明 request_max_retries 默认 4,stream_max_retries 默认 5,stream_idle_timeout_ms 默认 300000。先改缓冲,不要先把重试调大。
仍弹登录、401、404 时先改哪一处
按这个顺序查,一次只改一处,改完重启桌面版或重开 CLI。
| 看到的现象 | 先核对 | 改法 |
|---|---|---|
| 仍弹出 ChatGPT 登录 | model_provider 是否仍是默认 openai,或 requires_openai_auth 是否为 true | 独立 Key 只保留 env_key,并确认 /status 里的 provider 标识一致 |
| 401 或认证失败 | 启动进程里有没有 env_key 那个变量;有没有和 auth 命令或官方登录态叠用 | 变量设到实际启动 Codex 的环境;认证方式只留一种 |
| 404 | 网关日志里的最终 URL 是不是 /v1/responses | base_url 停在 /v1,不要自带 /responses 或 /chat/completions |
| 模型不存在 | model 是否从当前网关列表复制 | 换成该列表中的 ID;有 catalog 时与管理员核对别名路由 |
| 配置像没保存 | 字段是否写在项目 .codex/config.toml,或写在表头之后 | 移到用户级文件,顶层键放在第一个表头之前 |
| 流式中途停住 | 中间代理是否缓冲了 SSE | 先让 response.completed 增量到达,再谈重试次数 |
401 时不要打开 auth.json 来对比密钥。那份文件是官方登录缓存。自定义网关这条路线看的是环境变量 CODEX_GATEWAY_API_KEY 在进程里有没有值。桌面版读不到终端变量时,现象同样可以是认证失败:把变量放到用户环境后重启应用。
404 先区分两层:/v1/models 成功而 /v1/responses 返回 404,说明 Key 能用,但 Responses 路径或协议还没按网关兼容要求实现;两个都 404,先检查 base_url 是否多写或少写了 /v1,并以网关访问日志里的最终 URL 为准。网关把所有错误都收成 500 时,客户端无法区分认证、未知模型和上游失败。这要网关侧保留区别,不是再改一遍本地字段能解决的。
本文未核实的范围
OPENAI_BASE_URL从哪一个小版本起不再被读取:本文未展开。截至 2026-10-21 的环境变量说明没有把它列为稳定变量。改内置地址请用openai_base_url。- 0.134.0 之前,
[profiles.名字]旧表的每一步迁移:本文未展开。0.134.0 及以后以高级配置里的独立 profile 文件为准。 - 某一家网关是否提供
/v1/responses、增量流式和工具回路:未核实。用上一节的请求和网关日志确认。 - 云端 Codex、GitHub 上的任务是否使用这份本地 endpoint:本文不覆盖。
- 尾斜杠是否固定返回某个状态码:未写成普遍事实。
- 价格、额度、并发和活动:未写。以各服务自己的官方页面为准。
- 文内 PowerShell 脚本:未在 Windows PowerShell 5.1 或 PowerShell 7 上代跑。
本页为第三方接入说明,不是 OpenAI 官方文档。
常见问题
只设置 OPENAI_BASE_URL,为什么自定义 endpoint 不生效?
截至 2026-10-21 能核对到的稳定环境变量列表里没有 OPENAI_BASE_URL。要改内置 openai 的地址,写用户级 config.toml 的 openai_base_url。网关使用自己的 Key 时,改为独立 provider 的 base_url 和 env_key。
项目里的 .codex/config.toml 已经写了 base_url,为什么还是官方地址?
项目级文件会忽略 openai_base_url、model_provider 和 model_providers。把这些字段移到 ~/.codex/config.toml;Windows 则是 %USERPROFILE%\.codex\config.toml。保存后重启客户端,再用 /status 核对 provider。
网关只有 /v1/chat/completions,把 wire_api 改成 chat 可以吗?
不可以。wire_api 唯一支持的值是 responses,省略时默认也是它。现行 Codex 不支持 wire_api = chat,也不支持只有 Chat Completions 的端点。需要网关接受 POST /v1/responses。
终端里能用,桌面版仍报认证失败,是不是 Key 错了?
不一定。只在终端设置的变量,桌面启动的应用不一定继承。确认 env_key 写的是变量名,把该变量设到用户环境,重启桌面版,并用 /status 确认 provider 不是默认 openai。
GET /v1/models 返回 200,能否说明 Codex 已经接好?
不能。模型列表不构成对话,也不证明流式和工具。还需要一条带模型 ID 的 POST /v1/responses,在终端里陆续看到 response.completed,并在网关日志里核对这次请求落在目标账户和目标模型上。