Chat Completions
POST /v1/chat/completions
compatible
semantic operation generate
contract revision 1
State semantics: stateless
Raw contract deviations
These statements are source-contract evidence. Use the examples, field table, and Profile coverage matrix for ordinary integration, and expand this list when investigating a compatibility boundary.
- gateway routes model names; provider-owned chat state is not exposed
- unknown request fields are rejected before provider I/O
- provider-specific unsupported fields are rejected before provider I/O
Request examples
Section titled “Request examples”curl https://halro.example.com/v1/chat/completions \ -H "Authorization: Bearer $HALRO_GATEWAY_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "chat", "max_tokens": 256, "messages": [{"role": "user", "content": "Hello"}] }'import osfrom openai import OpenAI
client = OpenAI(base_url="https://halro.example.com/v1", api_key=os.environ["HALRO_GATEWAY_KEY"], timeout=60.0, max_retries=0)response = client.chat.completions.create( model="chat", max_tokens=256, messages=[{"role": "user", "content": "Hello"}],)print(response.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://halro.example.com/v1", apiKey: process.env.HALRO_GATEWAY_KEY, timeout: 60_000, maxRetries: 0,});const response = await client.chat.completions.create({ model: "chat", max_tokens: 256, messages: [{ role: "user", content: "Hello" }],});console.log(response.choices[0].message.content);Request headers
Section titled “Request headers”AuthorizationContent-Type
Request fields
Section titled “Request fields”| Field | Provider Profiles that do not support it | Combination-level constraints |
|---|---|---|
model | — | 1 |
messages | — | — |
messages[].name | openai.responses.v1, anthropic.messages.2023-06-01, gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, bedrock.mantle.responses.v1, bedrock.mantle.openai.responses.v1, bedrock.mantle.anthropic.messages.v1, minimax.anthropic.messages.v1, minimax.responses.v1, kimi.anthropic.messages.v1, kimi.responses.v1 | — |
messages[].content[].image_url | — | — |
messages[].content[].detail | anthropic.messages.2023-06-01, bedrock.mantle.anthropic.messages.v1, minimax.anthropic.messages.v1, kimi.anthropic.messages.v1 | — |
stream | — | 2 |
stream_options | — | — |
temperature | kimi.chat.v1, kimi.anthropic.messages.v1, kimi.responses.v1 | 3 |
top_p | kimi.chat.v1, kimi.anthropic.messages.v1, kimi.responses.v1 | 4 |
max_tokens | minimax.chat.v1, kimi.chat.v1, kimi.responses.v1 | 5 |
max_completion_tokens | deepseek.chat.v1 | 6 |
n | openai.responses.v1, anthropic.messages.2023-06-01, deepseek.chat.v1, bedrock.runtime.converse.text.v1, bedrock.mantle.responses.v1, bedrock.mantle.openai.responses.v1, bedrock.mantle.anthropic.messages.v1, minimax.anthropic.messages.v1, minimax.chat.v1, minimax.responses.v1, kimi.chat.v1, kimi.anthropic.messages.v1, kimi.responses.v1 | 7 |
stop | openai.responses.v1, bedrock.mantle.responses.v1, bedrock.mantle.openai.responses.v1, minimax.anthropic.messages.v1, minimax.chat.v1, minimax.responses.v1, kimi.chat.v1, kimi.responses.v1 | 8 |
seed | openai.responses.v1, anthropic.messages.2023-06-01, deepseek.chat.v1, gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, bedrock.mantle.responses.v1, bedrock.mantle.openai.responses.v1, bedrock.mantle.anthropic.messages.v1, minimax.anthropic.messages.v1, minimax.chat.v1, minimax.responses.v1, kimi.chat.v1, kimi.anthropic.messages.v1, kimi.responses.v1 | — |
tools | gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, bedrock.mantle.responses.v1, bedrock.mantle.openai.responses.v1 | 9 |
tool_choice | gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, kimi.chat.v1 | 10 |
parallel_tool_calls | deepseek.chat.v1, gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, minimax.chat.v1, minimax.responses.v1, kimi.chat.v1, kimi.responses.v1 | 11 |
response_format | anthropic.messages.2023-06-01, deepseek.chat.v1, gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, bedrock.mantle.anthropic.messages.v1, minimax.anthropic.messages.v1, minimax.chat.v1, minimax.responses.v1, kimi.anthropic.messages.v1 | 12 |
reasoning_effort | openai.responses.v1, anthropic.messages.2023-06-01, deepseek.chat.v1, gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, bedrock.mantle.responses.v1, bedrock.mantle.openai.responses.v1, bedrock.mantle.anthropic.messages.v1, minimax.anthropic.messages.v1, minimax.responses.v1, kimi.chat.v1, kimi.anthropic.messages.v1, kimi.responses.v1 | 13 |
user | anthropic.messages.2023-06-01, gemini.generate-content.text.v1beta, bedrock.runtime.converse.text.v1, bedrock.mantle.anthropic.messages.v1, minimax.anthropic.messages.v1, minimax.chat.v1, minimax.responses.v1, kimi.chat.v1, kimi.anthropic.messages.v1, kimi.responses.v1 | — |
Response fields
Section titled “Response fields”id object created model choices usage
Stream events
Section titled “Stream events”chat.completion.chunk [DONE] error
Provider Profile contract coverage and current availability
Section titled “Provider Profile contract coverage and current availability”| Provider Profile | Maturity | Current build | Unsupported request fields |
|---|---|---|---|
openai.chat-embeddings.v1 | compatible | creatable | undeclared |
openai.responses.v1 | compatible | creatable | messages[].name, n, stop, seed, reasoning_effort |
anthropic.messages.2023-06-01 | compatible | creatable | messages[].name, n, seed, response_format, reasoning_effort, user, messages[].content[].detail |
azure-openai.chat-embeddings.v1 | compatible | creatable | undeclared |
deepseek.chat.v1 | compatible | creatable | n, seed, max_completion_tokens, parallel_tool_calls, response_format, reasoning_effort |
openai-compatible.chat-embeddings.v1 | compatible | creatable | undeclared |
gemini.generate-content.text.v1beta | compatible | creatable | messages[].name, seed, tools, tool_choice, parallel_tool_calls, response_format, reasoning_effort, user |
bedrock.runtime.converse.text.v1 | compatible | withheld (cannot create) | messages[].name, n, seed, tools, tool_choice, parallel_tool_calls, response_format, reasoning_effort, user |
bedrock.mantle.chat.v1 | compatible | creatable | undeclared |
bedrock.mantle.openai.chat.v1 | compatible | creatable | undeclared |
bedrock.mantle.responses.v1 | compatible | creatable | messages[].name, n, stop, seed, tools, reasoning_effort |
bedrock.mantle.openai.responses.v1 | compatible | creatable | messages[].name, n, stop, seed, tools, reasoning_effort |
bedrock.mantle.anthropic.messages.v1 | compatible | creatable | messages[].name, n, seed, response_format, reasoning_effort, user, messages[].content[].detail |
minimax.anthropic.messages.v1 | compatible | creatable | messages[].name, n, seed, stop, response_format, reasoning_effort, user, messages[].content[].detail |
minimax.chat.v1 | compatible | creatable | n, seed, stop, response_format, parallel_tool_calls, user, max_tokens |
minimax.responses.v1 | compatible | creatable | messages[].name, n, seed, stop, response_format, parallel_tool_calls, reasoning_effort, user |
kimi.chat.v1 | compatible | creatable | temperature, top_p, n, seed, parallel_tool_calls, user, max_tokens, reasoning_effort, stop, tool_choice |
kimi.anthropic.messages.v1 | compatible | creatable | temperature, top_p, messages[].name, n, seed, response_format, reasoning_effort, user, messages[].content[].detail |
kimi.responses.v1 | compatible | withheld (cannot create) | temperature, top_p, messages[].name, n, seed, stop, max_tokens, parallel_tool_calls, reasoning_effort, user |
Declared transformations
Section titled “Declared transformations”openai.responses.v1
- Chat messages are mapped to stateless Responses input items
- store=false is always sent upstream
- this profile binds no stream primitive, so a streaming request is routed away rather than refused by field
anthropic.messages.2023-06-01
- portable Chat content is mapped to Anthropic Messages blocks
- an Anthropic image source is base64, url, or file and has no member for the OpenAI fidelity hint, so detail is rejected before provider I/O at any value other than auto rather than dropped from a request that paid for it
- response_format and reasoning_effort are declared unsupported at field granularity because support is value-dependent: json_schema maps to output_config.format and the low/medium/high/xhigh/max ladder maps to output_config.effort, while json_object and any effort outside that ladder have no Anthropic representation and are routed away before provider I/O
deepseek.chat.v1
- DeepSeek speaks this wire format but accepts a smaller member list, so the fields it has no place for are rejected before provider I/O rather than sent and ignored
- user is carried as DeepSeek’s user_id
- reasoning_effort and response_format are declared unsupported at field granularity because support is value-dependent: none maps to thinking.type=disabled and the low and high rungs map to thinking.reasoning_effort with thinking enabled, while minimal, medium and xhigh have no DeepSeek rung; json_object maps to response_format and json_schema has no DeepSeek counterpart
- max_completion_tokens is value-dependent too: it counts reasoning tokens and DeepSeek’s max_tokens does not, so it is carried as max_tokens on a request with thinking off and rejected before provider I/O on one with thinking on, or on one that already carries max_tokens
- n and parallel_tool_calls are value-dependent in the same way: n=1 and parallel_tool_calls=true are what omitting the member already means, and only n>1 and a request to run tools one at a time are rejected
gemini.generate-content.text.v1beta
- developer messages are merged into Gemini system_instruction
bedrock.runtime.converse.text.v1
- Bedrock stop reasons are normalized to OpenAI finish reasons
bedrock.mantle.responses.v1
- Chat messages are mapped to stateless Responses input items
- store=false is always sent upstream
- streaming requests with tools are rejected before provider I/O
bedrock.mantle.openai.responses.v1
- Chat messages are mapped to stateless Responses input items
- store=false is always sent upstream
- streaming requests with tools are rejected before provider I/O
bedrock.mantle.anthropic.messages.v1
- portable Chat content is mapped to Bedrock Mantle Anthropic Messages blocks
- an Anthropic image source is base64, url, or file and has no member for the OpenAI fidelity hint, so detail is rejected before provider I/O at any value other than auto rather than dropped from a request that paid for it
- Anthropic states that only base64-encoded image sources are available on Amazon Bedrock, so an image_url naming an address to fetch is rejected before provider I/O while a data URL is carried as a base64 source
minimax.anthropic.messages.v1
- portable Chat content is mapped to MiniMax’s Anthropic Messages blocks
- an Anthropic image source is base64, url, or file and has no member for the OpenAI fidelity hint, so detail is rejected before provider I/O at any value other than auto rather than dropped from a request that paid for it
- reasoning_effort is unsupported at any value because MiniMax’s reasoning switch is the thinking member and it accepts no output_config; a request that asks for no depth still reaches MiniMax with thinking disabled, because the portable mapper emits that member on its own and MiniMax spells it the same way
- stop is rejected before provider I/O rather than sent: MiniMax documents stop_sequences as ignored, so sending it would return 200 for a completion that ran past a boundary the caller set and paid for
- response_format has no counterpart on this face at all: MiniMax accepts no output_config, and its Chat face was later measured serving json_object while this one was not
minimax.chat.v1
- MiniMax speaks this wire format but accepts a smaller member list, so the fields it has no place for are rejected before provider I/O rather than sent and ignored — this upstream documents its unsupported parameters as ignored, not refused
- reasoning_effort is carried as MiniMax’s thinking member: none reaches thinking.type=disabled and every other rung reaches thinking.type=adaptive, because MiniMax’s switch has one on state and no depth ladder, so depth is not preserved
- a request that asks for no reasoning is sent with thinking explicitly disabled rather than left to MiniMax’s own default, which is on
- reasoning_split is sent alongside an enabled switch so reasoning returns in its own member instead of inline in the answer
- max_completion_tokens is carried as itself and max_tokens is carried into it only while thinking is off, because MiniMax’s single output bound counts reasoning; max_tokens is value-dependent for that reason — a request that also thinks, or that carries both members, is routed away by field before provider I/O rather than refused after the budget is reserved
- n and parallel_tool_calls are value-dependent: n=1 and parallel_tool_calls=true are what omitting the member already means
- response_format is value-dependent: json_object was measured against a real account on 2026-08-31 and is carried, text is dropped because it means what omitting the member already means, and the schema mode has never been sent and is rejected before provider I/O
minimax.responses.v1
- Chat messages are mapped to stateless Responses input items
- store=false is always sent upstream
- this profile binds no stream primitive, so a streaming request is routed away rather than refused by field — MiniMax itself documents stream on /v1/responses, so this is a Halro scope decision and not an upstream limit
- reasoning is unsupported here and supported on the MiniMax Chat profile: the canonical response mapper cannot preserve the reasoning items this endpoint returns
kimi.chat.v1
- Kimi speaks this wire format but does not model the sampling parameters at all: temperature, top_p and n are absent from its request schema, and its parameter reference pins each to one value per model and answers any other with an error, so they are rejected before provider I/O rather than sent or silently replaced with the pinned value
- temperature and top_p are rejected at any value rather than only at the wrong one, because the pinned value varies by model and a field rule is keyed by profile with no target in hand; carrying the pinned value is a capability-model change rather than a platform registration
- max_tokens is value-dependent: Kimi has one output bound and it counts reasoning, so an answer-only bound is the same tokens exactly while nothing is thinking — which is the ordinary case, because a request that names no depth is sent with reasoning switched off. A request that does ask for depth, or that carries both output members, is routed away before provider I/O
- reasoning_effort is value-dependent: none, low and high are carried, and minimal, medium, xhigh and Kimi’s own max are routed away because the portable ladder and Kimi’s do not share those rungs
- the reasoning member itself depends on the model named: kimi-k3 reads a top-level reasoning_effort while the K2.x line reads thinking, and the renderer follows Kimi’s own OpenAPI discriminator over exact model identifiers rather than inferring a family from a name
- K2.x has one on state and no depth ladder, so an effort routed to those models is honoured as on rather than preserved
- a request that names no depth is sent with reasoning explicitly switched off rather than left to Kimi’s default, which is on: measured 2026-09-01, kimi-k3 and kimi-k2.6 both accept the off switch, and leaving it out returned a 64-token budget spent entirely on reasoning with an empty answer. kimi-k2.7-code has no off state and keeps reasoning, which is a per-model fact this profile-scoped declaration cannot express
- stop is value-dependent: Kimi documents it as honoured and it is carried, but only within its published bounds of five sequences of at most 32 bytes each, and a request outside them is routed away before provider I/O rather than refused while the body is encoded
- tool_choice is value-dependent, and the two ways of forcing a tool call are not the same value: a named function together with a depth is refused by every Kimi model measured and is routed away before provider I/O, while required together with a depth is refused by the K2.x line and accepted by kimi-k3, which answers with a tool call and a reasoning span in one response. That second half is a per-model limit this profile-scoped declaration cannot express, so it is refused after the reservation rather than routed away with the kimi-k3 request that works
kimi.anthropic.messages.v1
- portable Chat content is mapped to Kimi’s Anthropic Messages blocks
- an Anthropic image source is base64, url, or file and has no member for the OpenAI fidelity hint, so detail is rejected before provider I/O at any value other than auto rather than dropped from a request that paid for it
- reasoning_effort is unsupported at any value on this face, and that is what makes the face usable at all: a request naming no depth reaches Kimi with thinking disabled and comes back with no thinking block, which the portable decoder can read, while a request naming a depth comes back with one, which it refuses outright after the upstream has already been paid
- temperature and top_p are rejected because Kimi pins each per model and answers any other value with an error
- response_format is value-dependent: output_config.format takes a json_schema and enforces it, and there is no schema-less mode
- stop is carried: measured honoured on this face, unlike MiniMax’s, which documents the member as ignored
kimi.responses.v1
- Chat messages are mapped to stateless Responses input items
- Kimi does not model the sampling parameters on this face either, so temperature and top_p are rejected before provider I/O
- this profile binds no stream primitive, so a streaming request is routed away rather than refused by field — Kimi itself documents stream on /v1/responses, so this is a Halro scope decision and not an upstream limit
- the schema-less json_object mode is unavailable here and available on the Kimi Chat profile: this endpoint models structured output as text.format and accepts the json_schema type alone, so a json_object request is routed away by the absent json_object capability rather than by field
- reasoning is unsupported here and supported on the Kimi Chat profile: the canonical response mapper cannot preserve the reasoning items this endpoint returns
- this endpoint serves kimi-k3 alone, which the model catalogue records per model rather than this profile-scoped declaration
Evidence
Section titled “Evidence”Verified evidence: gateway_contract, provider_transport_fixture, sdk_blackbox
SDK black-box protocol-stub matrix
Section titled “SDK black-box protocol-stub matrix”openai-go, openai-node, openai-python
Best practices
Section titled “Best practices”See the scenario library for routing failover, budgets, and interrupted streams.
Fields, coverage, transformations, and deviations are generated from Halro 8f185de7674c
(scripts/generate-api-pages-en.mjs; synchronized manifest digest 8e5e7a0a794721aa…).
The synchronized copy contains 1 recorded accuracy correction(s); source manifest digest b321ee18ad2e38f3….
The contract currently carries field names, not complete types, required relationships, or value semantics.
Footnotes
Section titled “Footnotes”-
Combination constraints for
model:kimi.chat.v1: Kimi speaks this wire format but does not model the sampling parameters at all: temperature, top_p and n are absent from its request schema, and its parameter reference pins each to one value per model and answers any other with an error, so they are rejected before provider I/O rather than sent or silently replaced with the pinned valuekimi.chat.v1: temperature and top_p are rejected at any value rather than only at the wrong one, because the pinned value varies by model and a field rule is keyed by profile with no target in hand; carrying the pinned value is a capability-model change rather than a platform registrationkimi.chat.v1: tool_choice is value-dependent, and the two ways of forcing a tool call are not the same value: a named function together with a depth is refused by every Kimi model measured and is routed away before provider I/O, while required together with a depth is refused by the K2.x line and accepted by kimi-k3, which answers with a tool call and a reasoning span in one response. That second half is a per-model limit this profile-scoped declaration cannot express, so it is refused after the reservation rather than routed away with the kimi-k3 request that workskimi.anthropic.messages.v1: temperature and top_p are rejected because Kimi pins each per model and answers any other value with an errorkimi.responses.v1: Kimi does not model the sampling parameters on this face either, so temperature and top_p are rejected before provider I/Okimi.responses.v1: the schema-less json_object mode is unavailable here and available on the Kimi Chat profile: this endpoint models structured output as text.format and accepts the json_schema type alone, so a json_object request is routed away by the absent json_object capability rather than by field
-
Combination constraints for
stream:openai.responses.v1: this profile binds no stream primitive, so a streaming request is routed away rather than refused by fieldbedrock.mantle.responses.v1: streaming requests with tools are rejected before provider I/Obedrock.mantle.openai.responses.v1: streaming requests with tools are rejected before provider I/Ominimax.responses.v1: this profile binds no stream primitive, so a streaming request is routed away rather than refused by field — MiniMax itself documents stream on /v1/responses, so this is a Halro scope decision and not an upstream limitkimi.responses.v1: this profile binds no stream primitive, so a streaming request is routed away rather than refused by field — Kimi itself documents stream on /v1/responses, so this is a Halro scope decision and not an upstream limit
-
Combination constraints for
temperature:kimi.chat.v1: Kimi speaks this wire format but does not model the sampling parameters at all: temperature, top_p and n are absent from its request schema, and its parameter reference pins each to one value per model and answers any other with an error, so they are rejected before provider I/O rather than sent or silently replaced with the pinned valuekimi.chat.v1: temperature and top_p are rejected at any value rather than only at the wrong one, because the pinned value varies by model and a field rule is keyed by profile with no target in hand; carrying the pinned value is a capability-model change rather than a platform registrationkimi.anthropic.messages.v1: temperature and top_p are rejected because Kimi pins each per model and answers any other value with an errorkimi.responses.v1: Kimi does not model the sampling parameters on this face either, so temperature and top_p are rejected before provider I/O
-
Combination constraints for
top_p:kimi.chat.v1: Kimi speaks this wire format but does not model the sampling parameters at all: temperature, top_p and n are absent from its request schema, and its parameter reference pins each to one value per model and answers any other with an error, so they are rejected before provider I/O rather than sent or silently replaced with the pinned valuekimi.chat.v1: temperature and top_p are rejected at any value rather than only at the wrong one, because the pinned value varies by model and a field rule is keyed by profile with no target in hand; carrying the pinned value is a capability-model change rather than a platform registrationkimi.anthropic.messages.v1: temperature and top_p are rejected because Kimi pins each per model and answers any other value with an errorkimi.responses.v1: Kimi does not model the sampling parameters on this face either, so temperature and top_p are rejected before provider I/O
-
Combination constraints for
max_tokens:deepseek.chat.v1: max_completion_tokens is value-dependent too: it counts reasoning tokens and DeepSeek’s max_tokens does not, so it is carried as max_tokens on a request with thinking off and rejected before provider I/O on one with thinking on, or on one that already carries max_tokensminimax.chat.v1: max_completion_tokens is carried as itself and max_tokens is carried into it only while thinking is off, because MiniMax’s single output bound counts reasoning; max_tokens is value-dependent for that reason — a request that also thinks, or that carries both members, is routed away by field before provider I/O rather than refused after the budget is reservedkimi.chat.v1: max_tokens is value-dependent: Kimi has one output bound and it counts reasoning, so an answer-only bound is the same tokens exactly while nothing is thinking — which is the ordinary case, because a request that names no depth is sent with reasoning switched off. A request that does ask for depth, or that carries both output members, is routed away before provider I/O
-
Combination constraints for
max_completion_tokens:deepseek.chat.v1: max_completion_tokens is value-dependent too: it counts reasoning tokens and DeepSeek’s max_tokens does not, so it is carried as max_tokens on a request with thinking off and rejected before provider I/O on one with thinking on, or on one that already carries max_tokensminimax.chat.v1: max_completion_tokens is carried as itself and max_tokens is carried into it only while thinking is off, because MiniMax’s single output bound counts reasoning; max_tokens is value-dependent for that reason — a request that also thinks, or that carries both members, is routed away by field before provider I/O rather than refused after the budget is reserved
-
Combination constraints for
n:deepseek.chat.v1: n and parallel_tool_calls are value-dependent in the same way: n=1 and parallel_tool_calls=true are what omitting the member already means, and only n>1 and a request to run tools one at a time are rejectedkimi.chat.v1: Kimi speaks this wire format but does not model the sampling parameters at all: temperature, top_p and n are absent from its request schema, and its parameter reference pins each to one value per model and answers any other with an error, so they are rejected before provider I/O rather than sent or silently replaced with the pinned value
-
Combination constraints for
stop:minimax.anthropic.messages.v1: stop is rejected before provider I/O rather than sent: MiniMax documents stop_sequences as ignored, so sending it would return 200 for a completion that ran past a boundary the caller set and paid forkimi.chat.v1: stop is value-dependent: Kimi documents it as honoured and it is carried, but only within its published bounds of five sequences of at most 32 bytes each, and a request outside them is routed away before provider I/O rather than refused while the body is encoded
-
Combination constraints for
tools:deepseek.chat.v1: n and parallel_tool_calls are value-dependent in the same way: n=1 and parallel_tool_calls=true are what omitting the member already means, and only n>1 and a request to run tools one at a time are rejectedbedrock.mantle.responses.v1: streaming requests with tools are rejected before provider I/Obedrock.mantle.openai.responses.v1: streaming requests with tools are rejected before provider I/O
-
Combination constraints for
tool_choice:kimi.chat.v1: tool_choice is value-dependent, and the two ways of forcing a tool call are not the same value: a named function together with a depth is refused by every Kimi model measured and is routed away before provider I/O, while required together with a depth is refused by the K2.x line and accepted by kimi-k3, which answers with a tool call and a reasoning span in one response. That second half is a per-model limit this profile-scoped declaration cannot express, so it is refused after the reservation rather than routed away with the kimi-k3 request that works
-
Combination constraints for
parallel_tool_calls:deepseek.chat.v1: n and parallel_tool_calls are value-dependent in the same way: n=1 and parallel_tool_calls=true are what omitting the member already means, and only n>1 and a request to run tools one at a time are rejected
-
Combination constraints for
response_format:anthropic.messages.2023-06-01: response_format and reasoning_effort are declared unsupported at field granularity because support is value-dependent: json_schema maps to output_config.format and the low/medium/high/xhigh/max ladder maps to output_config.effort, while json_object and any effort outside that ladder have no Anthropic representation and are routed away before provider I/Ominimax.chat.v1: response_format is value-dependent: json_object was measured against a real account on 2026-08-31 and is carried, text is dropped because it means what omitting the member already means, and the schema mode has never been sent and is rejected before provider I/O
-
Combination constraints for
reasoning_effort:anthropic.messages.2023-06-01: response_format and reasoning_effort are declared unsupported at field granularity because support is value-dependent: json_schema maps to output_config.format and the low/medium/high/xhigh/max ladder maps to output_config.effort, while json_object and any effort outside that ladder have no Anthropic representation and are routed away before provider I/Okimi.chat.v1: reasoning_effort is value-dependent: none, low and high are carried, and minimal, medium, xhigh and Kimi’s own max are routed away because the portable ladder and Kimi’s do not share those rungs