Skip to navigation

Reasoning

Control how much a model thinks before it answers, and read the reasoning it returns

Reasoning lets a model spend extra output tokens planning before it answers. Use it for multi-step coding, math, analysis, and tool-use planning, where a better answer is worth a slower, costlier request.

Support is per vendor route: send one control, and Gateway translates it for the serving route and warns you if the level changed.

Request reasoning

On /v1/responses, send one control:

  • reasoning: {"effort": "high"} names a level: none, minimal, low, medium, high, xhigh, or max. It works on any route with an effort control, including the newest Claude models.
  • thinking: {"type": "enabled", "budget_tokens": 2048} sets a token budget. Routes that take levels get one derived from it: under 4,096 is low, 4,096 to 16,383 medium, and 16,384 or more high.
curl https://api-gateway.merge.dev/v1/responses \
-H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-5-5",
"max_tokens": 8192,
"reasoning": {"effort": "high"},
"input": [
{"type": "message", "role": "user", "content": "Plan a migration that splits a monolith into services. Include risks and sequencing."}
]
}'

/v1/responses ignores a top-level reasoning_effort; use reasoning.effort, or reasoning_effort on /v1/openai/chat/completions. The surface table lists every spelling.

Reasoning tokens count against max_tokens. A model that spends the whole budget thinking returns a 200 with empty content and a reasoning_exhausted warning, so raise max_tokens or lower the effort.

To turn reasoning off, send thinking: {"type": "disabled"} on a route with disable_supported: true, or reasoning: {"effort": "none"} on a route whose effort_values include none. Claude Fable 5, Fable 5.1, and Opus 5.5 ignore the disable.

How Gateway picks the level

GET /v1/models lists each route’s support under vendors.<vendor>.capabilities.reasoning (route fields), and it can differ by vendor: moonshot/kimi-k3 accepts max on moonshot but tops out at xhigh on baseten, and doesn’t reason at all on fireworks. effort_values is empty on every Claude route, so use the per-vendor table for Claude.

When you name a level, Gateway prefers routes that accept it before comparing price, though a vendor pin, your own vendor keys, or a policy can still pick one that doesn’t. A known level the route doesn’t accept is never rejected: Gateway sends the nearest accepted level (higher only when nothing lower exists) with a reasoning_effort_adjusted warning whose detail.applied_effort is what ran, or null when the route has no effort control. include_routing_metadata: true adds routing.reasoning_effort_applied and routing.vendor_used.

Read reasoning output

On /v1/responses, reasoning arrives as thinking blocks ahead of the text. Keep them apart from the answer; most apps shouldn’t show reasoning to end users.

{
"output": [
{
"type": "message",
"role": "assistant",
"content": [
{"type": "thinking", "thinking": "Billing has the fewest inbound dependencies, so...", "signature": "ErUBCkYIBRgCIkC8..."},
{"type": "text", "text": "Start by isolating the billing workflow..."}
]
}
],
"usage": {"input_tokens": 41, "output_tokens": 1630, "total_tokens": 1671, "reasoning_output_tokens": null}
}

signature is set on Claude routes and null elsewhere. When Anthropic encrypts reasoning, the block has empty thinking and the payload on redacted_data. usage.reasoning_output_tokens is the part of output_tokens spent reasoning, or null when the vendor doesn’t report it (every Claude route).

Replay reasoning in multi-round tool loops

On Claude routes, the assistant turn you send back in a tool loop must keep its thinking block’s signature.

Unsigned thinking blocks are dropped

Gateway drops an unsigned thinking block before sending to a Claude route, since Anthropic rejects it. The request succeeds, but the model loses reasoning you already paid for. A signature is valid only on the vendor that produced it, so failover between anthropic and bedrock also drops signed blocks.

On /v1/responses and /v1/anthropic, echo assistant content blocks back unchanged. On /v1/openai/chat/completions and /v1/ai-sdk/chat/completions, thinking and thinking_signature are Gateway extensions on the message, so carry both:

OpenAI SDK
message = response.choices[0].message
messages.append({
"role": "assistant",
"content": message.content,
"thinking": message.thinking,
"thinking_signature": message.thinking_signature,
"tool_calls": message.tool_calls,
})
messages.append({"role": "tool", "tool_call_id": call_id, "content": tool_output})

