Client or integration
Codex CLI
Provider or upstream service
zai (Z.AI GLM Coding Plan)
OpenCodex version
2.50.0 (installed), dev at 29d632f
Endpoint or capability
/v1/responses upstream wire selection
Current behaviour
The zai row is pinned to Chat Completions:
id: "zai", label: "Z.AI - GLM Coding Plan", baseUrl: "https://api.z.ai/api/coding/paas/v4", adapter: "openai-chat", authKind: "key",
Because the upstream wire is chosen by the routed provider's adapter rather than by the inbound route (src/server/adapter-resolve.ts), every request to a GLM model leaves over Chat Completions no matter which surface the client used. In practice that path is the unstable one.
Z.AI serves the same subscription and the same key over three protocols, and the Responses endpoint is the one its own documentation points Codex-family clients at. OpenCodex already ships a Responses row for the domestic portal (zhipu-bigmodel-responses on open.bigmodel.cn/api/v1) but has no equivalent for api.z.ai.
Expected behaviour
zai should default to the Responses protocol on https://api.z.ai/api/v1, with Chat Completions still reachable as an explicit per-model opt-in.
Switching costs no models. Every id in the current roster answers on the Responses endpoint (measured against a live key, 2026-09-12):
POST /api/v1/responses glm-5.3 -> 200
POST /api/v1/responses glm-5.3-flash -> 200
POST /api/v1/responses glm-5.2 -> 200
POST /api/v1/responses glm-5.1 -> 200
POST /api/v1/responses glm-5 -> 200
POST /api/v1/responses glm-4.6 -> 200
POST /api/v1/responses glm-5-turbo -> 200
GET /api/v1/models also returns a Codex-shaped catalog carrying input_modalities, context_window and supported_reasoning_levels, which is strictly richer than what the Chat endpoint's /models exposes.
Minimal redacted request or reproduction
curl -s -o /dev/null -w '%{http_code}\n' https://api.z.ai/api/v1/responses \
-H "Authorization: Bearer <redacted>" -H 'Content-Type: application/json' \
-d '{"model":"glm-5.3","input":"hi","max_output_tokens":16}'
curl -s -o /dev/null -w '%{http_code}\n' https://api.z.ai/api/v1/chat/completions \
-H "Authorization: Bearer <redacted>" -H 'Content-Type: application/json' \
-d '{"model":"glm-5.3","messages":[{"role":"user","content":"hi"}],"max_tokens":8}'
Actual response or error
200 # /api/v1/responses
403 # /api/v1/chat/completions -> {"error":{"code":"model_access_denied", ...}}
200 # /api/coding/paas/v4/chat/completions
The two wires live under different path prefixes. That matters for the fix: swapping the adapter alone cannot reach the right endpoint, because resolveWireProtocolOverride changes provider.adapter and leaves provider.baseUrl untouched.
Upstream documentation
https://docs.z.ai/guides/llm/glm-5.3
| Protocol | Base URL |
| OpenAI Chat Completion Protocol | https://api.z.ai/api/coding/paas/v4 |
| OpenAI Response Protocol | https://api.z.ai/api/v1 |
| Anthropic Message Protocol | https://api.z.ai/api/anthropic |
https://docs.z.ai/devpack/latest-model
Claude Code / Goose (Anthropic-compatible): https://api.z.ai/api/anthropic
Codex: https://api.z.ai/api/v1
Other OpenAI-compatible tools: https://api.z.ai/api/coding/paas/v4
The same page also warns that a previously subscribed Coding Plan key may be limited to the Chat protocol. That did not reproduce on the key measured above, but it is the reason Chat has to stay reachable rather than be removed.
Suggested mapping or implementation notes
Add chatCompletionsPath as the mirror of the existing responsesPath, then point one row at both endpoints:
baseUrl: "https://api.z.ai",
adapter: "openai-responses",
responsesPath: "/api/v1/responses",
chatCompletionsPath: "/api/coding/paas/v4/chat/completions",
Chat then becomes the existing per-model opt-in: a user writes modelAdapters with openai-chat, resolveWireProtocolOverride swaps the adapter, and the new path field sends it to the coding prefix. A model that Chat refuses is pinned to Responses through modelWireDefaults, the same mechanism the xAI row uses for grok-4.20-multi-agent.
One dependency worth flagging: modelSuffixBracketStrip is honoured only by the openai-chat and ollama-native adapters today. The roster contains glm-5.3[1m] and glm-5.2[1m], and the bracketed id returns 400 model_not_found upstream, so the strip has to be applied on the Responses path as well before the default flips.
Additional context and attachments
The free-directory glm id is separate and stays on the Chat endpoint. Existing zai users change protocol silently, so this needs a release note plus the modelAdapters opt-in instructions for anyone on a Chat-only key.
Checks
Client or integration
Codex CLI
Provider or upstream service
zai (Z.AI GLM Coding Plan)
OpenCodex version
2.50.0 (installed), dev at 29d632f
Endpoint or capability
/v1/responses upstream wire selection
Current behaviour
The zai row is pinned to Chat Completions:
Because the upstream wire is chosen by the routed provider's adapter rather than by the inbound route (src/server/adapter-resolve.ts), every request to a GLM model leaves over Chat Completions no matter which surface the client used. In practice that path is the unstable one.
Z.AI serves the same subscription and the same key over three protocols, and the Responses endpoint is the one its own documentation points Codex-family clients at. OpenCodex already ships a Responses row for the domestic portal (zhipu-bigmodel-responses on open.bigmodel.cn/api/v1) but has no equivalent for api.z.ai.
Expected behaviour
zai should default to the Responses protocol on https://api.z.ai/api/v1, with Chat Completions still reachable as an explicit per-model opt-in.
Switching costs no models. Every id in the current roster answers on the Responses endpoint (measured against a live key, 2026-09-12):
GET /api/v1/models also returns a Codex-shaped catalog carrying input_modalities, context_window and supported_reasoning_levels, which is strictly richer than what the Chat endpoint's /models exposes.
Minimal redacted request or reproduction
Actual response or error
The two wires live under different path prefixes. That matters for the fix: swapping the adapter alone cannot reach the right endpoint, because resolveWireProtocolOverride changes provider.adapter and leaves provider.baseUrl untouched.
Upstream documentation
https://docs.z.ai/guides/llm/glm-5.3
https://docs.z.ai/devpack/latest-model
The same page also warns that a previously subscribed Coding Plan key may be limited to the Chat protocol. That did not reproduce on the key measured above, but it is the reason Chat has to stay reachable rather than be removed.
Suggested mapping or implementation notes
Add chatCompletionsPath as the mirror of the existing responsesPath, then point one row at both endpoints:
Chat then becomes the existing per-model opt-in: a user writes modelAdapters with openai-chat, resolveWireProtocolOverride swaps the adapter, and the new path field sends it to the coding prefix. A model that Chat refuses is pinned to Responses through modelWireDefaults, the same mechanism the xAI row uses for grok-4.20-multi-agent.
One dependency worth flagging: modelSuffixBracketStrip is honoured only by the openai-chat and ollama-native adapters today. The roster contains glm-5.3[1m] and glm-5.2[1m], and the bracketed id returns 400 model_not_found upstream, so the strip has to be applied on the Responses path as well before the default flips.
Additional context and attachments
The free-directory glm id is separate and stays on the Chat endpoint. Existing zai users change protocol silently, so this needs a release note plus the modelAdapters opt-in instructions for anyone on a Chat-only key.
Checks