> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Reasoning

> Request reasoning with an effort level or a thinking budget, check which routes support it, replay signed thinking blocks in tool loops, and read reasoning token usage.

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`**

```bash title="cURL"
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."}
    ]
  }'
```

**`OpenAI SDK`**

```python title="OpenAI SDK"
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["MERGE_GATEWAY_API_KEY"],
    base_url="https://api-gateway.merge.dev/v1/openai",
)

response = client.responses.create(
    model="anthropic/claude-sonnet-5-5",
    max_output_tokens=8192,
    reasoning={"effort": "high"},
    input="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](#reference) 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](#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](#effort-levels-by-vendor) 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.

```json
{
  "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`**

```python title="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

| Surface                                        | Effort control                                               | Budget control                       | Reasoning output                        | Signature                    | Reasoning token count                          |
| ---------------------------------------------- | ------------------------------------------------------------ | ------------------------------------ | --------------------------------------- | ---------------------------- | ---------------------------------------------- |
| `/v1/responses`                                | `reasoning.effort`                                           | `thinking`                           | `thinking` block                        | `signature` on the block     | `usage.reasoning_output_tokens`                |
| `/v1/anthropic/v1/messages`                    | `output_config.effort` with `thinking: {"type": "adaptive"}` | `thinking`                           | `thinking` or `redacted_thinking` block | `signature` on the block     | None                                           |
| `/v1/openai/chat/completions`                  | `reasoning_effort`                                           | `thinking`                           | `message.thinking`                      | `message.thinking_signature` | None                                           |
| `/v1/openai/responses`, `/v1/ai-sdk/responses` | `reasoning.effort`                                           | `thinking` on `/v1/openai/responses` | `reasoning` item with `summary_text`    | None                         | `usage.output_tokens_details.reasoning_tokens` |
| `/v1/ai-sdk/chat/completions`                  | None                                                         | `thinking`                           | `message.thinking`                      | `message.thinking_signature` | None                                           |
| `/v1/langchain/chat/completions`               | `reasoning_effort`                                           | None                                 | `message.thinking`                      | None                         | None                                           |

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

### Streaming

`/v1/responses` [streams](/merge-gateway/streaming) 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

| Field                         | Meaning                                                                                                                 |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `supports_reasoning`          | The route can produce reasoning                                                                                         |
| `reasoning.configurable`      | The route accepts at least one reasoning control                                                                        |
| `reasoning.default_enabled`   | The route may reason without being asked                                                                                |
| `reasoning.disable_supported` | The route can switch reasoning off                                                                                      |
| `reasoning.controls`          | Controls the route accepts, such as `reasoning.effort`, `reasoning_effort`, `thinking`, or `thinking.budget_tokens`     |
| `reasoning.effort_values`     | Effort levels the route accepts. Empty when the route has no curated list, which includes every Claude route.           |
| `reasoning.output_style`      | How 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 family                                                                    | Accepted levels                                 | Notes                                                                                                 |
| ------------------------------------------------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Claude Opus 4.7 and later, Sonnet 5 and later, Fable 5 and later on `anthropic` | `low`, `medium`, `high`, `xhigh`, `max`         | Runs as adaptive thinking with summarized output                                                      |
| Same models on `bedrock`                                                        | `high` only                                     | `xhigh` and `max` run at `high`. `low` and `medium` are dropped with a warning.                       |
| Earlier Claude on `anthropic`                                                   | `low`, `medium`, `high`                         | Sent as a 1,024, 2,048, or 4,096 token budget. A tight `max_tokens` lowers the level, with a warning. |
| Earlier Claude on `bedrock`                                                     | None                                            | Effort is dropped. Use `thinking.budget_tokens`.                                                      |
| OpenAI `gpt-5`, `gpt-5-mini`, `gpt-5-nano`                                      | `minimal`, `low`, `medium`, `high`              |                                                                                                       |
| OpenAI `gpt-5.1`                                                                | `none`, `low`, `medium`, `high`                 |                                                                                                       |
| OpenAI `gpt-5.2`, `gpt-5.4`, `gpt-5.5`, `gpt-5.6`                               | `none`, `low`, `medium`, `high`, `xhigh`        | `gpt-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-luna`                                                | `none`, `low`, `medium`, `high`, `xhigh`, `max` | `max` on the `openai` vendor only                                                                     |
| OpenAI `gpt-6-astra`                                                            | `low`, `medium`, `high`, `xhigh`, `max`         | Can't switch reasoning off. `max` on the `openai` vendor only.                                        |
| OpenAI o-series                                                                 | `low`, `medium`, `high`, `xhigh`                |                                                                                                       |

### Errors and warnings

| Code                            | Status  | Cause                                                                                                        |
| ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `unsupported_reasoning_effort`  | 400     | The effort value isn't one of the seven levels                                                               |
| `unsupported_params`            | 400     | The route that resolved can't do Gateway-controlled reasoning. Pick a route with `supports_reasoning: true`. |
| Validation error                | 422     | `thinking.type` is `enabled` without a positive `budget_tokens`                                              |
| `reasoning_effort_adjusted`     | Warning | The route doesn't accept your level. Also sent when `none` couldn't switch reasoning off.                    |
| `reasoning_effort_policy_bound` | Warning | The routing policy's effort bounds for the model changed your level                                          |
| `reasoning_exhausted`           | Warning | Output tokens were billed but the message is empty because the model spent the budget reasoning              |

## Next steps

#### [Tool calling](/merge-gateway/capabilities/tool-calling)

Carry reasoning through multi-round tool loops

#### [Warnings](/merge-gateway/warnings)

Every warning code Gateway can add to a response

#### [Model catalog](/merge-gateway/models)

Routes that support reasoning and the levels they accept