/v1/openai/responses and LangChain can’t replay reasoning, and the chat completions surfaces can’t carry a redacted block. For long Claude tool loops, use /v1/responses or /v1/anthropic.

Reference

Controls and output by surface

SurfaceEffort controlBudget controlReasoning outputSignatureReasoning token count
/v1/responsesreasoning.effortthinkingthinking blocksignature on the blockusage.reasoning_output_tokens
/v1/anthropic/v1/messagesoutput_config.effort with thinking: {"type": "adaptive"}thinkingthinking or redacted_thinking blocksignature on the blockNone
/v1/openai/chat/completionsreasoning_effortthinkingmessage.thinkingmessage.thinking_signatureNone
/v1/openai/responses, /v1/ai-sdk/responsesreasoning.effortthinking on /v1/openai/responsesreasoning item with summary_textNoneusage.output_tokens_details.reasoning_tokens
/v1/ai-sdk/chat/completionsNonethinkingmessage.thinkingmessage.thinking_signatureNone
/v1/langchain/chat/completionsreasoning_effortNonemessage.thinkingNoneNone

reasoning.summary (auto, concise, or detailed) asks OpenAI reasoning models for a reasoning summary, which they don’t return otherwise.

Streaming

/v1/responses streams cumulative frames, each carrying the whole thinking block so far, so read the latest instead of concatenating. /v1/anthropic streams thinking_delta then signature_delta, matching Anthropic. The chat completions surfaces stream delta.thinking, with delta.thinking_signature and any warnings on the final chunk.

Route fields

FieldMeaning
supports_reasoningThe route can produce reasoning
reasoning.configurableThe route accepts at least one reasoning control
reasoning.default_enabledThe route may reason without being asked
reasoning.disable_supportedThe route can switch reasoning off
reasoning.controlsControls the route accepts, such as reasoning.effort, reasoning_effort, thinking, or thinking.budget_tokens
reasoning.effort_valuesEffort levels the route accepts. Empty when the route has no curated list, which includes every Claude route.
reasoning.output_styleHow the vendor exposes reasoning: reasoning_content, summarized, blocks, inline_think_tags, hidden, or null

Effort levels by vendor

Unlisted routes accept their reasoning.effort_values.

Model familyAccepted levelsNotes
Claude Opus 4.7 and later, Sonnet 5 and later, Fable 5 and later on anthropiclow, medium, high, xhigh, maxRuns as adaptive thinking with summarized output
Same models on bedrockhigh onlyxhigh and max run at high. low and medium are dropped with a warning.
Earlier Claude on anthropiclow, medium, highSent as a 1,024, 2,048, or 4,096 token budget. A tight max_tokens lowers the level, with a warning.
Earlier Claude on bedrockNoneEffort is dropped. Use thinking.budget_tokens.
OpenAI gpt-5, gpt-5-mini, gpt-5-nanominimal, low, medium, high
OpenAI gpt-5.1none, low, medium, high
OpenAI gpt-5.2, gpt-5.4, gpt-5.5, gpt-5.6none, low, medium, high, xhighgpt-5.6 on the openai vendor also runs max when the output budget is 2,048 tokens or more
OpenAI gpt-6-sol, gpt-6-lunanone, low, medium, high, xhigh, maxmax on the openai vendor only
OpenAI gpt-6-astralow, medium, high, xhigh, maxCan’t switch reasoning off. max on the openai vendor only.
OpenAI o-serieslow, medium, high, xhigh

Errors and warnings

CodeStatusCause
unsupported_reasoning_effort400The effort value isn’t one of the seven levels
unsupported_params400The route that resolved can’t do Gateway-controlled reasoning. Pick a route with supports_reasoning: true.
Validation error422thinking.type is enabled without a positive budget_tokens
reasoning_effort_adjustedWarningThe route doesn’t accept your level. Also sent when none couldn’t switch reasoning off.
reasoning_effort_policy_boundWarningThe routing policy’s effort bounds for the model changed your level
reasoning_exhaustedWarningOutput tokens were billed but the message is empty because the model spent the budget reasoning

Next steps