Follow Discord
Sweep 22 Oct 2026 · 04:32Z Build v2.1.308 527 read Stable v2.1.308 Latest v2.1.308 Next v2.1.308 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One capture · api

One read of Claude Developer Platformapi-20261019T193710Z

91 pages moved out of 783 read.

Pages moved 91 significant first
Pages read 783 in this capture
Captured 19:37 UTC
Corpus hash 666b1bc29b7d corpus-hash

What this read moved

1-25 of 91, page 1 of 4

This capture is too large to show at once. Changes 1-25 of 91 are below, significant first; the rest are on the following screens.

about-claude/models/optimizing-for-cost-and-intelligence Changed · +41 / -50 lines

from line 172
172172 
173173#### Turn on caching
174174 
175Setup takes little work. [Automatic caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching) places breakpoints for you; otherwise, the [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill) that ships with Claude Code can add caching to an existing integration from one prompt, `/claude-api add prompt caching to this integration`. On the code that produced these measurements, it put one breakpoint on the system block and one on each of the two most recent user turns; [Explicit cache breakpoints](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#explicit-cache-breakpoints) explains where to place them.
175Setup takes little work. [Automatic caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching) places breakpoints for you; otherwise, the [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill) that ships with Claude Code can add caching to an existing integration from one prompt:
176176 
177Let Claude Code add caching for you
178 
179```text
180/claude-api add prompt caching to this integration
181```
182 
183On the code that produced these measurements, the skill put one breakpoint on the system block and one on each of the two most recent user turns; [Explicit cache breakpoints](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#explicit-cache-breakpoints) explains where to place them.
184 
177185#### What breaks the cache
178186 
179187Keep per-request text in the newest user turn, after the last cache breakpoint, and make every cache-invalidating change at a natural break, such as the first request after a compaction. The cache is a byte-exact prefix match over the request in order (tools, then system prompt, then messages), so a change anywhere invalidates everything after it.
from line 321
313321* The usual case is over-specific instruction added to compensate for an older model: "verify twice," "be maximally thorough," a mandatory step-by-step procedure, or a hand-rolled reasoning scratchpad.
314322* A newer model follows such instructions to the letter, producing extra tool rounds and extra writing, so the bill goes up with no gain in accuracy.
315323 
316The audit is one command. The [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill) that ships with Claude Code has a `prompt-audit` command that reads a project's prompts and request code and reports what was written for a different model. This shortened excerpt shows it run against a support-desk prompt and request code containing those patterns:
324The audit is one command. The [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill) that ships with Claude Code has a `prompt-audit` command that reads a project's prompts and request code and reports what was written for a different model:
317325 
326Let Claude Code audit your prompts for you
327 
328```text
329/claude-api prompt-audit
330```
331 
332This shortened excerpt shows the command run against a support-desk prompt and request code containing those patterns:
333 
318334```text wrap
319335$ claude
320336> /claude-api prompt-audit
from line 658
642658 
643659The following example computes one request's cost at Claude Opus 5.5's list prices. Change the three prices for another model:
644660 
645<CodeGroup>
646 ```bash cURL
647 INPUT_PER_MTOK=4.00 # Claude Opus 5.5
648 CACHE_READ_PER_MTOK=0.20 # 0.05x the input price on Claude Opus 5.5; the multiplier differs by model
649 OUTPUT_PER_MTOK=20.00
661<CodeGroup exclude="shell:cURL, shell:CLI">
662 <CodeGroupItem language="cURL">
663 This calculation needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
664 </CodeGroupItem>
650665 
651 response=$(curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
652 -H "x-api-key: $ANTHROPIC_API_KEY" \
653 -H "anthropic-version: 2023-06-01" \
654 -H "content-type: application/json" \
655 -d '{
656 "model": "claude-opus-5-5",
657 "max_tokens": 1024,
658 "messages": [{"role": "user", "content": "Hello, Claude"}]
659 }')
666 <CodeGroupItem language="CLI">
667 This calculation needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
668 </CodeGroupItem>
660669 
661 cost=$(jq -r --argjson in_price "$INPUT_PER_MTOK" --argjson read_price "$CACHE_READ_PER_MTOK" --argjson out_price "$OUTPUT_PER_MTOK" '
662 .usage
663 | (.input_tokens * $in_price
664 + (.cache_creation.ephemeral_1h_input_tokens // 0) * $in_price * 2.00 # 1-hour cache write
665 + (.cache_creation.ephemeral_5m_input_tokens // 0) * $in_price * 1.25 # 5-minute cache write
666 + (.cache_read_input_tokens // 0) * $read_price # cache read
667 + .output_tokens * $out_price) / 1e6
668 ' <<<"$response")
669 printf 'Request cost: $%.6f\n' "$cost"
670 ```
671 
672 ```bash CLI
673 INPUT_PER_MTOK=4.00 # Claude Opus 5.5
674 CACHE_READ_PER_MTOK=0.20 # 0.05x the input price on Claude Opus 5.5; the multiplier differs by model
675 OUTPUT_PER_MTOK=20.00
676 
677 USAGE=$(ant messages create \
678 --model claude-opus-5-5 \
679 --max-tokens 1024 \
680 --message '{role: user, content: "Hello, Claude"}' \
681 --transform usage)
682 
683 COST=$(jq -r --argjson in_price "$INPUT_PER_MTOK" --argjson read_price "$CACHE_READ_PER_MTOK" --argjson out_price "$OUTPUT_PER_MTOK" '
684 (.input_tokens * $in_price
685 + (.cache_creation.ephemeral_1h_input_tokens // 0) * $in_price * 2.00 # 1-hour cache write
686 + (.cache_creation.ephemeral_5m_input_tokens // 0) * $in_price * 1.25 # 5-minute cache write
687 + (.cache_read_input_tokens // 0) * $read_price # cache read
688 + .output_tokens * $out_price) / 1e6
689 ' <<<"$USAGE")
690 printf 'Request cost: $%.6f\n' "$COST"
691 ```
692 
693670 ```python Python
694671 INPUT_PER_MTOK = 4.00 # Claude Opus 5.5
695672 # 0.05x the input price on Claude Opus 5.5; the multiplier differs by model
from line 870
893870 
894871### Automate the search with the Claude API skill
895872 
896Run the [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill)'s `cost-optimize` command before you search for savings. It profiles where your tokens go and applies the levers in order, caching first. Then run `/claude-api hillclimb` in the directory that holds your evaluation.
873Run the [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill)'s `cost-optimize` command before you search for savings. It profiles where your tokens go and applies the levers in order, caching first:
874 
875Let Claude Code find savings for you
876 
877```text
878/claude-api cost-optimize
879```
880 
881Then run the `hillclimb` command in the directory that holds your evaluation:
882 
883Let Claude Code run the search for you
884 
885```text
886/claude-api hillclimb
887```
897888 
898889Anthropic measured the first lever of `cost-optimize` and two searches on tau2-bench retail[24](https://platform.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence#refs), all on earlier models. The cache breakpoints that `cost-optimize` produced cut cost per task, and the pass rate fell by less than the spread of identical runs.
899890 

agents-and-tools/agent-skills/quickstart Changed · +2 / -6 lines

from line 374
374374 
375375 if [[ -n "$file_id" ]]; then
376376 # Download the file and save it
377 output_path="${TMPDIR:-/tmp}/renewable_energy.pptx"
378377 curl --fail-with-body -sS "https://api.anthropic.com/v1/files/$file_id/content" \
379378 -H "x-api-key: $ANTHROPIC_API_KEY" \
380379 -H "anthropic-version: 2023-06-01" \
381 -o "$output_path"
382 echo "Presentation saved to $output_path"
380 -o "${TMPDIR:-/tmp}/renewable_energy.pptx"
383381 fi
384382 ```
385383 
from line 397
399397 
400398 if [[ -n "$file_id" ]]; then
401399 # Download the file and save it
402 output_path="${TMPDIR:-/tmp}/renewable_energy.pptx"
403 ant files download --file-id "$file_id" --output "$output_path"
404 echo "Presentation saved to $output_path"
400 ant files download --file-id "$file_id" --output "${TMPDIR:-/tmp}/renewable_energy.pptx"
405401 fi
406402 ```
407403 

agents-and-tools/mcp-connector Changed · +100 / -58 lines

from line 576
576576 
577577<CodeGroup>
578578 ```bash cURL
579 BODY='{
580 "model": "claude-opus-5-5",
581 "max_tokens": 1024,
582 "mcp_servers": [
583 {
584 "type": "url",
585 "url": "https://example-server.modelcontextprotocol.io/sse",
586 "name": "example-mcp",
587 "authorization_token": "YOUR_TOKEN"
588 }
589 ],
590 "tools": [
591 {
592 "type": "mcp_toolset",
593 "mcp_server_name": "example-mcp"
594 }
595 ],
596 "messages": [
597 {
598 "role": "user",
599 "content": "What tools do you have available?"
600 }
601 ]
602 }'
603 
604579 # First request: the toolset isn't pinned, so the API asks the server for
605580 # its tools and the response starts with an mcp_tool_listing block.
606 # tee shows the response on stderr while the variable captures it.
607 FIRST=$(curl -sS https://api.anthropic.com/v1/messages \
581 curl https://api.anthropic.com/v1/messages \
608582 -H "content-type: application/json" \
609583 -H "x-api-key: $ANTHROPIC_API_KEY" \
610584 -H "anthropic-version: 2023-06-01" \
611585 -H "anthropic-beta: mcp-client-2026-09-15" \
612 -d "$BODY" | tee /dev/stderr)
586 -d '{
587 "model": "claude-opus-5-5",
588 "max_tokens": 1024,
589 "mcp_servers": [
590 {
591 "type": "url",
592 "url": "https://example-server.modelcontextprotocol.io/sse",
593 "name": "example-mcp",
594 "authorization_token": "YOUR_TOKEN"
595 }
596 ],
597 "tools": [
598 {
599 "type": "mcp_toolset",
600 "mcp_server_name": "example-mcp"
601 }
602 ],
603 "messages": [
604 {
605 "role": "user",
606 "content": "What tools do you have available?"
607 }
608 ]
609 }'
613610 
614611 # Pin the list: copy the block's tools into the toolset. The API uses
615 # exactly these entries and doesn't ask the server again.
616 TOOLS=$(jq '.content[] | select(.type == "mcp_tool_listing") | .tools' \
617 <<<"$FIRST")
618 PINNED=$(jq --argjson tools "$TOOLS" '.tools[0].tools = $tools' <<<"$BODY")
619 
620 # With a pinned toolset, the response has no mcp_tool_listing block.
612 # exactly these entries and doesn't ask the server again, so the response
613 # has no mcp_tool_listing block.
621614 curl https://api.anthropic.com/v1/messages \
622615 -H "content-type: application/json" \
623616 -H "x-api-key: $ANTHROPIC_API_KEY" \
624617 -H "anthropic-version: 2023-06-01" \
625618 -H "anthropic-beta: mcp-client-2026-09-15" \
626 -d "$PINNED"
619 -d '{
620 "model": "claude-opus-5-5",
621 "max_tokens": 1024,
622 "mcp_servers": [
623 {
624 "type": "url",
625 "url": "https://example-server.modelcontextprotocol.io/sse",
626 "name": "example-mcp",
627 "authorization_token": "YOUR_TOKEN"
628 }
629 ],
630 "tools": [
631 {
632 "type": "mcp_toolset",
633 "mcp_server_name": "example-mcp",
634 "tools": [
635 {
636 "name": "echo",
637 "description": "Returns the text it receives.",
638 "input_schema": {
639 "type": "object",
640 "properties": { "text": { "type": "string" } },
641 "required": ["text"]
642 }
643 }
644 ]
645 }
646 ],
647 "messages": [
648 {
649 "role": "user",
650 "content": "What tools do you have available?"
651 }
652 ]
653 }'
627654 ```
628655 
629656 ```bash CLI
630 request=$(cat <<'YAML'
657 # First request: the toolset isn't pinned, so the API asks the server for
658 # its tools and the response starts with an mcp_tool_listing block.
659 ant beta:messages create --beta mcp-client-2026-09-15 <<'YAML'
631660 model: claude-opus-5-5
632661 max_tokens: 1024
633662 mcp_servers:
from line 671
642671 - role: user
643672 content: What tools do you have available?
644673 YAML
645 )
646674 
647 # First request: the toolset isn't pinned, so the API asks the server for
648 # its tools and the response starts with an mcp_tool_listing block.
649 # tee shows the response on stderr while the variable captures it.
650 first=$(ant beta:messages create --beta mcp-client-2026-09-15 --format json \
651 <<<"$request" | tee /dev/stderr)
652 tools=$(jq -c '.content[] | select(.type == "mcp_tool_listing") | .tools' \
653 <<<"$first")
654 
655 # Pin the list: copy the block's tools into the toolset. The --tool flag
656 # replaces the body's tools array. The API uses exactly these entries and
657 # doesn't ask the server again, so the response has no mcp_tool_listing block.
658 ant beta:messages create --beta mcp-client-2026-09-15 \
659 --tool "{type: mcp_toolset, mcp_server_name: example-mcp, tools: $tools}" \
660 <<<"$request"
675 # Pin the list: copy the block's tools into the toolset. The API uses
676 # exactly these entries and doesn't ask the server again, so the response
677 # has no mcp_tool_listing block.
678 ant beta:messages create --beta mcp-client-2026-09-15 <<'YAML'
679 model: claude-opus-5-5
680 max_tokens: 1024
681 mcp_servers:
682 - type: url
683 url: https://example-server.modelcontextprotocol.io/sse
684 name: example-mcp
685 authorization_token: YOUR_TOKEN
686 tools:
687 - type: mcp_toolset
688 mcp_server_name: example-mcp
689 tools:
690 - name: echo
691 description: Returns the text it receives.
692 input_schema:
693 type: object
694 properties:
695 text:
696 type: string
697 required:
698 - text
699 messages:
700 - role: user
701 content: What tools do you have available?
702 YAML
661703 ```
662704 
663705 ```python Python

agents-and-tools/tool-use/build-a-tool-using-agent Changed · +37 / -731 lines

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 18
1818 
1919The request sends a `tools` array alongside the user message. When Claude determines that a tool call is needed, the response comes back with `stop_reason: "tool_use"` and a `tool_use` content block containing the tool name, a unique `id`, and the structured `input`. Your code runs the tool, then sends the result back in a `tool_result` block whose `tool_use_id` matches the `id` from the call.
2020 
21<CodeGroup>
22 ```bash cURL
23 #!/bin/bash
24 # Ring 1: Single tool, single turn.
25 
26 # Define one tool as a JSON fragment. The input_schema is a JSON Schema
27 # object describing the arguments Claude should pass when it calls this
28 # tool. This schema includes nested objects (recurrence), arrays
29 # (attendees), and optional fields, which is closer to real-world tools
30 # than a flat string argument.
31 TOOLS='[
32 {
33 "name": "create_calendar_event",
34 "description": "Create a calendar event with attendees and optional recurrence.",
35 "input_schema": {
36 "type": "object",
37 "properties": {
38 "title": {"type": "string"},
39 "start": {"type": "string", "format": "date-time"},
40 "end": {"type": "string", "format": "date-time"},
41 "attendees": {
42 "type": "array",
43 "items": {"type": "string", "format": "email"}
44 },
45 "recurrence": {
46 "type": "object",
47 "properties": {
48 "frequency": {"enum": ["daily", "weekly", "monthly"]},
49 "count": {"type": "integer", "minimum": 1}
50 }
51 }
52 },
53 "required": ["title", "start", "end"]
54 }
55 }
56 ]'
57 
58 USER_MSG="Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am."
59 
60 # Send the user's request along with the tool definition. Claude decides
61 # whether to call the tool based on the request and the tool description.
62 RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
63 -H "x-api-key: $ANTHROPIC_API_KEY" \
64 -H "anthropic-version: 2023-06-01" \
65 -H "content-type: application/json" \
66 -d "$(jq -n \
67 --argjson tools "$TOOLS" \
68 --arg msg "$USER_MSG" \
69 '{
70 model: "claude-opus-5-5",
71 max_tokens: 1024,
72 tools: $tools,
73 tool_choice: {type: "auto", disable_parallel_tool_use: true},
74 messages: [{role: "user", content: $msg}]
75 }')")
76 
77 # When Claude calls a tool, the response has stop_reason "tool_use"
78 # and the content array contains a tool_use block alongside any text.
79 echo "stop_reason: $(echo "$RESPONSE" | jq -r '.stop_reason')"
80 
81 # Find the tool_use block. A response may contain text blocks before the
82 # tool_use block, so filter by type rather than assuming position.
83 TOOL_USE=$(echo "$RESPONSE" | jq '.content[] | select(.type == "tool_use")')
84 TOOL_USE_ID=$(echo "$TOOL_USE" | jq -r '.id')
85 echo "Tool: $(echo "$TOOL_USE" | jq -r '.name')"
86 echo "Input: $(echo "$TOOL_USE" | jq -c '.input')"
87 
88 # Execute the tool. In a real system this would call your calendar API.
89 # Here the result is hardcoded to keep the example self-contained.
90 RESULT='{"event_id": "evt_123", "status": "created"}'
91 
92 # Send the result back. The tool_result block goes in a user message and
93 # its tool_use_id must match the id from the tool_use block above. The
94 # assistant's previous response is included so Claude has the full history.
95 ASSISTANT_CONTENT=$(echo "$RESPONSE" | jq '.content')
96 FOLLOWUP=$(curl -s https://api.anthropic.com/v1/messages \
97 -H "x-api-key: $ANTHROPIC_API_KEY" \
98 -H "anthropic-version: 2023-06-01" \
99 -H "content-type: application/json" \
100 -d "$(jq -n \
101 --argjson tools "$TOOLS" \
102 --arg msg "$USER_MSG" \
103 --argjson assistant "$ASSISTANT_CONTENT" \
104 --arg tool_use_id "$TOOL_USE_ID" \
105 --arg result "$RESULT" \
106 '{
107 model: "claude-opus-5-5",
108 max_tokens: 1024,
109 tools: $tools,
110 tool_choice: {type: "auto", disable_parallel_tool_use: true},
111 messages: [
112 {role: "user", content: $msg},
113 {role: "assistant", content: $assistant},
114 {role: "user", content: [
115 {type: "tool_result", tool_use_id: $tool_use_id, content: $result}
116 ]}
117 ]
118 }')")
119 
120 # With the tool result in hand, Claude produces a final natural-language
121 # answer and stop_reason becomes "end_turn".
122 echo "stop_reason: $(echo "$FOLLOWUP" | jq -r '.stop_reason')"
123 echo "$FOLLOWUP" | jq -r '.content[] | select(.type == "text") | .text'
124 ```
125 
126 ```bash CLI
127 #!/usr/bin/env bash
128 # Ring 1: Single tool, single turn.
129 # Uses jq for cross-turn message-array state — building an agentic loop in shell
130 # requires JSON manipulation beyond ant's single-call --transform scope.
131 set -euo pipefail
132 
133 USER_MSG="Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am."
134 MESSAGES=$(jq -n --arg msg "$USER_MSG" '[{role: "user", content: $msg}]')
135 
136 # Define one tool. The input_schema is a JSON Schema object describing
137 # the arguments Claude should pass when it calls this tool. This schema
138 # includes nested objects (recurrence), arrays (attendees), and optional
139 # fields, which is closer to real-world tools than a flat string argument.
140 call_api() {
141 # ant reads the request body as YAML on stdin: no auth headers, no
142 # hand-built JSON envelope. The static keys (model, tools, tool_choice)
143 # live in a quoted heredoc; the growing messages array is appended as
144 # JSON, which YAML accepts as flow syntax.
145 {
146 cat <<'YAML'
147 model: claude-opus-5-5
148 max_tokens: 1024
149 tool_choice: {type: auto, disable_parallel_tool_use: true}
150 tools:
151 - name: create_calendar_event
152 description: Create a calendar event with attendees and optional recurrence.
153 input_schema:
154 type: object
155 properties:
156 title: {type: string}
157 start: {type: string, format: date-time}
158 end: {type: string, format: date-time}
159 attendees:
160 type: array
161 items: {type: string, format: email}
162 recurrence:
163 type: object
164 properties:
165 frequency: {enum: [daily, weekly, monthly]}
166 count: {type: integer, minimum: 1}
167 required: [title, start, end]
168 YAML
169 printf 'messages: %s\n' "$MESSAGES"
170 } | ant messages create --format json
171 }
172 
173 # Send the user's request along with the tool definition. Claude decides
174 # whether to call the tool based on the request and the tool description.
175 RESPONSE=$(call_api)
176 
177 # When Claude calls a tool, the response has stop_reason "tool_use"
178 # and the content array contains a tool_use block alongside any text.
179 echo "stop_reason: $(jq -r '.stop_reason' <<<"$RESPONSE")"
180 
181 # Find the tool_use block. A response may contain text blocks before the
182 # tool_use block, so filter by type rather than assuming position.
183 TOOL_USE=$(jq '.content[] | select(.type == "tool_use")' <<<"$RESPONSE")
184 TOOL_USE_ID=$(jq -r '.id' <<<"$TOOL_USE")
185 echo "Tool: $(jq -r '.name' <<<"$TOOL_USE")"
186 echo "Input: $(jq -c '.input' <<<"$TOOL_USE")"
187 
188 # Execute the tool. In a real system this would call your calendar API.
189 # Here the result is hardcoded to keep the example self-contained.
190 RESULT='{"event_id": "evt_123", "status": "created"}'
191 
192 # Send the result back. The tool_result block goes in a user message and
193 # its tool_use_id must match the id from the tool_use block above. The
194 # assistant's previous response is included so Claude has the full history.
195 MESSAGES=$(jq \
196 --argjson assistant "$(jq '.content' <<<"$RESPONSE")" \
197 --arg tool_use_id "$TOOL_USE_ID" \
198 --arg result "$RESULT" \
199 '. + [
200 {role: "assistant", content: $assistant},
201 {role: "user", content: [
202 {type: "tool_result", tool_use_id: $tool_use_id, content: $result}
203 ]}
204 ]' <<<"$MESSAGES")
205 
206 FOLLOWUP=$(call_api)
207 
208 # With the tool result in hand, Claude produces a final natural-language
209 # answer and stop_reason becomes "end_turn".
210 echo "stop_reason: $(jq -r '.stop_reason' <<<"$FOLLOWUP")"
211 jq -r '.content[] | select(.type == "text") | .text' <<<"$FOLLOWUP"
212 ```
21<CodeGroup exclude="shell:cURL, shell:CLI">
22 <CodeGroupItem language="cURL">
23 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
24 </CodeGroupItem>
25 
26 <CodeGroupItem language="CLI">
27 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
28 </CodeGroupItem>
21329 
21430 ```python Python
21531 # Ring 1: Single tool, single turn.
from line 845
1029845 
1030846The other change is conversation history. Instead of rebuilding the `messages` array from scratch on each request, keep a running list and append to it. Every turn sees the complete prior context.
1031847 
1032<CodeGroup>
1033 ```bash cURL
1034 #!/bin/bash
1035 # Ring 2: The agentic loop.
1036 
1037 TOOLS='[
1038 {
1039 "name": "create_calendar_event",
1040 "description": "Create a calendar event with attendees and optional recurrence.",
1041 "input_schema": {
1042 "type": "object",
1043 "properties": {
1044 "title": {"type": "string"},
1045 "start": {"type": "string", "format": "date-time"},
1046 "end": {"type": "string", "format": "date-time"},
1047 "attendees": {"type": "array", "items": {"type": "string", "format": "email"}},
1048 "recurrence": {
1049 "type": "object",
1050 "properties": {
1051 "frequency": {"enum": ["daily", "weekly", "monthly"]},
1052 "count": {"type": "integer", "minimum": 1}
1053 }
1054 }
1055 },
1056 "required": ["title", "start", "end"]
1057 }
1058 }
1059 ]'
1060 
1061 run_tool() {
1062 local name="$1"
1063 local input="$2"
1064 if [ "$name" = "create_calendar_event" ]; then
1065 local title=$(echo "$input" | jq -r '.title')
1066 jq -n --arg title "$title" '{event_id: "evt_123", status: "created", title: $title}'
1067 else
1068 echo "{\"error\": \"Unknown tool: $name\"}"
1069 fi
1070 }
1071 
1072 # Keep the full conversation history in a JSON array so each turn sees prior context.
1073 MESSAGES='[{"role": "user", "content": "Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: [email protected], [email protected], [email protected]."}]'
1074 
1075 call_api() {
1076 curl -s https://api.anthropic.com/v1/messages \
1077 -H "x-api-key: $ANTHROPIC_API_KEY" \
1078 -H "anthropic-version: 2023-06-01" \
1079 -H "content-type: application/json" \
1080 -d "$(jq -n --argjson tools "$TOOLS" --argjson messages "$MESSAGES" \
1081 '{model: "claude-opus-5-5", max_tokens: 1024, tools: $tools, tool_choice: {type: "auto", disable_parallel_tool_use: true}, messages: $messages}')"
1082 }
1083 
1084 RESPONSE=$(call_api)
1085 
1086 # Loop until Claude stops asking for tools. Each iteration runs the requested
1087 # tool, appends the result to history, and asks Claude to continue.
1088 while [ "$(echo "$RESPONSE" | jq -r '.stop_reason')" = "tool_use" ]; do
1089 TOOL_USE=$(echo "$RESPONSE" | jq '.content[] | select(.type == "tool_use")')
1090 TOOL_NAME=$(echo "$TOOL_USE" | jq -r '.name')
1091 TOOL_INPUT=$(echo "$TOOL_USE" | jq -c '.input')
1092 TOOL_USE_ID=$(echo "$TOOL_USE" | jq -r '.id')
1093 RESULT=$(run_tool "$TOOL_NAME" "$TOOL_INPUT")
1094 
1095 ASSISTANT_CONTENT=$(echo "$RESPONSE" | jq '.content')
1096 MESSAGES=$(echo "$MESSAGES" | jq \
1097 --argjson assistant "$ASSISTANT_CONTENT" \
1098 --arg tool_use_id "$TOOL_USE_ID" \
1099 --arg result "$RESULT" \
1100 '. + [
1101 {role: "assistant", content: $assistant},
1102 {role: "user", content: [{type: "tool_result", tool_use_id: $tool_use_id, content: $result}]}
1103 ]')
1104 
1105 RESPONSE=$(call_api)
1106 done
1107 
1108 echo "$RESPONSE" | jq -r '.content[] | select(.type == "text") | .text'
1109 ```
1110 
1111 ```bash CLI
1112 #!/usr/bin/env bash
1113 # Ring 2: The agentic loop.
1114 # Uses jq for cross-turn message-array state — building an agentic loop in shell
1115 # requires JSON manipulation beyond ant's single-call --transform scope.
1116 set -euo pipefail
1117 
1118 run_tool() {
1119 local name="$1" input="$2"
1120 if [ "$name" = "create_calendar_event" ]; then
1121 jq -n --arg title "$(jq -r '.title' <<<"$input")" \
1122 '{event_id: "evt_123", status: "created", title: $title}'
1123 else
1124 printf '{"error": "Unknown tool: %s"}' "$name"
1125 fi
1126 }
1127 
1128 # Keep the full conversation history in a JSON array so each turn sees
1129 # prior context.
1130 MESSAGES='[{"role": "user", "content": "Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: [email protected], [email protected], [email protected]."}]'
1131 
1132 call_api() {
1133 # ant reads the request body as YAML on stdin: no auth headers, no
1134 # hand-built JSON envelope. The static keys (model, tools, tool_choice)
1135 # live in a quoted heredoc; the growing messages array is appended as
1136 # JSON, which YAML accepts as flow syntax.
1137 {
1138 cat <<'YAML'
1139 model: claude-opus-5-5
1140 max_tokens: 1024
1141 tool_choice: {type: auto, disable_parallel_tool_use: true}
1142 tools:
1143 - name: create_calendar_event
1144 description: Create a calendar event with attendees and optional recurrence.
1145 input_schema:
1146 type: object
1147 properties:
1148 title: {type: string}
1149 start: {type: string, format: date-time}
1150 end: {type: string, format: date-time}
1151 attendees:
1152 type: array
1153 items: {type: string, format: email}
1154 recurrence:
1155 type: object
1156 properties:
1157 frequency: {enum: [daily, weekly, monthly]}
1158 count: {type: integer, minimum: 1}
1159 required: [title, start, end]
1160 YAML
1161 printf 'messages: %s\n' "$MESSAGES"
1162 } | ant messages create --format json
1163 }
1164 
1165 RESPONSE=$(call_api)
1166 
1167 # Loop until Claude stops asking for tools. Each iteration runs the
1168 # requested tool, appends the result to history, and asks Claude to
1169 # continue.
1170 while [ "$(jq -r '.stop_reason' <<<"$RESPONSE")" = "tool_use" ]; do
1171 TOOL_USE=$(jq '.content[] | select(.type == "tool_use")' <<<"$RESPONSE")
1172 TOOL_NAME=$(jq -r '.name' <<<"$TOOL_USE")
1173 TOOL_INPUT=$(jq -c '.input' <<<"$TOOL_USE")
1174 TOOL_USE_ID=$(jq -r '.id' <<<"$TOOL_USE")
1175 RESULT=$(run_tool "$TOOL_NAME" "$TOOL_INPUT")
1176 
1177 MESSAGES=$(jq \
1178 --argjson assistant "$(jq '.content' <<<"$RESPONSE")" \
1179 --arg tool_use_id "$TOOL_USE_ID" \
1180 --arg result "$RESULT" \
1181 '. + [
1182 {role: "assistant", content: $assistant},
1183 {role: "user", content: [
1184 {type: "tool_result", tool_use_id: $tool_use_id, content: $result}
1185 ]}
1186 ]' <<<"$MESSAGES")
1187 
1188 RESPONSE=$(call_api)
1189 done
1190 
1191 jq -r '.content[] | select(.type == "text") | .text' <<<"$RESPONSE"
1192 ```
848<CodeGroup exclude="shell:cURL, shell:CLI">
849 <CodeGroupItem language="cURL">
850 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
851 </CodeGroupItem>
852 
853 <CodeGroupItem language="CLI">
854 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
855 </CodeGroupItem>
1193856 
1194857 ```python Python
1195858 # Ring 2: The agentic loop.
from line 1615
19521615 
19531616When Claude has multiple independent tool calls to make, it might return several `tool_use` blocks in a single response. Your loop needs to process all of them and send back all results together in one user message. Iterate over every `tool_use` block in `response.content`, not just the first.
19541617 
1955<CodeGroup>
1956 ```bash cURL
1957 #!/bin/bash
1958 # Ring 3: Multiple tools, parallel calls.
1959 
1960 TOOLS='[
1961 {
1962 "name": "create_calendar_event",
1963 "description": "Create a calendar event with attendees and optional recurrence.",
1964 "input_schema": {
1965 "type": "object",
1966 "properties": {
1967 "title": {"type": "string"},
1968 "start": {"type": "string", "format": "date-time"},
1969 "end": {"type": "string", "format": "date-time"},
1970 "attendees": {"type": "array", "items": {"type": "string", "format": "email"}},
1971 "recurrence": {
1972 "type": "object",
1973 "properties": {
1974 "frequency": {"enum": ["daily", "weekly", "monthly"]},
1975 "count": {"type": "integer", "minimum": 1}
1976 }
1977 }
1978 },
1979 "required": ["title", "start", "end"]
1980 }
1981 },
1982 {
1983 "name": "list_calendar_events",
1984 "description": "List all calendar events on a given date.",
1985 "input_schema": {
1986 "type": "object",
1987 "properties": {"date": {"type": "string", "format": "date"}},
1988 "required": ["date"]
1989 }
1990 }
1991 ]'
1992 
1993 run_tool() {
1994 case "$1" in
1995 create_calendar_event)
1996 jq -n --arg title "$(echo "$2" | jq -r '.title')" '{event_id: "evt_123", status: "created", title: $title}' ;;
1997 list_calendar_events)
1998 echo '{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}' ;;
1999 *)
2000 echo "{\"error\": \"Unknown tool: $1\"}" ;;
2001 esac
2002 }
2003 
2004 MESSAGES='[{"role": "user", "content": "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts."}]'
2005 
2006 call_api() {
2007 curl -s https://api.anthropic.com/v1/messages \
2008 -H "x-api-key: $ANTHROPIC_API_KEY" \
2009 -H "anthropic-version: 2023-06-01" \
2010 -H "content-type: application/json" \
2011 -d "$(jq -n --argjson tools "$TOOLS" --argjson messages "$MESSAGES" \
2012 '{model: "claude-opus-5-5", max_tokens: 1024, tools: $tools, messages: $messages}')"
2013 }
2014 
2015 RESPONSE=$(call_api)
2016 
2017 while [ "$(echo "$RESPONSE" | jq -r '.stop_reason')" = "tool_use" ]; do
2018 # A single response can contain multiple tool_use blocks. Process all of
2019 # them and return all results together in one user message.
2020 TOOL_RESULTS='[]'
2021 while read -r block; do
2022 NAME=$(echo "$block" | jq -r '.name')
2023 INPUT=$(echo "$block" | jq -c '.input')
2024 ID=$(echo "$block" | jq -r '.id')
2025 RESULT=$(run_tool "$NAME" "$INPUT")
2026 TOOL_RESULTS=$(echo "$TOOL_RESULTS" | jq --arg id "$ID" --arg result "$RESULT" \
2027 '. + [{type: "tool_result", tool_use_id: $id, content: $result}]')
2028 done < <(echo "$RESPONSE" | jq -c '.content[] | select(.type == "tool_use")')
2029 
2030 MESSAGES=$(echo "$MESSAGES" | jq \
2031 --argjson assistant "$(echo "$RESPONSE" | jq '.content')" \
2032 --argjson results "$TOOL_RESULTS" \
2033 '. + [{role: "assistant", content: $assistant}, {role: "user", content: $results}]')
2034 
2035 RESPONSE=$(call_api)
2036 done
2037 
2038 echo "$RESPONSE" | jq -r '.content[] | select(.type == "text") | .text'
2039 ```
2040 
2041 ```bash CLI
2042 #!/usr/bin/env bash
2043 # Ring 3: Multiple tools, parallel calls.
2044 # Uses jq for cross-turn message-array state — building an agentic loop in shell
2045 # requires JSON manipulation beyond ant's single-call --transform scope.
2046 set -euo pipefail
2047 
2048 run_tool() {
2049 case "$1" in
2050 create_calendar_event)
2051 jq -n --arg title "$(jq -r '.title' <<<"$2")" \
2052 '{event_id: "evt_123", status: "created", title: $title}' ;;
2053 list_calendar_events)
2054 echo '{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}' ;;
2055 *)
2056 printf '{"error": "Unknown tool: %s"}' "$1" ;;
2057 esac
2058 }
2059 
2060 MESSAGES='[{"role": "user", "content": "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts."}]'
2061 
2062 call_api() {
2063 # ant reads the request body as YAML on stdin: no auth headers, no
2064 # hand-built JSON envelope. The static keys (model, tools) live in a
2065 # quoted heredoc; the growing messages array is appended as JSON,
2066 # which YAML accepts as flow syntax.
2067 {
2068 cat <<'YAML'
2069 model: claude-opus-5-5
2070 max_tokens: 1024
2071 tools:
2072 - name: create_calendar_event
2073 description: Create a calendar event with attendees and optional recurrence.
2074 input_schema:
2075 type: object
2076 properties:
2077 title: {type: string}
2078 start: {type: string, format: date-time}
2079 end: {type: string, format: date-time}
2080 attendees:
2081 type: array
2082 items: {type: string, format: email}
2083 recurrence:
2084 type: object
2085 properties:
2086 frequency: {enum: [daily, weekly, monthly]}
2087 count: {type: integer, minimum: 1}
2088 required: [title, start, end]
2089 - name: list_calendar_events
2090 description: List all calendar events on a given date.
2091 input_schema:
2092 type: object
2093 properties:
2094 date: {type: string, format: date}
2095 required: [date]
2096 YAML
2097 printf 'messages: %s\n' "$MESSAGES"
2098 } | ant messages create --format json
2099 }
2100 
2101 RESPONSE=$(call_api)
2102 
2103 while [ "$(jq -r '.stop_reason' <<<"$RESPONSE")" = "tool_use" ]; do
2104 # A single response can contain multiple tool_use blocks. Process all
2105 # of them and return all results together in one user message.
2106 TOOL_RESULTS='[]'
2107 while read -r block; do
2108 NAME=$(jq -r '.name' <<<"$block")
2109 INPUT=$(jq -c '.input' <<<"$block")
2110 ID=$(jq -r '.id' <<<"$block")
2111 RESULT=$(run_tool "$NAME" "$INPUT")
2112 TOOL_RESULTS=$(jq --arg id "$ID" --arg result "$RESULT" \
2113 '. + [{type: "tool_result", tool_use_id: $id, content: $result}]' \
2114 <<<"$TOOL_RESULTS")
2115 done < <(jq -c '.content[] | select(.type == "tool_use")' <<<"$RESPONSE")
2116 
2117 MESSAGES=$(jq \
2118 --argjson assistant "$(jq '.content' <<<"$RESPONSE")" \
2119 --argjson results "$TOOL_RESULTS" \
2120 '. + [
2121 {role: "assistant", content: $assistant},
2122 {role: "user", content: $results}
2123 ]' <<<"$MESSAGES")
2124 
2125 RESPONSE=$(call_api)
2126 done
2127 
2128 jq -r '.content[] | select(.type == "text") | .text' <<<"$RESPONSE"
2129 ```
1618<CodeGroup exclude="shell:cURL, shell:CLI">
1619 <CodeGroupItem language="cURL">
1620 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
1621 </CodeGroupItem>
1622 
1623 <CodeGroupItem language="CLI">
1624 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
1625 </CodeGroupItem>
21301626 
21311627 ```python Python
21321628 # Ring 3: Multiple tools, parallel calls.
from line 2427
29312427 
29322428Tools fail. A calendar API might reject an event with too many attendees, or a date might be malformed. When a tool raises an error, send the error message back with `is_error: true` instead of crashing. Claude reads the error and can retry with corrected input, ask the user for clarification, or explain the limitation.
29332429 
2934<CodeGroup>
2935 ```bash cURL
2936 #!/bin/bash
2937 # Ring 4: Error handling.
2938 
2939 TOOLS='[
2940 {
2941 "name": "create_calendar_event",
2942 "description": "Create a calendar event with attendees and optional recurrence.",
2943 "input_schema": {
2944 "type": "object",
2945 "properties": {
2946 "title": {"type": "string"},
2947 "start": {"type": "string", "format": "date-time"},
2948 "end": {"type": "string", "format": "date-time"},
2949 "attendees": {"type": "array", "items": {"type": "string", "format": "email"}},
2950 "recurrence": {
2951 "type": "object",
2952 "properties": {
2953 "frequency": {"enum": ["daily", "weekly", "monthly"]},
2954 "count": {"type": "integer", "minimum": 1}
2955 }
2956 }
2957 },
2958 "required": ["title", "start", "end"]
2959 }
2960 },
2961 {
2962 "name": "list_calendar_events",
2963 "description": "List all calendar events on a given date.",
2964 "input_schema": {
2965 "type": "object",
2966 "properties": {"date": {"type": "string", "format": "date"}},
2967 "required": ["date"]
2968 }
2969 }
2970 ]'
2971 
2972 run_tool() {
2973 case "$1" in
2974 create_calendar_event)
2975 local count=$(echo "$2" | jq '.attendees | length // 0')
2976 if [ "$count" -gt 10 ]; then
2977 echo "ERROR: Too many attendees (max 10)"
2978 return 1
2979 fi
2980 jq -n --arg title "$(echo "$2" | jq -r '.title')" '{event_id: "evt_123", status: "created", title: $title}' ;;
2981 list_calendar_events)
2982 echo '{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}' ;;
2983 *)
2984 echo "ERROR: Unknown tool: $1"
2985 return 1 ;;
2986 esac
2987 }
2988 
2989 EMAILS=$(seq 0 14 | sed 's/.*/user&@example.com/' | paste -sd, -)
2990 MESSAGES="[{\"role\": \"user\", \"content\": \"Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: $EMAILS\"}]"
2991 
2992 call_api() {
2993 curl -s https://api.anthropic.com/v1/messages \
2994 -H "x-api-key: $ANTHROPIC_API_KEY" \
2995 -H "anthropic-version: 2023-06-01" \
2996 -H "content-type: application/json" \
2997 -d "$(jq -n --argjson tools "$TOOLS" --argjson messages "$MESSAGES" \
2998 '{model: "claude-opus-5-5", max_tokens: 1024, tools: $tools, messages: $messages}')"
2999 }
3000 
3001 RESPONSE=$(call_api)
3002 
3003 while [ "$(echo "$RESPONSE" | jq -r '.stop_reason')" = "tool_use" ]; do
3004 TOOL_RESULTS='[]'
3005 while read -r block; do
3006 NAME=$(echo "$block" | jq -r '.name')
3007 INPUT=$(echo "$block" | jq -c '.input')
3008 ID=$(echo "$block" | jq -r '.id')
3009 if OUTPUT=$(run_tool "$NAME" "$INPUT"); then
3010 TOOL_RESULTS=$(echo "$TOOL_RESULTS" | jq --arg id "$ID" --arg result "$OUTPUT" \
3011 '. + [{type: "tool_result", tool_use_id: $id, content: $result}]')
3012 else
3013 # Signal failure so Claude can retry or ask for clarification.
3014 TOOL_RESULTS=$(echo "$TOOL_RESULTS" | jq --arg id "$ID" --arg result "$OUTPUT" \
3015 '. + [{type: "tool_result", tool_use_id: $id, content: $result, is_error: true}]')
3016 fi
3017 done < <(echo "$RESPONSE" | jq -c '.content[] | select(.type == "tool_use")')
3018 
3019 MESSAGES=$(echo "$MESSAGES" | jq \
3020 --argjson assistant "$(echo "$RESPONSE" | jq '.content')" \
3021 --argjson results "$TOOL_RESULTS" \
3022 '. + [{role: "assistant", content: $assistant}, {role: "user", content: $results}]')
3023 
3024 RESPONSE=$(call_api)
3025 done
3026 
3027 echo "$RESPONSE" | jq -r '.content[] | select(.type == "text") | .text'
3028 ```
3029 
3030 ```bash CLI
3031 #!/usr/bin/env bash
3032 # Ring 4: Error handling.
3033 # Uses jq for cross-turn message-array state — building an agentic loop in shell
3034 # requires JSON manipulation beyond ant's single-call --transform scope.
3035 set -euo pipefail
3036 
3037 run_tool() {
3038 case "$1" in
3039 create_calendar_event)
3040 local count
3041 count=$(jq '.attendees | length // 0' <<<"$2")
3042 if [ "$count" -gt 10 ]; then
3043 echo "ERROR: Too many attendees (max 10)"
3044 return 1
3045 fi
3046 jq -n --arg title "$(jq -r '.title' <<<"$2")" \
3047 '{event_id: "evt_123", status: "created", title: $title}' ;;
3048 list_calendar_events)
3049 echo '{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}' ;;
3050 *)
3051 echo "ERROR: Unknown tool: $1"
3052 return 1 ;;
3053 esac
3054 }
3055 
3056 EMAILS=$(seq 0 14 | sed 's/.*/user&@example.com/' | paste -sd, -)
3057 MESSAGES=$(jq -n --arg msg "Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: $EMAILS" \
3058 '[{role: "user", content: $msg}]')
3059 
3060 call_api() {
3061 # ant reads the request body as YAML on stdin: no auth headers, no
3062 # hand-built JSON envelope. The static keys (model, tools) live in a
3063 # quoted heredoc; the growing messages array is appended as JSON,
3064 # which YAML accepts as flow syntax.
3065 {
3066 cat <<'YAML'
3067 model: claude-opus-5-5
3068 max_tokens: 1024
3069 tools:
3070 - name: create_calendar_event
3071 description: Create a calendar event with attendees and optional recurrence.
3072 input_schema:
3073 type: object
3074 properties:
3075 title: {type: string}
3076 start: {type: string, format: date-time}
3077 end: {type: string, format: date-time}
3078 attendees:
3079 type: array
3080 items: {type: string, format: email}
3081 recurrence:
3082 type: object
3083 properties:
3084 frequency: {enum: [daily, weekly, monthly]}
3085 count: {type: integer, minimum: 1}
3086 required: [title, start, end]
3087 - name: list_calendar_events
3088 description: List all calendar events on a given date.
3089 input_schema:
3090 type: object
3091 properties:
3092 date: {type: string, format: date}
3093 required: [date]
3094 YAML
3095 printf 'messages: %s\n' "$MESSAGES"
3096 } | ant messages create --format json
3097 }
3098 
3099 RESPONSE=$(call_api)
3100 
3101 while [ "$(jq -r '.stop_reason' <<<"$RESPONSE")" = "tool_use" ]; do
3102 TOOL_RESULTS='[]'
3103 while read -r block; do
3104 NAME=$(jq -r '.name' <<<"$block")
3105 INPUT=$(jq -c '.input' <<<"$block")
3106 ID=$(jq -r '.id' <<<"$block")
3107 if OUTPUT=$(run_tool "$NAME" "$INPUT"); then
3108 TOOL_RESULTS=$(jq --arg id "$ID" --arg result "$OUTPUT" \
3109 '. + [{type: "tool_result", tool_use_id: $id, content: $result}]' \
3110 <<<"$TOOL_RESULTS")
3111 else
3112 # Signal failure so Claude can retry or ask for clarification.
3113 TOOL_RESULTS=$(jq --arg id "$ID" --arg result "$OUTPUT" \
3114 '. + [{type: "tool_result", tool_use_id: $id, content: $result, is_error: true}]' \
3115 <<<"$TOOL_RESULTS")
3116 fi
3117 done < <(jq -c '.content[] | select(.type == "tool_use")' <<<"$RESPONSE")
3118 
3119 MESSAGES=$(jq \
3120 --argjson assistant "$(jq '.content' <<<"$RESPONSE")" \
3121 --argjson results "$TOOL_RESULTS" \
3122 '. + [
3123 {role: "assistant", content: $assistant},
3124 {role: "user", content: $results}
3125 ]' <<<"$MESSAGES")
3126 
3127 RESPONSE=$(call_api)
3128 done
3129 
3130 jq -r '.content[] | select(.type == "text") | .text' <<<"$RESPONSE"
3131 ```
2430<CodeGroup exclude="shell:cURL, shell:CLI">
2431 <CodeGroupItem language="cURL">
2432 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
2433 </CodeGroupItem>
2434 
2435 <CodeGroupItem language="CLI">
2436 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
2437 </CodeGroupItem>
31322438 
31332439 ```python Python
31342440 # Ring 4: Error handling.
from line 3323
40173323 
40183324Rings 2 through 4 wrote the same loop by hand: call the API, check `stop_reason`, run tools, append results, repeat. The Tool Runner does this for you. Define each tool as a function, pass the list to `client.beta.messages.tool_runner()` (typescript: `client.beta.messages.toolRunner()`; java: `client.beta().messages().toolRunner()`; php: `$client->beta->messages->toolRunner()`; csharp: `client.Beta.Messages.ToolRunner()`; go: `client.Beta.Messages.NewToolRunner()`), and retrieve the final message once the loop completes. Error wrapping, result formatting, and conversation management are handled internally.
40193325 
4020Each SDK provides a helper that turns an ordinary function into a runnable tool and derives the input schema from its signature; the tabs below show the idiomatic form for each language.
4021 
4022<CodeGroup exclude="shell">
3326Each SDK provides a helper that turns an ordinary function into a runnable tool and derives the input schema from its signature; the following tabs show the idiomatic form for each language.
3327 
3328<CodeGroup exclude="shell:cURL, shell:CLI">
40233329 <CodeGroupItem language="cURL">
4024 The Tool Runner is a helper in the Claude SDK. Raw HTTP requests have no equivalent. To keep using curl, use the loop from [Ring 4](https://platform.claude.com/docs/en/agents-and-tools/tool-use/build-a-tool-using-agent#ring-4-error-handling).
3330 The Tool Runner is a helper in the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview). Raw HTTP requests have no equivalent.
40253331 </CodeGroupItem>
40263332 
40273333 <CodeGroupItem language="CLI">
4028 The Tool Runner is a helper in the Claude SDK. The `ant` CLI has no equivalent. To keep using the CLI, use the loop from [Ring 4](https://platform.claude.com/docs/en/agents-and-tools/tool-use/build-a-tool-using-agent#ring-4-error-handling).
3334 The Tool Runner is a helper in the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview). The `ant` CLI has no equivalent.
40293335 </CodeGroupItem>
40303336 
40313337 ```python Python
40323338 

agents-and-tools/tool-use/code-execution-tool Changed · +16 / -15 lines

from line 289
289289 
290290<CodeGroup>
291291 ```bash cURL
292 # First, upload a file and capture the file ID (using jq)
293 FILE_ID=$(curl --fail-with-body -sS https://api.anthropic.com/v1/files \
292 # First, upload a file
293 curl --fail-with-body -sS https://api.anthropic.com/v1/files \
294294 -H "x-api-key: $ANTHROPIC_API_KEY" \
295295 -H "anthropic-version: 2023-06-01" \
296 -F "[email protected]" | jq -r '.id')
297297 
298 # Then use the file_id with code execution
298 # Then use the file with code execution: FILE_ID is the id from the upload response
299299 curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
300300 -H "x-api-key: $ANTHROPIC_API_KEY" \
301301 -H "anthropic-version: 2023-06-01" \
from line 318
318318 ```
319319 
320320 ```bash CLI
321 # First, upload a file and capture the file ID
322 FILE_ID=$(ant files upload --file ./data.csv --transform id --raw-output)
321 # First, upload a file
322 ant files upload --file ./data.csv
323323 
324 # Then use the file_id with code execution
324 # Then use the file with code execution: FILE_ID is the id from the upload response
325325 ant messages create <<YAML
326326 model: claude-opus-5-5
327327 max_tokens: 4096
from line 1139
11391139 
11401140<CodeGroup>
11411141 ```bash cURL
1142 # First request: Create a file with a random number, capturing the container ID (using jq)
1143 CONTAINER_ID=$(curl -s https://api.anthropic.com/v1/messages \
1142 # First request: Create a file with a random number
1143 curl https://api.anthropic.com/v1/messages \
11441144 -H "x-api-key: $ANTHROPIC_API_KEY" \
11451145 -H "anthropic-version: 2023-06-01" \
11461146 -H "content-type: application/json" \
from line 1155
11551155 "type": "code_execution_20250825",
11561156 "name": "code_execution"
11571157 }]
1158 }' | jq -r '.container.id')
1158 }'
11591159 
11601160 # Second request: Reuse the container to read the file
1161 # CONTAINER_ID is the container.id from the first response
11611162 curl https://api.anthropic.com/v1/messages \
11621163 -H "x-api-key: $ANTHROPIC_API_KEY" \
11631164 -H "anthropic-version: 2023-06-01" \
from line 1180
11791180 
11801181 ```bash CLI
11811182 # First request: Create a file with a random number
1182 CONTAINER_ID=$(ant messages create \
1183 ant messages create \
11831184 --model claude-opus-5-5 \
11841185 --max-tokens 4096 \
11851186 --message '{role: user, content: Write a file with a random number and save it to "/tmp/number.txt"}' \
1186 --tool '{type: code_execution_20250825, name: code_execution}' \
1187 --transform container.id --raw-output)
1187 --tool '{type: code_execution_20250825, name: code_execution}'
11881188 
11891189 # Second request: Reuse the container to read the file
1190 # CONTAINER_ID is the container.id from the first response
11901191 ant messages create \
11911192 --container "$CONTAINER_ID" \
11921193 --model claude-opus-5-5 \

agents-and-tools/tool-use/fine-grained-tool-streaming Changed · +2 / -3 lines

from line 63
6363 ```
6464 
6565 ```bash CLI
66 ant messages create --stream --format jsonl <<'YAML' |
66 ant messages create --stream <<'YAML'
6767 model: claude-opus-5-5
6868 max_tokens: 65536
6969 tools:
from line 86
8686 - role: user
8787 content: Can you write a long poem and make a file called poem.txt?
8888 YAML
89 jq -rj 'select(.delta.type == "input_json_delta") | .delta.partial_json'
9089 ```
9190 
9291 ```python Python
from line 468
469468 ```
470469</CodeGroup>
471470 
472Every tab turns on fine-grained streaming for the `make_file` tool. The SDK tabs print each input fragment the moment it arrives, then print the complete accumulated input once the stream ends. The cURL tab shows the raw event stream, and the CLI tab uses `jq` to print just the fragments. Because the printed fragments join into the full tool input, the poem fills your terminal as Claude writes it:
471Each input fragment arrives the moment Claude writes it, and the fragments join into the full tool input, so the poem arrives piece by piece:
473472 
474473```text wrap
475474{"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", "", "I.", "", "Beneath the vast and star-strewn sky,", "Where silver moonbeams softly lie,", ...

agents-and-tools/tool-use/overview Changed · +9 / -107 lines

from line 23
2323 ```
2424 
2525 ```bash CLI
26 ant messages create --transform content --format yaml \
26 ant messages create \
2727 --model claude-opus-5-5 \
2828 --max-tokens 1024 \
2929 --tool '{type: web_search_20260209, name: web_search}' \
from line 142
142142 
143143Here's that round trip in full for a client tool. The first request defines a `get_weather` tool, and Claude answers the question by calling it: the response carries a `tool_use` block, your code runs the lookup, and a second request sends the result back in a `tool_result` block so Claude can reply with the answer.
144144 
145<CodeGroup>
146 ```bash cURL
147 # Claude replies with a tool_use block naming the tool and its arguments.
148 TOOLS='[
149 {
150 "name": "get_weather",
151 "description": "Get the current weather for a given location.",
152 "input_schema": {
153 "type": "object",
154 "properties": {
155 "location": {"type": "string", "description": "City and state, e.g. San Francisco, CA"}
156 },
157 "required": ["location"]
158 }
159 }
160 ]'
161 USER_MSG="What's the weather in San Francisco?"
162 RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
163 -H "x-api-key: $ANTHROPIC_API_KEY" \
164 -H "anthropic-version: 2023-06-01" \
165 -H "content-type: application/json" \
166 -d "$(jq -n --argjson tools "$TOOLS" --arg msg "$USER_MSG" '{
167 model: "claude-opus-5-5",
168 max_tokens: 1024,
169 tools: $tools,
170 # Ask for at most one tool call per turn.
171 tool_choice: {type: "auto", disable_parallel_tool_use: true},
172 messages: [{role: "user", content: $msg}]
173 }')")
174 TOOL_USE=$(echo "$RESPONSE" | jq '.content[] | select(.type == "tool_use")')
175 echo "Claude called $(echo "$TOOL_USE" | jq -r '.name') with $(echo "$TOOL_USE" | jq -c '.input')"
145<CodeGroup exclude="shell:cURL, shell:CLI">
146 <CodeGroupItem language="cURL">
147 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
148 </CodeGroupItem>
176149 
177 # Run the tool, then send the result back in a tool_result block.
178 # Claude uses the result to answer the original question.
179 WEATHER="15 degrees Celsius, partly cloudy"
180 curl -s https://api.anthropic.com/v1/messages \
181 -H "x-api-key: $ANTHROPIC_API_KEY" \
182 -H "anthropic-version: 2023-06-01" \
183 -H "content-type: application/json" \
184 -d "$(jq -n \
185 --argjson tools "$TOOLS" \
186 --arg msg "$USER_MSG" \
187 --argjson assistant "$(echo "$RESPONSE" | jq '.content')" \
188 --arg tool_use_id "$(echo "$TOOL_USE" | jq -r '.id')" \
189 --arg weather "$WEATHER" \
190 '{
191 model: "claude-opus-5-5",
192 max_tokens: 1024,
193 tools: $tools,
194 tool_choice: {type: "auto", disable_parallel_tool_use: true},
195 messages: [
196 {role: "user", content: $msg},
197 {role: "assistant", content: $assistant},
198 {role: "user", content: [
199 {type: "tool_result", tool_use_id: $tool_use_id, content: $weather}
200 ]}
201 ]
202 }')"
203 ```
204 
205 ```bash CLI
206 # ant reads the request body as YAML on stdin; jq carries the conversation
207 # state into the second request.
208 USER_MSG="What's the weather in San Francisco?"
209 MESSAGES=$(jq -n --arg msg "$USER_MSG" '[{role: "user", content: $msg}]')
210 call_api() {
211 {
212 cat <<'YAML'
213 model: claude-opus-5-5
214 max_tokens: 1024
215 # Ask for at most one tool call per turn.
216 tool_choice: {type: auto, disable_parallel_tool_use: true}
217 tools:
218 - name: get_weather
219 description: Get the current weather for a given location.
220 input_schema:
221 type: object
222 properties:
223 location: {type: string, description: "City and state, e.g. San Francisco, CA"}
224 required: [location]
225 YAML
226 printf 'messages: %s\n' "$MESSAGES"
227 } | ant messages create --format json
228 }
229 
230 # Claude replies with a tool_use block naming the tool and its arguments.
231 RESPONSE=$(call_api)
232 TOOL_USE=$(jq '.content[] | select(.type == "tool_use")' <<<"$RESPONSE")
233 echo "Claude called $(jq -r '.name' <<<"$TOOL_USE") with $(jq -c '.input' <<<"$TOOL_USE")"
234 
235 # Run the tool, then send the result back in a tool_result block.
236 WEATHER="15 degrees Celsius, partly cloudy"
237 MESSAGES=$(jq \
238 --argjson assistant "$(jq '.content' <<<"$RESPONSE")" \
239 --arg tool_use_id "$(jq -r '.id' <<<"$TOOL_USE")" \
240 --arg weather "$WEATHER" \
241 '. + [
242 {role: "assistant", content: $assistant},
243 {role: "user", content: [
244 {type: "tool_result", tool_use_id: $tool_use_id, content: $weather}
245 ]}
246 ]' <<<"$MESSAGES")
247 
248 # Claude uses the result to answer the original question.
249 call_api
250 ```
150 <CodeGroupItem language="CLI">
151 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
152 </CodeGroupItem>
251153 
252154 ```python Python
253155 client = anthropic.Anthropic()

build-with-claude/batch-processing Changed · +8 / -28 lines

from line 457
457457 
458458<CodeGroup>
459459 ```bash cURL
460 #!/bin/sh
461 # ...
462460 # Check the status; repeat until processing_status is "ended"
463 curl -s "https://api.anthropic.com/v1/messages/batches/$MESSAGE_BATCH_ID" \
461 curl "https://api.anthropic.com/v1/messages/batches/$MESSAGE_BATCH_ID" \
464462 --header "x-api-key: $ANTHROPIC_API_KEY" \
465 --header "anthropic-version: 2023-06-01" \
466 | jq -r '.processing_status'
463 --header "anthropic-version: 2023-06-01"
467464 ```
468465 
469466 ```bash CLI
470 #!/bin/bash
471 # ...
472467 # Check the status; repeat until processing_status is "ended"
473468 ant messages:batches retrieve \
474 --message-batch-id "$MESSAGE_BATCH_ID" \
475 --transform processing_status --raw-output
469 --message-batch-id "$MESSAGE_BATCH_ID"
476470 ```
477471 
478472 ```python Python
from line 605
611605 
612606<CodeGroup>
613607 ```bash cURL
614 #!/bin/sh
615608 # Fetches one page. While the response's has_more is true, pass its
616609 # last_id as after_id to fetch the next page. (The SDKs and the CLI
617610 # perform automatic pagination.)
from line 720
727720 
728721<CodeGroup>
729722 ```bash cURL
730 #!/bin/sh
731 # Fetch the batch's results_url, then stream the .jsonl results it
732 # points to. For per-result handling (retries, validation errors),
733 # use the SDK examples in the other tabs.
734 RESULTS_URL=$(curl -s "https://api.anthropic.com/v1/messages/batches/msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d" \
723 # Streams the batch's .jsonl results, one result per line.
724 # For per-result handling (retries, validation errors), use the Claude SDK.
725 curl "https://api.anthropic.com/v1/messages/batches/msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d/results" \
735726 --header "anthropic-version: 2023-06-01" \
736 --header "x-api-key: $ANTHROPIC_API_KEY" \
737 | jq -r '.results_url')
738 
739 curl -s "$RESULTS_URL" \
740 --header "anthropic-version: 2023-06-01" \
741 --header "x-api-key: $ANTHROPIC_API_KEY" \
742 | jq -r '"\(.result.type): \(.custom_id)"'
727 --header "x-api-key: $ANTHROPIC_API_KEY"
743728 ```
744729 
745730 ```bash CLI
746 # Prints one line per result, e.g. `{"custom_id":"test-1","type":"succeeded",…}`.
731 # Prints one result per line, e.g. `{"custom_id":"test-1","result":{"type":"succeeded",…}}`.
747732 # For per-result handling (retries, validation errors), use the SDK
748733 # examples in the other tabs.
749734 ant messages:batches results \
750735 --message-batch-id msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d \
751 --transform '{custom_id,"type":result.type,"error":result.error.error.type}' \
752736 --format jsonl
753737 ```
754738 
from line 941
957941 
958942<CodeGroup>
959943 ```bash cURL
960 #!/bin/sh
961 # ...
962944 curl --request POST https://api.anthropic.com/v1/messages/batches/$MESSAGE_BATCH_ID/cancel \
963945 --header "x-api-key: $ANTHROPIC_API_KEY" \
964946 --header "anthropic-version: 2023-06-01"
from line 947
965947 ```
966948 
967949 ```bash CLI
968 #!/bin/bash
969 # ...
970950 ant messages:batches cancel --message-batch-id "$MESSAGE_BATCH_ID"
971951 ```
972952 

build-with-claude/cache-diagnostics Changed · +14 / -22 lines

from line 30
3030<CodeGroup>
3131 ```bash cURL
3232 # Turn 1: establish the cache and opt in to diagnostics
33 response=$(curl -sS --fail-with-body https://api.anthropic.com/v1/messages \
33 curl -sS --fail-with-body https://api.anthropic.com/v1/messages \
3434 -H "x-api-key: $ANTHROPIC_API_KEY" \
3535 -H "anthropic-version: 2023-06-01" \
3636 -H "content-type: application/json" \
from line 41
4141 "system": "You are an AI assistant analyzing a large document. <document>...</document>",
4242 "messages": [{"role": "user", "content": "Summarize section 1."}],
4343 "diagnostics": {"previous_message_id": null}
44 }')
45 jq '{id, diagnostics}' <<< "$response"
46 message_id=$(jq -r '.id' <<< "$response")
44 }'
4745 
48 # Turn 2: reference the previous turn so the API can compare prefixes
46 # Turn 2: pass the id from the turn 1 response as previous_message_id, so the API can compare prefixes.
47 # In the response, diagnostics: null means no divergence was found.
4948 curl -sS --fail-with-body https://api.anthropic.com/v1/messages \
5049 -H "x-api-key: $ANTHROPIC_API_KEY" \
5150 -H "anthropic-version: 2023-06-01" \
from line 60
6160 {"role": "user", "content": "Now summarize section 2."}
6261 ],
6362 "diagnostics": {"previous_message_id": "'"$message_id"'"}
64 }' | jq '{id, diagnostics}' # diagnostics: null means no divergence was found
63 }'
6564 ```
6665 
6766 ```bash CLI
68 # Turn 1
69 turn1=$(ant beta:messages create \
70 --transform '{id,usage,diagnostics}' <<'YAML'
67 # Turn 1: establish the cache and opt in to diagnostics
68 ant beta:messages create <<'YAML'
7169 model: claude-opus-5-5
7270 max_tokens: 1024
7371 cache_control:
from line 77
7977 diagnostics:
8078 previous_message_id: null
8179 YAML
82 )
83 printf '%s\n' "$turn1"
8480 
85 # Turn 2: pass the id from turn 1 as previous_message_id
86 message_id=$(jq -r '.id' <<<"$turn1")
87 ant beta:messages create \
88 --transform '{id,usage,diagnostics}' <<YAML
81 # Turn 2: pass the id from the turn 1 response as previous_message_id
82 ant beta:messages create <<YAML
8983 model: claude-opus-5-5
9084 max_tokens: 1024
9185 cache_control:
from line 417
423417 {"role": "user", "content": "Now summarize section 2."}
424418 ],
425419 "diagnostics": {"previous_message_id": "'"$message_id"'"}
426 }' | jq -R 'select(startswith("data: ")) | ltrimstr("data: ") | fromjson | select(.type == "message_start") | .message.diagnostics'
420 }'
427421 ```
428422 
429423 ```bash CLI
430424 # Turn 2: stream. With --stream the CLI emits each SSE event as one JSON object.
431 # diagnostics arrives on the message_start event; pick it out with jq.
432 ant beta:messages create \
433 --stream --format jsonl <<YAML |
425 # diagnostics arrives on the message_start event.
426 ant beta:messages create --stream <<YAML
434427 model: claude-opus-5-5
435428 max_tokens: 1024
436429 cache_control:
from line 439
446439 diagnostics:
447440 previous_message_id: $message_id
448441 YAML
449 jq -c 'select(.type == "message_start") | .message | {id,usage,diagnostics}'
450442 ```
451443 
452444 ```python Python

build-with-claude/claude-in-amazon-bedrock Changed · +9 / -1 lines

from line 351
351351Use the latest version of Claude Code with Claude in Amazon Bedrock; run `claude update` to upgrade.
352352 
353353<Tip>
354 Upgrading to a newer Claude model? In Claude Code, run `/claude-api migrate` to apply model ID swaps and breaking parameter changes across your codebase. The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform. See [Migrating to a newer Claude model](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model).
354 Upgrading to a newer Claude model? The [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model) in Claude Code applies model ID swaps and breaking parameter changes across your code base:
355 
356 Let Claude Code migrate your code for you
357 
358 ```text
359 /claude-api migrate this project to the latest Claude Opus model
360 ```
361 
362 The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform.
355363</Tip>
356364 
357365## Feature support

build-with-claude/claude-in-microsoft-foundry Changed · +20 / -17 lines

from line 219
219219 --base-url https://example-resource.services.ai.azure.com/anthropic \
220220 --model claude-opus-5-5 \
221221 --max-tokens 1024 \
222 --message '{role: user, content: "Hello!"}' \
223 --transform content
222 --message '{role: user, content: "Hello!"}'
224223 ```
225224 
226225 ```python Python
from line 400
401400 
402401<CodeGroup>
403402 ```bash cURL
404 # Get Microsoft Entra ID token
405 ACCESS_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
406 
407 # Make request with token. Replace {resource} with your resource name
403 # Make a request with a Microsoft Entra ID token. Replace {resource} with your resource name
408404 curl https://{resource}.services.ai.azure.com/anthropic/v1/messages \
409405 -H "content-type: application/json" \
410 -H "Authorization: Bearer $ACCESS_TOKEN" \
406 -H "Authorization: Bearer $(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)" \
411407 -H "anthropic-version: 2023-06-01" \
412408 -d '{
413409 "model": "claude-opus-5-5",
from line 415
419415 ```
420416 
421417 ```bash CLI
422 # Get Microsoft Entra ID token
423 ACCESS_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
424 
425 # Make request with token. Replace {resource} with your resource name
418 # Make a request with a Microsoft Entra ID token. Replace {resource} with your resource name
426419 # env -u unsets ANTHROPIC_API_KEY, which the CLI would send instead of the token
427 env -u ANTHROPIC_API_KEY ANTHROPIC_AUTH_TOKEN="$ACCESS_TOKEN" ant messages create \
428 --base-url https://{resource}.services.ai.azure.com/anthropic \
429 --model claude-opus-5-5 \
430 --max-tokens 1024 \
431 --message '{role: user, content: "Hello!"}'
420 env -u ANTHROPIC_API_KEY \
421 ANTHROPIC_AUTH_TOKEN="$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)" \
422 ant messages create \
423 --base-url https://{resource}.services.ai.azure.com/anthropic \
424 --model claude-opus-5-5 \
425 --max-tokens 1024 \
426 --message '{role: user, content: "Hello!"}'
432427 ```
433428 
434429 ```python Python
from line 705
710705</Info>
711706 
712707<Tip>
713 Upgrading to a newer Claude model? In Claude Code, run `/claude-api migrate` to apply model ID swaps and breaking parameter changes across your codebase. The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform. See [Migrating to a newer Claude model](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model).
708 Upgrading to a newer Claude model? The [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model) in Claude Code applies model ID swaps and breaking parameter changes across your code base:
709 
710 Let Claude Code migrate your code for you
711 
712 ```text
713 /claude-api migrate this project to the latest Claude Opus model
714 ```
715 
716 The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform.
714717</Tip>
715718 
716719## Billing

build-with-claude/claude-on-vertex-ai Changed · +15 / -19 lines

from line 146
146146| Claude Haiku 3.5 ([deprecated](https://platform.claude.com/docs/en/about-claude/model-deprecations)) | `claude-3-5-haiku@20241022` |
147147 
148148<Tip>
149 Upgrading to a newer Claude model? In Claude Code, run `/claude-api migrate` to apply model ID swaps and breaking parameter changes across your codebase. The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform. See [Migrating to a newer Claude model](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model).
149 Upgrading to a newer Claude model? The [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model) in Claude Code applies model ID swaps and breaking parameter changes across your code base:
150 
151 Let Claude Code migrate your code for you
152 
153 ```text
154 /claude-api migrate this project to the latest Claude Opus model
155 ```
156 
157 The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform.
150158</Tip>
151159 
152160### Making requests
from line 165
157165 
158166<CodeGroup exclude="shell:CLI">
159167 ```bash cURL
160 MODEL_ID=claude-opus-5-5
161 PROJECT_ID=MY_PROJECT_ID
162 
163 curl https://aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/publishers/anthropic/models/${MODEL_ID}:rawPredict \
168 curl https://aiplatform.googleapis.com/v1/projects/MY_PROJECT_ID/locations/global/publishers/anthropic/models/claude-opus-5-5:rawPredict \
164169 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
165170 -H "Content-Type: application/json" \
166171 -d '{
from line 438
433438 
434439<CodeGroup exclude="shell:CLI">
435440 ```bash cURL
436 MODEL_ID=claude-opus-5-5
437 PROJECT_ID=MY_PROJECT_ID
438 
439 curl https://aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/publishers/anthropic/models/${MODEL_ID}:rawPredict \
441 curl https://aiplatform.googleapis.com/v1/projects/MY_PROJECT_ID/locations/global/publishers/anthropic/models/claude-opus-5-5:rawPredict \
440442 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
441443 -H "Content-Type: application/json" \
442444 -d '{
from line 623
621623 
622624<CodeGroup exclude="shell:CLI">
623625 ```bash cURL
624 MODEL_ID=claude-opus-5-5
625 LOCATION=us # Multi-region identifier: "us" or "eu"
626 PROJECT_ID=MY_PROJECT_ID
627 
628 curl https://aiplatform.${LOCATION}.rep.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/publishers/anthropic/models/${MODEL_ID}:rawPredict \
626 # The multi-region identifier, "us" or "eu", appears in the host name and in the path
627 curl https://aiplatform.us.rep.googleapis.com/v1/projects/MY_PROJECT_ID/locations/us/publishers/anthropic/models/claude-opus-5-5:rawPredict \
629628 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
630629 -H "Content-Type: application/json" \
631630 -d '{
from line 810
811810<CodeGroup exclude="shell:CLI">
812811 ```bash cURL
813812 # Specific regional endpoints support Claude Sonnet 4.6 and earlier; newer models use the global or multi-region endpoints
814 MODEL_ID=claude-sonnet-4-6
815 LOCATION=us-east5 # Specify a specific region
816 PROJECT_ID=MY_PROJECT_ID
817 
818 curl https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/publishers/anthropic/models/${MODEL_ID}:rawPredict \
813 # The region, here us-east5, appears in the host name and in the path
814 curl https://us-east5-aiplatform.googleapis.com/v1/projects/MY_PROJECT_ID/locations/us-east5/publishers/anthropic/models/claude-sonnet-4-6:rawPredict \
819815 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
820816 -H "Content-Type: application/json" \
821817 -d '{

build-with-claude/claude-platform-on-aws Changed · +11 / -5 lines

from line 366
366366New models typically launch on Claude Platform on AWS the same day as the first-party Claude API.
367367 
368368<Tip>
369 Upgrading to a newer Claude model? In Claude Code, run `/claude-api migrate` to apply model ID swaps and breaking parameter changes across your codebase. The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform. See [Migrating to a newer Claude model](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model).
369 Upgrading to a newer Claude model? The [Claude API skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model) in Claude Code applies model ID swaps and breaking parameter changes across your code base:
370 
371 Let Claude Code migrate your code for you
372 
373 ```text
374 /claude-api migrate this project to the latest Claude Opus model
375 ```
376 
377 The skill detects which cloud platform your code targets and adjusts model ID formats and feature changes for that platform.
370378</Tip>
371379 
372380## Making requests
from line 414
406414 --workspace-id "$ANTHROPIC_AWS_WORKSPACE_ID" \
407415 --model claude-sonnet-5-5 \
408416 --max-tokens 1024 \
409 --message '{role: user, content: "Hello!"}' \
410 --transform content
417 --message '{role: user, content: "Hello!"}'
411418 ```
412419 
413420 ```python Python
from line 639
632639 --model claude-sonnet-5-5 \
633640 --max-tokens 1024 \
634641 --inference-geo us \
635 --message '{role: user, content: "Hello!"}' \
636 --transform content
642 --message '{role: user, content: "Hello!"}'
637643 ```
638644 
639645 ```python Python

build-with-claude/compaction-threshold Changed · +26 / -194 lines

from line 1393
13931393 
13941394You must pass the `compaction` block back to the API on subsequent requests to continue the conversation with the shortened prompt. The simplest approach is to append the entire response content to your messages:
13951395 
1396<CodeGroup>
1397 ```bash cURL
1398 # The response content, including the compaction block, must go back to the
1399 # API as the assistant turn of the next request. Managing that message list
1400 # doesn't translate well to a one-off shell command; see the CLI and SDK
1401 # tabs for the full flow. First request:
1402 curl https://api.anthropic.com/v1/messages \
1403 -H "x-api-key: $ANTHROPIC_API_KEY" \
1404 -H "anthropic-version: 2023-06-01" \
1405 -H "anthropic-beta: compact-2026-01-12" \
1406 -H "content-type: application/json" \
1407 -d '{
1408 "model": "claude-opus-5-5",
1409 "max_tokens": 4096,
1410 "messages": [
1411 {
1412 "role": "user",
1413 "content": "Hello, Claude"
1414 }
1415 ],
1416 "context_management": {
1417 "edits": [
1418 {
1419 "type": "compact_20260112"
1420 }
1421 ]
1422 }
1423 }'
1424 ```
1396<CodeGroup exclude="shell:cURL, shell:CLI">
1397 <CodeGroupItem language="cURL">
1398 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
1399 </CodeGroupItem>
14251400 
1426 ```bash CLI
1427 ant beta:messages create \
1428 --beta compact-2026-01-12 \
1429 --transform content \
1430 --format jsonl <<'YAML' > content.json
1431 model: claude-opus-5-5
1432 max_tokens: 4096
1433 messages:
1434 - role: user
1435 content: Hello, Claude
1436 context_management:
1437 edits:
1438 - type: compact_20260112
1439 YAML
1401 <CodeGroupItem language="CLI">
1402 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
1403 </CodeGroupItem>
14401404 
1441 # After receiving a response with a compaction block, append it as the
1442 # assistant turn and continue the conversation
1443 ant beta:messages create --beta compact-2026-01-12 <<YAML
1444 model: claude-opus-5-5
1445 max_tokens: 4096
1446 messages:
1447 - role: user
1448 content: Hello, Claude
1449 - role: assistant
1450 content: $(cat content.json)
1451 - role: user
1452 content: Now add error handling
1453 context_management:
1454 edits:
1455 - type: compact_20260112
1456 YAML
1457 ```
1458 
14591405 ```python Python
14601406 client = anthropic.Anthropic()
14611407 messages = [{"role": "user", "content": "Hello, Claude"}]
from line 2330
23842330 ```
23852331 
23862332 ```bash CLI
2387 CURRENT=$(ant beta:messages count-tokens \
2388 --beta compact-2026-01-12 \
2389 --transform input_tokens \
2390 --raw-output <<'YAML'
2333 ant beta:messages count-tokens \
2334 --beta compact-2026-01-12 <<'YAML'
23912335 model: claude-opus-5-5
23922336 messages:
23932337 - role: user
from line 2340
23962340 edits:
23972341 - type: compact_20260112
23982342 YAML
2399 )
2400 
2401 ORIGINAL=$(ant beta:messages count-tokens \
2402 --beta compact-2026-01-12 \
2403 --transform context_management.original_input_tokens \
2404 --raw-output <<'YAML'
2405 model: claude-opus-5-5
2406 messages:
2407 - role: user
2408 content: Hello, Claude
2409 context_management:
2410 edits:
2411 - type: compact_20260112
2412 YAML
2413 )
2414 
2415 printf 'Current tokens: %s\n' "$CURRENT"
2416 printf 'Original tokens: %s\n' "$ORIGINAL"
24172343 ```
24182344 
24192345 ```python Python
from line 2480
25542480 
25552481Here's a complete example of a long-running conversation with compaction:
25562482 
2557<CodeGroup>
2558 ```bash cURL
2559 # curl sends individual requests; maintain the messages array in the
2560 # calling script. See the SDK tabs for the full chat() loop. Single-turn
2561 # request shape:
2562 curl https://api.anthropic.com/v1/messages \
2563 -H "x-api-key: $ANTHROPIC_API_KEY" \
2564 -H "anthropic-version: 2023-06-01" \
2565 -H "anthropic-beta: compact-2026-01-12" \
2566 -H "content-type: application/json" \
2567 -d '{
2568 "model": "claude-opus-5-5",
2569 "max_tokens": 4096,
2570 "messages": [
2571 {
2572 "role": "user",
2573 "content": "Help me build a Python web scraper"
2574 }
2575 ],
2576 "context_management": {
2577 "edits": [
2578 {
2579 "type": "compact_20260112",
2580 "trigger": {
2581 "type": "input_tokens",
2582 "value": 100000
2583 }
2584 }
2585 ]
2586 }
2587 }'
2588 ```
2483<CodeGroup exclude="shell:cURL, shell:CLI">
2484 <CodeGroupItem language="cURL">
2485 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
2486 </CodeGroupItem>
25892487 
2590 ```bash CLI
2591 # The CLI handles individual turns; maintain the messages array in the
2592 # calling script. See the SDK tabs for the full chat() loop. Single-turn
2593 # request shape:
2594 ant beta:messages create \
2595 --beta compact-2026-01-12 \
2596 --transform 'content.#(type=="text").text' \
2597 --raw-output <<'YAML'
2598 model: claude-opus-5-5
2599 max_tokens: 4096
2600 messages:
2601 - role: user
2602 content: Help me build a Python web scraper
2603 context_management:
2604 edits:
2605 - type: compact_20260112
2606 trigger:
2607 type: input_tokens
2608 value: 100000
2609 YAML
2610 ```
2488 <CodeGroupItem language="CLI">
2489 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
2490 </CodeGroupItem>
26112491 
26122492 ```python Python
26132493 client = anthropic.Anthropic()
from line 2782
29022782 
29032783Here's an example that uses `pause_after_compaction` to preserve the prior exchange and the current user message (three messages total) instead of summarizing them. It keeps their text and tool blocks unchanged, removes the `thinking` and `redacted_thinking` blocks from the preserved assistant turn, and drops that turn if nothing else is left:
29042784 
2905<CodeGroup>
2906 ```bash cURL
2907 # curl sends individual requests; maintain the messages array in the
2908 # calling script. See the SDK tabs for the full chat() loop with
2909 # pause-and-preserve handling. Single-turn request shape:
2910 curl https://api.anthropic.com/v1/messages \
2911 -H "x-api-key: $ANTHROPIC_API_KEY" \
2912 -H "anthropic-version: 2023-06-01" \
2913 -H "anthropic-beta: compact-2026-01-12" \
2914 -H "content-type: application/json" \
2915 -d '{
2916 "model": "claude-opus-5-5",
2917 "max_tokens": 4096,
2918 "messages": [
2919 {
2920 "role": "user",
2921 "content": "Help me build a Python web scraper"
2922 }
2923 ],
2924 "context_management": {
2925 "edits": [
2926 {
2927 "type": "compact_20260112",
2928 "trigger": {
2929 "type": "input_tokens",
2930 "value": 100000
2931 },
2932 "pause_after_compaction": true
2933 }
2934 ]
2935 }
2936 }'
2937 ```
2785<CodeGroup exclude="shell:cURL, shell:CLI">
2786 <CodeGroupItem language="cURL">
2787 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
2788 </CodeGroupItem>
29382789 
2939 ```bash CLI
2940 # The CLI handles individual turns; maintain the messages array in the
2941 # calling script. See the SDK tabs for the full chat() loop with
2942 # pause-and-preserve handling. Single-turn request shape:
2943 ant beta:messages create \
2944 --beta compact-2026-01-12 \
2945 --transform 'content.#(type=="text").text' \
2946 --raw-output <<'YAML'
2947 model: claude-opus-5-5
2948 max_tokens: 4096
2949 messages:
2950 - role: user
2951 content: Help me build a Python web scraper
2952 context_management:
2953 edits:
2954 - type: compact_20260112
2955 trigger:
2956 type: input_tokens
2957 value: 100000
2958 pause_after_compaction: true
2959 YAML
2960 ```
2790 <CodeGroupItem language="CLI">
2791 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
2792 </CodeGroupItem>
29612793 
29622794 ```python Python
29632795 from typing import Any

build-with-claude/context-editing Changed · +2 / -28 lines

from line 1829
18291829 ```
18301830 
18311831 ```bash CLI
1832 ORIGINAL=$(ant beta:messages count-tokens \
1833 --beta context-management-2025-06-27 \
1834 --transform context_management.original_input_tokens \
1835 --raw-output <<'YAML'
1832 ant beta:messages count-tokens \
1833 --beta context-management-2025-06-27 <<'YAML'
18361834 model: claude-opus-5-5
18371835 messages:
18381836 - role: user
from line 1845
18471845 type: tool_uses
18481846 value: 5
18491847 YAML
1850 )
1851 
1852 INPUT_TOKENS=$(ant beta:messages count-tokens \
1853 --beta context-management-2025-06-27 \
1854 --transform input_tokens --raw-output <<'YAML'
1855 model: claude-opus-5-5
1856 messages:
1857 - role: user
1858 content: Continue our conversation...
1859 context_management:
1860 edits:
1861 - type: clear_tool_uses_20250919
1862 trigger:
1863 type: input_tokens
1864 value: 30000
1865 keep:
1866 type: tool_uses
1867 value: 5
1868 YAML
1869 )
1870 
1871 printf 'Original tokens: %s\n' "$ORIGINAL"
1872 printf 'After clearing: %s\n' "$INPUT_TOKENS"
1873 printf 'Savings: %s tokens\n' "$((ORIGINAL - INPUT_TOKENS))"
18741848 ```
18751849 
18761850 ```python Python

build-with-claude/effort Changed · +3 / -6 lines

from line 39
3939 
4040## Set the effort level
4141 
42Set `output_config.effort` on the request. The following example runs one request at `medium` effort and prints the response text.
42Set `output_config.effort` on the request. The following example runs one request at `medium` effort.
4343 
4444<CodeGroup>
4545 ```bash cURL
from line 65
6565 --model claude-opus-5-5 \
6666 --max-tokens 4096 \
6767 --output-config '{effort: medium}' \
68 --message '{role: user, content: "Analyze the trade-offs between microservices and monolithic architectures"}' \
69 --transform 'content.#(type=="text").text' \
70 --raw-output
68 --message '{role: user, content: "Analyze the trade-offs between microservices and monolithic architectures"}'
7169 ```
7270 
7371 ```python Python
from line 405
407405 ```
408406 
409407 ```bash CLI
410 ant beta:messages create --beta mid-conversation-output-config-2026-07-01 \
411 --transform 'content.#(type=="text").text' --raw-output <<'YAML'
408 ant beta:messages create --beta mid-conversation-output-config-2026-07-01 <<'YAML'
412409 model: claude-fable-5-1
413410 max_tokens: 4096
414411 output_config:

build-with-claude/fallback-credit Changed · +8 / -62 lines

from line 46
4646 
4747The following example makes a request that may be refused and redeems the credit token on a retry against Claude Opus 4.8. When a retry attempt is rejected, the example degrades through the rejection ladder: the sequence of progressively simpler retry shapes covered in [When a retry is rejected](https://platform.claude.com/docs/en/build-with-claude/fallback-credit#when-a-retry-is-rejected).
4848 
49<CodeGroup>
50 ```bash cURL
51 # Initial request (may be refused)
52 response=$(curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
53 -H "x-api-key: $ANTHROPIC_API_KEY" \
54 -H "anthropic-version: 2023-06-01" \
55 -H "anthropic-beta: fallback-credit-2026-07-01" \
56 -H "content-type: application/json" \
57 -d '{
58 "model": "claude-fable-5",
59 "max_tokens": 1024,
60 "messages": [{"role": "user", "content": "Hello, Claude"}]
61 }')
49<CodeGroup exclude="shell:cURL, shell:CLI">
50 <CodeGroupItem language="cURL">
51 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
52 </CodeGroupItem>
6253 
63 # A refusal carries a one-time credit token in stop_details
64 token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")
65 
66 if [[ -n "${token}" ]]; then
67 # Retry on the fallback model with the credit token (same body)
68 response=$(curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
69 -H "x-api-key: $ANTHROPIC_API_KEY" \
70 -H "anthropic-version: 2023-06-01" \
71 -H "anthropic-beta: fallback-credit-2026-07-01" \
72 -H "content-type: application/json" \
73 -d "$(jq -n --arg token "${token}" '{
74 model: "claude-opus-4-8",
75 max_tokens: 1024,
76 messages: [{"role": "user", "content": "Hello, Claude"}],
77 fallback_credit_token: $token
78 }')")
79 fi
80 
81 # See the SDK examples for the full rejection-handling ladder.
82 jq -c '{stop_reason, model}' <<<"${response}"
83 ```
84 
85 ```bash CLI
86 # Initial request (may be refused)
87 response=$(ant beta:messages create \
88 --model claude-fable-5 \
89 --max-tokens 1024 \
90 --message '{"role":"user","content":"Hello, Claude"}' \
91 --beta fallback-credit-2026-07-01 \
92 --format json)
93 
94 # A refusal carries a one-time credit token in stop_details
95 token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")
96 
97 if [[ -n "${token}" ]]; then
98 # Retry on the fallback model with the credit token
99 response=$(ant beta:messages create \
100 --model claude-opus-4-8 \
101 --max-tokens 1024 \
102 --message '{"role":"user","content":"Hello, Claude"}' \
103 --fallback-credit-token "${token}" \
104 --beta fallback-credit-2026-07-01 \
105 --format json)
106 fi
107 
108 # See the SDK examples for the full rejection-handling ladder.
109 jq -c '{stop_reason, model}' <<<"${response}"
110 ```
54 <CodeGroupItem language="CLI">
55 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
56 </CodeGroupItem>
11157 
11258 ```python Python
11359 client = Anthropic()

build-with-claude/fast-mode Changed · +43 / -75 lines

## Supported platforms ## Model availability ### Claude Opus 5.5 ### Claude Opus 5 and Claude Opus 4.8 ## Retries and fallbacks ## Supported models and platforms ## Retries and fallback

from line 2
22title: Fast mode
33url: https://platform.claude.com/docs/en/build-with-claude/fast-mode
44description: Get up to 2.5x higher output tokens per second from supported Claude models.
5featureMetadata:
6 zdr:
7 eligibility: eligible
8 note: Excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements).
9 supportedModels:
10 - claude-opus-5-5
11 - claude-opus-5
12 - claude-opus-4-8
13 supportedPlatforms:
14 Claude API:
15 availability: ga
16 note: Includes [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview).
17 Microsoft Foundry:
18 availability: ga
19 note: Available for Claude Opus 5.5 on deployments hosted on Azure, not on deployments [hosted on Anthropic](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options).
20 details:
21 - Fast mode on Claude Opus 5 and Claude Opus 4.8 is in research preview and needs the `fast-mode-2026-02-01` beta header.
522---
623 
7Fast mode delivers up to 2.5x higher output tokens per second from Claude Opus 5.5, Claude Opus 5, and Claude Opus 4.8 at premium pricing. Set `speed: "fast"` on your request to opt in.
24Fast mode delivers up to 2.5x higher output tokens per second at premium pricing. Set `speed: "fast"` on your request to opt in. Fast mode is available on Claude Opus 5.5 and in research preview on Claude Opus 5 and Claude Opus 4.8.
825 
9<Note>
10 Fast mode for Claude Opus 5.5 doesn't need the `fast-mode-2026-02-01` beta header or an access request. Requests that still send the header keep working. Fast mode for Claude Opus 5 and Claude Opus 4.8 is in research preview: contact your account manager or [join the waitlist](https://claude.com/fast-mode) to request access, and send the [beta header](https://platform.claude.com/docs/en/api/beta-headers) on your requests.
11</Note>
12 
13<Note>
14 To learn how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
15</Note>
16 
17## Supported models and platforms
18 
19Fast mode is supported on the following models and platforms:
20 
21| Supported model | Supported platforms |
22| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23| Claude Opus 5.5 (claude-opus-5-5) | Claude API, including [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview)<br />[Claude in Microsoft Foundry](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry), for deployments hosted on Azure |
24| Claude Opus 5 (claude-opus-5) | Claude API, including [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) |
25| Claude Opus 4.8 (claude-opus-4-8) | Claude API, including [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) |
26 
27If you were using fast mode on an earlier model, [migrate to a model that supports fast mode](https://platform.claude.com/docs/en/models/opus-5-5/migration-guide).
28 
29Fast mode is not available for Claude in Microsoft Foundry deployments [hosted on Anthropic](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options).
30 
3126## How fast mode works
3227 
3328Fast mode runs the same model with a faster inference configuration. There is no change to intelligence or capabilities.
from line 29
3429 
3530* Up to 2.5x higher output tokens per second compared to standard speed
3631* Speed benefits are focused on output tokens per second (OTPS), not time to first token (TTFT)
37* Same model weights and behavior (not a different model)
3832* Compatible with [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming), where the OTPS gain is most visible
3933 
4034## Basic usage
4135 
42Models in research preview also need the `fast-mode-2026-02-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers).
36Fast mode works only on supported models. See [Model availability](https://platform.claude.com/docs/en/build-with-claude/fast-mode#supported-models) for details, including which models need a beta header.
4337 
4438<CodeGroup>
4539 ```bash cURL
from line 195
201195 ```
202196</CodeGroup>
203197 
198## Supported platforms
199 
200Fast mode is available on the Claude API, including [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview).
201 
202Fast mode on Claude Opus 5.5 is also available on [Claude in Microsoft Foundry](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry) for deployments hosted on Azure. It is not available for deployments [hosted on Anthropic](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options).
203 
204## Model availability
205 
206Fast mode is available only on the following models. If you were using fast mode on an earlier model, [migrate to a model that supports fast mode](https://platform.claude.com/docs/en/models/opus-5-5/migration-guide).
207 
208### Claude Opus 5.5
209 
210Fast mode is available on Claude Opus 5.5. It doesn't need a beta header or an access request.
211 
212### Claude Opus 5 and Claude Opus 4.8
213 
214Fast mode on Claude Opus 5 and Claude Opus 4.8 is in research preview. Requests that set `speed` must send the `fast-mode-2026-02-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers); without it, they return a `400` error. To access fast mode on Claude Opus 5 or Claude Opus 4.8, contact your account manager or [join the waitlist](https://claude.com/fast-mode).
215 
204216## Pricing
205217 
206Fast mode is priced at a multiplier on standard rates across the full context window, including requests over 200k input tokens. The following table shows fast mode pricing for the supported models:
218Fast mode is priced at a multiplier on standard rates across the full context window, including requests over 200k input tokens.
207219 
208220| Model | Input | Output |
209221| ------------------------------- | -------------- | -------------- |
from line 250
238250 
239251## Checking which speed was used
240252 
241The response `usage` object includes a `speed` field that indicates which speed was used, either `"fast"` or `"standard"`. Requesting `speed: "fast"` on a [model that doesn't support fast mode](https://platform.claude.com/docs/en/build-with-claude/fast-mode#supported-models-and-platforms) returns an error, and so does exceeding fast mode's rate limits or capacity (a `429` or `529`). When a request with `speed: "fast"` succeeds, `usage.speed` is `"fast"`. Claude Opus 4.6 is an exception: instead of returning an error, a request with `speed: "fast"` and the `fast-mode-2026-02-01` beta header succeeds, but runs at standard speed. This behavior is correctly reflected in `usage.speed`, which shows `"standard"` in this situation.
253The response `usage` object includes a `speed` field that indicates which speed was used, either `"fast"` or `"standard"`. Requesting `speed: "fast"` on a [model that doesn't support fast mode](https://platform.claude.com/docs/en/build-with-claude/fast-mode#supported-models) returns an error, and so does exceeding fast mode's rate limits or capacity (a `429` or `529`). When a request with `speed: "fast"` succeeds, `usage.speed` is `"fast"`. Claude Opus 4.6 is an exception: instead of returning an error, a request with `speed: "fast"` and the `fast-mode-2026-02-01` beta header succeeds, but runs at standard speed. This behavior is correctly reflected in `usage.speed`, which shows `"standard"` in this situation.
242254 
243255<CodeGroup>
244256 ```bash cURL
from line 267
255267 ```
256268 
257269 ```bash CLI
258 ant messages create \
259 --transform usage.speed \
260 --raw-output <<'YAML'
270 ant messages create <<'YAML'
261271 model: claude-opus-5-5
262272 max_tokens: 1024
263273 speed: fast
from line 391
381391 
382392To track fast mode usage and costs across your organization, see the [Usage and Cost API](https://platform.claude.com/docs/en/manage-claude/usage-cost-api).
383393 
384## Retries and fallback
394## Retries and fallbacks
385395 
386396### Automatic retries
387397 
388When fast mode rate limits are exceeded, the API returns a `429` error with a `retry-after` header. The SDK automatically retries these requests up to 2 times by default (configurable with `max_retries` (typescript, java, php: `maxRetries`; csharp: `MaxRetries`; go: `option.WithMaxRetries`)), waiting for the server-specified delay before each retry. Because fast mode uses continuous token replenishment, the `retry-after` delay is typically short and requests succeed once capacity is available.
398The SDK automatically retries fast mode `429` errors up to 2 times by default (configurable with `max_retries` (typescript, java, php: `maxRetries`; csharp: `MaxRetries`; go: `option.WithMaxRetries`)), waiting for the delay in the `retry-after` header before each retry. Because fast mode uses continuous token replenishment, the `retry-after` delay is typically short and requests succeed once capacity is available.
389399 
390400### Falling back to standard speed
391401 
from line 407
397407 
398408Because setting `max_retries` (typescript, java, php: `maxRetries`; csharp: `MaxRetries`; go: `option.WithMaxRetries`) to `0` also disables retries for other transient errors (overloaded, internal server errors), the following examples reissue the original request with default retries for those cases.
399409 
400<CodeGroup exclude="shell:cURL">
401 ```bash CLI
402 # `ant` retries 429/5xx automatically and has no per-request max_retries
403 # override, so on a fast-mode 429 the fallback runs after the built-in
404 # retries exhaust. --transform-error surfaces error.type for branching.
405 create_message_with_fast_fallback() {
406 local speed="$1" max_attempts="${2:-3}" body out
407 body=${3:-$(cat)}
408 out=$(
409 ant messages create \
410 ${speed:+--speed "$speed"} \
411 --transform-error error.type --format-error yaml <<<"$body" 2>/dev/null
412 ) && { printf '%s\n' "$out"; return; }
413 case "$out" in
414 rate_limit_error)
415 if [[ -n "$speed" ]]; then
416 create_message_with_fast_fallback "" "$max_attempts" "$body"
417 return
418 fi ;;
419 overloaded_error | api_error | "")
420 if (( max_attempts > 1 )); then
421 create_message_with_fast_fallback "$speed" $((max_attempts - 1)) "$body"
422 return
423 fi ;;
424 esac
425 printf '%s\n' "${out:-connection_error}" >&2
426 return 1
427 }
428 
429 MESSAGE=$(
430 create_message_with_fast_fallback fast <<'YAML'
431 model: claude-opus-5-5
432 max_tokens: 1024
433 messages:
434 - role: user
435 content: Hello
436 YAML
437 )
438 ```
439 
410<CodeGroup exclude="shell:cURL, shell:CLI">
440411 ```python Python
441412 client = anthropic.Anthropic()
442413 
from line 691
720691 
721692## Considerations
722693 
723* **Prompt caching:** Switching between fast and standard speed invalidates the prompt cache. Requests at different speeds do not share cached prefixes.
724* **TTFT:** Fast mode's benefits are focused on output tokens per second (OTPS), not time to first token (TTFT).
725694* **Batch API:** Fast mode is not available with the [Batch API](https://platform.claude.com/docs/en/build-with-claude/batch-processing).
726695* **Priority Tier:** Fast mode is not available with a [Priority Tier](https://platform.claude.com/docs/en/api/service-tiers) commitment.
727* **Claude Platform on AWS:** Fast mode is not currently available on [Claude Platform on AWS](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws).
728696 
729697## Next steps
730698 

build-with-claude/files Changed · +4 / -11 lines

from line 42
4242 
4343<CodeGroup>
4444 ```bash cURL
45 FILE_ID=$(curl -X POST https://api.anthropic.com/v1/files \
45 curl -X POST https://api.anthropic.com/v1/files \
4646 -H "x-api-key: $ANTHROPIC_API_KEY" \
4747 -H "anthropic-version: 2023-06-01" \
48 -F "file=@/path/to/document.pdf" | jq -r '.id')
49 echo "$FILE_ID"
48 -F "file=@/path/to/document.pdf"
5049 ```
5150 
5251 ```bash CLI
53 FILE_ID=$(ant files upload \
54 --file /path/to/document.pdf \
55 --transform id \
56 --raw-output)
57 echo "$FILE_ID"
52 ant files upload --file /path/to/document.pdf
5853 ```
5954 
6055 ```python Python
from line 466
471466 # The "@./path" reference inlines the file contents directly into the field.
472467 ant messages create \
473468 --model claude-opus-5-5 \
474 --max-tokens 1024 \
475 --transform 'content.#(type=="text").text' \
476 --raw-output <<'YAML'
469 --max-tokens 1024 <<'YAML'
477470 messages:
478471 - role: user
479472 content:

build-with-claude/handling-stop-reasons Changed · +27 / -69 lines

from line 61
6161 "model": "claude-opus-5-5",
6262 "max_tokens": 1024,
6363 "messages": [{"role": "user", "content": "Hello!"}]
64 }' | jq 'if .stop_reason == "end_turn" then (.content[] | select(.type == "text") | .text) else . end'
64 }'
6565 ```
6666 
6767 ```bash CLI
from line 68
6868 ant messages create \
6969 --model claude-opus-5-5 \
7070 --max-tokens 1024 \
71 --message '{role: user, content: "Hello!"}' \
72 --format json | jq 'if .stop_reason == "end_turn" then (.content[] | select(.type == "text") | .text) else . end'
71 --message '{role: user, content: "Hello!"}'
7372 ```
7473 
7574 ```python Python
from line 753
754753 "model": "claude-opus-5-5",
755754 "max_tokens": 10,
756755 "messages": [{"role": "user", "content": "Explain quantum physics"}]
757 }' | jq '.stop_reason'
756 }'
758757 ```
759758 
760759 ```bash CLI
from line 760
761760 ant messages create \
762761 --model claude-opus-5-5 \
763762 --max-tokens 10 \
764 --message '{role: user, content: "Explain quantum physics"}' \
765 --format json | jq '.stop_reason'
763 --message '{role: user, content: "Explain quantum physics"}'
766764 ```
767765 
768766 ```python Python
from line 893
895893<Accordion title="Incomplete tool use blocks">
896894 If Claude's response is cut off because it hit the `max_tokens` limit, and the truncated response contains an incomplete tool use block, you'll need to retry the request with a higher `max_tokens` value to get the full tool use.
897895 
898 <CodeGroup exclude="shell:cURL">
899 ```bash CLI
900 RESPONSE=$(ant messages create --max-tokens 1024 --format jsonl <<'YAML'
901 model: claude-opus-5-5
902 tools:
903 - name: get_weather
904 description: Get the current weather in a given location
905 input_schema:
906 type: object
907 properties:
908 location:
909 type: string
910 required:
911 - location
912 messages:
913 - role: user
914 content: What is the weather in San Francisco?
915 YAML
916 )
896 <CodeGroup exclude="shell:cURL, shell:CLI">
897 <CodeGroupItem language="cURL">
898 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
899 </CodeGroupItem>
917900 
918 # Check if the response was truncated mid tool use
919 STOP_REASON=$(jq -r '.stop_reason' <<<"$RESPONSE")
920 LAST_TYPE=$(jq -r '.content[-1].type' <<<"$RESPONSE")
921 if [ "$STOP_REASON" = "max_tokens" ] && [ "$LAST_TYPE" = "tool_use" ]; then
922 # Retry with a higher max_tokens
923 ant messages create --max-tokens 4096 <<'YAML'
924 model: claude-opus-5-5
925 tools:
926 - name: get_weather
927 description: Get the current weather in a given location
928 input_schema:
929 type: object
930 properties:
931 location:
932 type: string
933 required:
934 - location
935 messages:
936 - role: user
937 content: What is the weather in San Francisco?
938 YAML
939 fi
940 ```
901 <CodeGroupItem language="CLI">
902 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
903 </CodeGroupItem>
941904 
942905 ```python Python
943906 # Check if response was truncated during tool use
from line 1066
11031066 "max_tokens": 1024,
11041067 "stop_sequences": ["END", "STOP"],
11051068 "messages": [{"role": "user", "content": "Generate text until you say END"}]
1106 }' | jq '{stop_reason, stop_sequence}'
1069 }'
11071070 ```
11081071 
11091072 ```bash CLI
from line 1074
11111074 --model claude-opus-5-5 \
11121075 --max-tokens 1024 \
11131076 --stop-sequence END --stop-sequence STOP \
1114 --message '{role: user, content: "Generate text until you say END"}' \
1115 --format json | jq '{stop_reason, stop_sequence}'
1077 --message '{role: user, content: "Generate text until you say END"}'
11161078 ```
11171079 
11181080 ```python Python
from line 1218
12561218 }
12571219 }],
12581220 "messages": [{"role": "user", "content": "What is the weather in San Francisco?"}]
1259 }' | jq '.stop_reason, (.content[] | select(.type == "tool_use"))'
1221 }'
12601222 ```
12611223 
12621224 ```bash CLI
1263 ant messages create --format json <<'YAML' | jq '.stop_reason, (.content[] | select(.type == "tool_use"))'
1225 ant messages create <<'YAML'
12641226 model: claude-opus-5-5
12651227 max_tokens: 1024
12661228 messages:
from line 1553
15911553 "max_tokens": 4096,
15921554 "tools": [{"type": "web_search_20250305", "name": "web_search"}],
15931555 "messages": [{"role": "user", "content": "Search for latest AI news"}]
1594 }' | jq '{stop_reason, content}'
1556 }'
15951557 ```
15961558 
15971559 ```bash CLI
15981560 # Inspect stop_reason; if it is pause_turn, re-run with the assistant
15991561 # response appended to --message.
1600 ant messages create --format json <<'YAML' | jq '{stop_reason, content}'
1562 ant messages create <<'YAML'
16011563 model: claude-opus-5-5
16021564 max_tokens: 4096
16031565 tools:
from line 1771
18091771 "model": "claude-opus-5-5",
18101772 "max_tokens": 1024,
18111773 "messages": [{"role": "user", "content": "[Unsafe request]"}]
1812 }' | jq '{stop_reason, stop_details}'
1774 }'
18131775 ```
18141776 
18151777 ```bash CLI
from line 1778
18161778 ant messages create \
18171779 --model claude-opus-5-5 \
18181780 --max-tokens 1024 \
1819 --message '{role: user, content: "[Unsafe request]"}' \
1820 --format json | jq '{stop_reason, stop_details}'
1781 --message '{role: user, content: "[Unsafe request]"}'
18211782 ```
18221783 
18231784 ```python Python
from line 1927
19661927 "model": "claude-opus-5-5",
19671928 "max_tokens": 20000,
19681929 "messages": [{"role": "user", "content": "Large input that uses most of context window..."}]
1969 }' | jq '.stop_reason'
1930 }'
19701931 ```
19711932 
19721933 ```bash CLI
from line 1934
19731934 ant messages create \
19741935 --model claude-opus-5-5 \
19751936 --max-tokens 20000 \
1976 --message '{role: user, content: "Large input that uses most of context window..."}' \
1977 --format json | jq '.stop_reason'
1937 --message '{role: user, content: "Large input that uses most of context window..."}'
19781938 ```
19791939 
19801940 ```python Python
from line 2613
26532613 ```bash cURL
26542614 # cURL exits non-zero on HTTP errors with --fail-with-body; inspect
26552615 # $? for errors and stop_reason for successful responses.
2656 curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
2616 curl --fail-with-body https://api.anthropic.com/v1/messages \
26572617 -H "x-api-key: $ANTHROPIC_API_KEY" \
26582618 -H "anthropic-version: 2023-06-01" \
26592619 -H "content-type: application/json" \
from line 2621
26612621 "model": "claude-opus-5-5",
26622622 "max_tokens": 1024,
26632623 "messages": [{"role": "user", "content": "Hello!"}]
2664 }' | jq '.stop_reason'
2624 }'
26652625 ```
26662626 
26672627 ```bash CLI
from line 2629
26692629 ant messages create \
26702630 --model claude-opus-5-5 \
26712631 --max-tokens 1024 \
2672 --message '{role: user, content: "Hello!"}' \
2673 --format json | jq '.stop_reason'
2632 --message '{role: user, content: "Hello!"}'
26742633 ```
26752634 
26762635 ```python Python
from line 2841
28822841 
28832842 ```bash CLI
28842843 # stop_reason appears in the message_delta event.
2885 ant messages create --stream --format jsonl \
2844 ant messages create --stream \
28862845 --model claude-opus-5-5 \
28872846 --max-tokens 1024 \
2888 --message '{role: user, content: "Hello!"}' |
2889 jq -c 'select(.type == "message_delta") | .delta.stop_reason'
2847 --message '{role: user, content: "Hello!"}'
28902848 ```
28912849 
28922850 ```python Python

build-with-claude/mid-conversation-system-messages Changed · +4 / -6 lines

from line 74
7474 ```
7575 
7676 ```bash CLI
77 ant beta:messages create --beta inline-tools-2026-09-15 \
78 --transform 'content.#(type=="text").text' --raw-output <<'YAML'
77 ant beta:messages create --beta inline-tools-2026-09-15 <<'YAML'
7978 model: claude-opus-5-5
8079 max_tokens: 1024
8180 tools:
from line 454
455454 
456455### Define tools in a message (beta)
457456 
458With the `inline-tools-2026-09-15` beta header, a `tool_addition` block can define a tool by value, carrying its full definition, instead of naming it by reference. This lets you introduce a tool that is unknown at the start of the conversation, or whose schema changes later, by appending a `role: "system"` message. The `tools` array and every earlier message stay exactly as sent, so the prompt cache still hits and only the appended message is processed as new input. The one exception, a `tools` array with no non-deferred tool, is covered in the rules below. The header also covers adding and removing tools by reference, so you don't need to send `mid-conversation-tool-changes-2026-07-01` as well.
457With the `inline-tools-2026-09-15` beta header, a `tool_addition` block can define a tool by value, carrying its full definition, instead of naming it by reference. This lets you introduce a tool that is unknown at the start of the conversation, or whose schema changes later, by appending a `role: "system"` message. The `tools` array and every earlier message stay exactly as sent, so the prompt cache still hits and only the appended message is processed as new input. The one exception, a `tools` array with no non-deferred tool, is covered in the rules that follow. The header also covers adding and removing tools by reference, so you don't need to send `mid-conversation-tool-changes-2026-07-01` as well.
459458 
460459Wrap the definition in a `tool` object of type `tool_definition`. The `definition` is a `tools` entry, such as a custom tool or an Anthropic-defined client or server tool, with its usual configuration, including `cache_control` and `defer_loading`. During the beta, some tool types (the computer use tool among them) can't be defined in a message yet and return a 400 error that says so; declare those in `tools` and add them by reference. For example, to define a custom tool mid-conversation:
461460 
from line 1676
16771676 ```
16781677 
16791678 ```bash CLI
1680 ant messages create --transform 'content.#(type=="text").text' --raw-output <<'YAML'
1679 ant messages create <<'YAML'
16811680 model: claude-opus-5-5
16821681 max_tokens: 1024
16831682 cache_control:
from line 2138
21392138 ```
21402139 
21412140 ```bash CLI
2142 ant beta:messages create --beta mid-conversation-system-clear-at-2026-08-21 \
2143 --transform 'content.#(type=="text").text' --raw-output <<'YAML'
2141 ant beta:messages create --beta mid-conversation-system-clear-at-2026-08-21 <<'YAML'
21442142 model: claude-fable-5-1
21452143 max_tokens: 4096
21462144 messages:

build-with-claude/pdf-support Changed · +32 / -47 lines

from line 133
133133 ```
134134 
135135 ```bash CLI
136 ant messages create --transform content --format yaml <<'YAML'
136 ant messages create <<'YAML'
137137 model: claude-opus-5-5
138138 max_tokens: 1024
139139 messages:
from line 414
414414 ```bash CLI
415415 ant messages create \
416416 --model claude-opus-5-5 \
417 --max-tokens 1024 \
418 --transform content \
419 --format yaml <<'YAML'
417 --max-tokens 1024 <<'YAML'
420418 messages:
421419 - role: user
422420 content:
from line 714
716714<CodeGroup>
717715 ```bash cURL
718716 # First, upload your PDF to the Files API
719 FILE_ID=$(curl -sS -X POST https://api.anthropic.com/v1/files \
717 curl -sS -X POST https://api.anthropic.com/v1/files \
720718 -H "x-api-key: $ANTHROPIC_API_KEY" \
721719 -H "anthropic-version: 2023-06-01" \
722 -F "[email protected]" | jq -r '.id')
723721 
724 # Then use the returned file_id in your message
722 # Then use the file_id from the upload response in your message
725723 curl https://api.anthropic.com/v1/messages \
726724 -H "content-type: application/json" \
727725 -H "x-api-key: $ANTHROPIC_API_KEY" \
from line 746
748746 
749747 ```bash CLI
750748 # First, upload your PDF to the Files API
751 FILE_ID=$(ant files upload \
752 --file ./document.pdf \
753 --transform id \
754 --raw-output)
749 ant files upload \
750 --file ./document.pdf
755751 
756 # Then use the returned file_id in your message
757 ant messages create \
758 --transform content \
759 --format yaml <<YAML
752 # Then use the file_id from the upload response in your message
753 ant messages create <<YAML
760754 model: claude-opus-5-5
761755 max_tokens: 1024
762756 messages:
from line 1065
10711065 
10721066<CodeGroup>
10731067 ```bash cURL
1074 curl -sL "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf" | base64 | tr -d '\n' > pdf_base64.txt
1075 # Create a JSON request file using the pdf_base64.txt content
1076 jq -n --rawfile PDF_BASE64 pdf_base64.txt '{
1077 "model": "claude-opus-5-5",
1078 "max_tokens": 1024,
1079 "messages": [{
1080 "role": "user",
1081 "content": [{
1082 "type": "document",
1083 "source": {
1084 "type": "base64",
1085 "media_type": "application/pdf",
1086 "data": $PDF_BASE64
1087 },
1088 "cache_control": {
1089 "type": "ephemeral"
1090 }
1091 },
1068 curl -sL "https://assets.anthropic.com/m/1cd9d098ac3e6467/original/Claude-3-Model-Card-October-Addendum.pdf" |
1069 base64 | tr -d '\n' |
1070 jq -Rs '{
1071 model: "claude-opus-5-5",
1072 max_tokens: 1024,
1073 messages: [{
1074 role: "user",
1075 content: [
10921076 {
1093 "type": "text",
1094 "text": "Which model has the highest human preference win rates across each use-case?"
1095 }]
1077 type: "document",
1078 source: {type: "base64", media_type: "application/pdf", data: .},
1079 cache_control: {type: "ephemeral"}
1080 },
1081 {type: "text", text: "Which model has the highest human preference win rates across each use-case?"}
1082 ]
10961083 }]
1097 }' > request.json
1098 
1099 # Then make the API call using the JSON file
1100 curl https://api.anthropic.com/v1/messages \
1101 -H "content-type: application/json" \
1102 -H "x-api-key: $ANTHROPIC_API_KEY" \
1103 -H "anthropic-version: 2023-06-01" \
1104 -d @request.json
1084 }' |
1085 curl https://api.anthropic.com/v1/messages \
1086 -H "content-type: application/json" \
1087 -H "x-api-key: $ANTHROPIC_API_KEY" \
1088 -H "anthropic-version: 2023-06-01" \
1089 -d @-
11051090 ```
11061091 
11071092 ```bash CLI
1108 ant messages create --transform content --format yaml <<'YAML'
1093 ant messages create <<'YAML'
11091094 model: claude-opus-5-5
11101095 max_tokens: 1024
11111096 messages:

build-with-claude/preserved-thinking Changed · +25 / -224 lines

from line 457
457457 
458458Then confirm against the API. Add the `thinking-binding-controls-2026-08-01` beta header, set `prefix_mismatch_behavior` to `"drop_block"`, and run a normal multi-turn session through your integration on claude-fable-5-1. The following example runs two turns the way your integration should: `messages` only grows, each assistant turn goes back exactly as the API returned it, `thinking` blocks included, and `block_binding` is set on every request. After each turn it prints the number of `thinking` blocks in the response and the number of dropped blocks:
459459 
460<CodeGroup>
461 ```bash cURL
462 # Counts the thinking blocks in a response and the blocks the API dropped
463 COUNTS='"thinking blocks: \([.content[] | select(.type == "thinking")] | length), " +
464 "dropped: \(.input_transformations | length)"'
460<CodeGroup exclude="shell:cURL, shell:CLI">
461 <CodeGroupItem language="cURL">
462 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
463 </CodeGroupItem>
465464 
466 FIRST=$(curl -s https://api.anthropic.com/v1/messages \
467 -H "content-type: application/json" \
468 -H "x-api-key: $ANTHROPIC_API_KEY" \
469 -H "anthropic-version: 2023-06-01" \
470 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
471 -d '{
472 "model": "claude-fable-5-1",
473 "max_tokens": 16000,
474 "thinking": {
475 "type": "adaptive",
476 "block_binding": { "prefix_mismatch_behavior": "drop_block" }
477 },
478 "messages": [
479 {
480 "role": "user",
481 "content": "How many positive integers below 500 have exactly 6 positive divisors?"
482 }
483 ]
484 }')
485 echo "$FIRST" | jq -r "$COUNTS"
465 <CodeGroupItem language="CLI">
466 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
467 </CodeGroupItem>
486468 
487 # Turn 2: the assistant turn goes back exactly as returned, then the next user message
488 MESSAGES=$(jq -n --argjson first "$FIRST" '[
489 {
490 role: "user",
491 content: "How many positive integers below 500 have exactly 6 positive divisors?"
492 },
493 { role: "assistant", content: $first.content },
494 { role: "user", content: "How many of those are odd?" }
495 ]')
496 
497 jq -n --argjson messages "$MESSAGES" '{
498 model: "claude-fable-5-1",
499 max_tokens: 16000,
500 thinking: {
501 type: "adaptive",
502 block_binding: { prefix_mismatch_behavior: "drop_block" }
503 },
504 messages: $messages
505 }' | curl -s https://api.anthropic.com/v1/messages \
506 -H "content-type: application/json" \
507 -H "x-api-key: $ANTHROPIC_API_KEY" \
508 -H "anthropic-version: 2023-06-01" \
509 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
510 -d @- | jq -r "$COUNTS"
511 ```
512 
513 ```bash CLI
514 # Counts the thinking blocks in a response and the blocks the API dropped
515 COUNTS='"thinking blocks: \([.content[] | select(.type == "thinking")] | length), " +
516 "dropped: \(.input_transformations | length)"'
517 
518 FIRST=$(ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
519 --format json <<'YAML'
520 model: claude-fable-5-1
521 max_tokens: 16000
522 thinking:
523 type: adaptive
524 block_binding:
525 prefix_mismatch_behavior: drop_block
526 messages:
527 - role: user
528 content: How many positive integers below 500 have exactly 6 positive divisors?
529 YAML
530 )
531 echo "$FIRST" | jq -r "$COUNTS"
532 
533 # Turn 2: the assistant turn goes back exactly as returned, then the next user message
534 ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
535 --format json <<YAML | jq -r "$COUNTS"
536 model: claude-fable-5-1
537 max_tokens: 16000
538 thinking:
539 type: adaptive
540 block_binding:
541 prefix_mismatch_behavior: drop_block
542 messages:
543 - role: user
544 content: How many positive integers below 500 have exactly 6 positive divisors?
545 - role: assistant
546 content: $(echo "$FIRST" | jq -c .content)
547 - role: user
548 content: How many of those are odd?
549 YAML
550 ```
551 
552469 ```python Python
553470 client = anthropic.Anthropic()
554471 
from line 736
819736* **`reason: "model_binding_mismatch"`:** the conversation moved to a model that can't read the earlier model's blocks. This isn't a prefix edit. See [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models).
820737* **`reason: "organization_binding_mismatch"`:** the block came from another account. See [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking).
821738 
822To see a failure on purpose, send a third turn from the earlier example and add a `system` prompt to that request only, so that it differs from the first two requests, which had none. With `"drop_block"`, the dropped count is no longer 0: the response has one entry for each thinking block in the history, each with `reason: "prefix_binding_mismatch"`. With `"error"`, the request returns the 400 described in [What the API does with an invalid block](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#mismatch-behavior), and its last sentence names the `system` prompt. In the cURL and CLI tabs, remove the `jq` filter to see the error body. If the count is still 0, there was nothing to check: confirm that the model is claude-fable-5-1, that the request sets `block_binding`, that the history you sent contains `thinking` blocks, and that the first two requests had no `system` prompt.
739To see a failure on purpose, send a third turn from the earlier example and add a `system` prompt to that request only, so that it differs from the first two requests, which had none. With `"drop_block"`, the dropped count is no longer 0: the response has one entry for each thinking block in the history, each with `reason: "prefix_binding_mismatch"`. With `"error"`, the request returns the 400 described in [What the API does with an invalid block](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#mismatch-behavior), and its last sentence names the `system` prompt. If the count is still 0, there was nothing to check: confirm that the model is claude-fable-5-1, that the request sets `block_binding`, that the history you sent contains `thinking` blocks, and that the first two requests had no `system` prompt.
823740 
824741Two plain turns rarely show the problem. Run a session through each of the following, with `"error"` set so that a regression fails your CI:
825742 
from line 763
846763 
847764Run the following example from an [older account](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement), because a newer account rejects its third request with the 400. It sends the header without `block_binding` and extends the earlier session with a third request that adds a system prompt, which changes the prefix on purpose. After each turn it prints the number of `thinking` blocks and flagged blocks:
848765 
849<CodeGroup>
850 ```bash cURL
851 # Counts the thinking blocks in a response and the blocks that failed the prefix check
852 COUNTS='"thinking blocks: \([.content[] | select(.type == "thinking")] | length), " +
853 "flagged: \([.input_transformations[] |
854 select(.type == "thinking_mismatch_allowed")] | length)"'
766<CodeGroup exclude="shell:cURL, shell:CLI">
767 <CodeGroupItem language="cURL">
768 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
769 </CodeGroupItem>
855770 
856 FIRST=$(curl -s https://api.anthropic.com/v1/messages \
857 -H "content-type: application/json" \
858 -H "x-api-key: $ANTHROPIC_API_KEY" \
859 -H "anthropic-version: 2023-06-01" \
860 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
861 -d '{
862 "model": "claude-fable-5-1",
863 "max_tokens": 16000,
864 "thinking": { "type": "adaptive" },
865 "messages": [
866 {
867 "role": "user",
868 "content": "How many positive integers below 500 have exactly 6 positive divisors?"
869 }
870 ]
871 }')
872 echo "$FIRST" | jq -r "$COUNTS"
771 <CodeGroupItem language="CLI">
772 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
773 </CodeGroupItem>
873774 
874 # Turn 2: the assistant turn goes back exactly as returned, then the next user message
875 MESSAGES=$(jq -n --argjson first "$FIRST" '[
876 {
877 role: "user",
878 content: "How many positive integers below 500 have exactly 6 positive divisors?"
879 },
880 { role: "assistant", content: $first.content },
881 { role: "user", content: "How many of those are odd?" }
882 ]')
883 
884 SECOND=$(jq -n --argjson messages "$MESSAGES" '{
885 model: "claude-fable-5-1",
886 max_tokens: 16000,
887 thinking: { type: "adaptive" },
888 messages: $messages
889 }' | curl -s https://api.anthropic.com/v1/messages \
890 -H "content-type: application/json" \
891 -H "x-api-key: $ANTHROPIC_API_KEY" \
892 -H "anthropic-version: 2023-06-01" \
893 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
894 -d @-)
895 echo "$SECOND" | jq -r "$COUNTS"
896 
897 # Turn 3: only this request adds a system prompt, which changes the prefix on purpose
898 jq -n --argjson messages "$MESSAGES" --argjson second "$SECOND" '{
899 model: "claude-fable-5-1",
900 max_tokens: 16000,
901 thinking: { type: "adaptive" },
902 system: "Answer briefly.",
903 messages: ($messages + [
904 { role: "assistant", content: $second.content },
905 { role: "user", content: "And how many of the odd ones are below 100?" }
906 ])
907 }' | curl -s https://api.anthropic.com/v1/messages \
908 -H "content-type: application/json" \
909 -H "x-api-key: $ANTHROPIC_API_KEY" \
910 -H "anthropic-version: 2023-06-01" \
911 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
912 -d @- | jq -r "$COUNTS"
913 ```
914 
915 ```bash CLI
916 # Counts the thinking blocks in a response and the blocks that failed the prefix check
917 COUNTS='"thinking blocks: \([.content[] | select(.type == "thinking")] | length), " +
918 "flagged: \([.input_transformations[] |
919 select(.type == "thinking_mismatch_allowed")] | length)"'
920 
921 FIRST=$(ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
922 --format json <<'YAML'
923 model: claude-fable-5-1
924 max_tokens: 16000
925 thinking:
926 type: adaptive
927 messages:
928 - role: user
929 content: How many positive integers below 500 have exactly 6 positive divisors?
930 YAML
931 )
932 echo "$FIRST" | jq -r "$COUNTS"
933 
934 # Turn 2: the assistant turn goes back exactly as returned, then the next user message
935 SECOND=$(ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
936 --format json <<YAML
937 model: claude-fable-5-1
938 max_tokens: 16000
939 thinking:
940 type: adaptive
941 messages:
942 - role: user
943 content: How many positive integers below 500 have exactly 6 positive divisors?
944 - role: assistant
945 content: $(echo "$FIRST" | jq -c .content)
946 - role: user
947 content: How many of those are odd?
948 YAML
949 )
950 echo "$SECOND" | jq -r "$COUNTS"
951 
952 # Turn 3: only this request adds a system prompt, which changes the prefix on purpose
953 ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
954 --format json <<YAML | jq -r "$COUNTS"
955 model: claude-fable-5-1
956 max_tokens: 16000
957 thinking:
958 type: adaptive
959 system: Answer briefly.
960 messages:
961 - role: user
962 content: How many positive integers below 500 have exactly 6 positive divisors?
963 - role: assistant
964 content: $(echo "$FIRST" | jq -c .content)
965 - role: user
966 content: How many of those are odd?
967 - role: assistant
968 content: $(echo "$SECOND" | jq -c .content)
969 - role: user
970 content: And how many of the odd ones are below 100?
971 YAML
972 ```
973 
974775 ```python Python
975776 client = anthropic.Anthropic()
976777 
from line 1295
14941295 -H "x-api-key: $ANTHROPIC_API_KEY" \
14951296 -H "anthropic-version: 2023-06-01" \
14961297 -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
1497 -d "{
1498 \"model\": \"claude-fable-5-1\",
1499 \"max_tokens\": 16000,
1500 \"thinking\": {
1501 \"type\": \"adaptive\",
1502 \"block_binding\": { \"prefix_mismatch_behavior\": \"drop_block\" }
1298 -d '{
1299 "model": "claude-fable-5-1",
1300 "max_tokens": 16000,
1301 "thinking": {
1302 "type": "adaptive",
1303 "block_binding": { "prefix_mismatch_behavior": "drop_block" }
15031304 },
1504 \"messages\": $COMPACTED_MESSAGES
1505 }"
1305 "messages": '"$COMPACTED_MESSAGES"'
1306 }'
15061307 ```
15071308 
15081309 ```bash CLI

build-with-claude/prompt-caching Changed · +8 / -9 lines

from line 38
3838 ```
3939 
4040 ```bash CLI
41 ant messages create --transform usage <<'YAML'
41 ant messages create <<'YAML'
4242 model: claude-opus-5-5
4343 max_tokens: 1024
4444 cache_control:
from line 303
303303 ```
304304 
305305 ```bash CLI
306 ant messages create --transform usage <<'YAML'
306 ant messages create <<'YAML'
307307 model: claude-opus-5-5
308308 max_tokens: 1024
309309 cache_control:
from line 929
929929 ```
930930 
931931 ```bash CLI
932 ant messages create \
933 --transform '{stop_reason,content,usage}' --format yaml <<'YAML'
932 ant messages create <<'YAML'
934933 model: claude-opus-5-5
935934 max_tokens: 0
936935 system:
from line 1183
11841183 
11851184 ```bash CLI
11861185 # Warm the cache at application startup or on a scheduled interval.
1187 ant messages create --transform usage <<'YAML'
1186 ant messages create <<'YAML'
11881187 model: claude-opus-5-5
11891188 max_tokens: 0
11901189 system:
from line 1199
12001199 YAML
12011200 
12021201 # Later, when the user submits a message, the system-prompt prefix is already cached.
1203 ant messages create --transform 'content.#(type=="text").text' --raw-output <<'YAML'
1202 ant messages create <<'YAML'
12041203 model: claude-opus-5-5
12051204 max_tokens: 1024
12061205 system:
from line 1581
15821581 ```
15831582 
15841583 ```bash CLI
1585 ant messages create --transform usage <<'YAML'
1584 ant messages create <<'YAML'
15861585 model: claude-opus-5-5
15871586 max_tokens: 1024
15881587 system:
from line 1905
19061905 ```
19071906 
19081907 ```bash CLI
1909 ant messages create --transform usage <<'YAML'
1908 ant messages create <<'YAML'
19101909 model: claude-opus-5-5
19111910 max_tokens: 1024
19121911 system:
from line 2364
23652364 ```
23662365 
23672366 ```bash CLI
2368 ant messages create --transform usage <<'YAML'
2367 ant messages create <<'YAML'
23692368 model: claude-opus-5-5
23702369 max_tokens: 1024
23712370 tools:

build-with-claude/refusals-and-fallback Changed · +1 / -22 lines

from line 253
253253 "max_tokens": 1024,
254254 "fallbacks": "default",
255255 "messages": [{"role": "user", "content": "Hello, Claude"}]
256 }' |
257 jq -c '{
258 stop_reason,
259 model,
260 # A fallback_message entry in usage.iterations means a fallback model ran;
261 # pair it with stop_reason to confirm the fallback served the response.
262 served_by_fallback: (
263 any(.usage.iterations[]?; .type == "fallback_message")
264 and .stop_reason != "refusal"
265 )
266256 }'
267257 ```
268258 
from line 262
272262 --max-tokens 1024 \
273263 --message '{"role":"user","content":"Hello, Claude"}' \
274264 --fallbacks default \
275 --beta server-side-fallback-2026-07-01 \
276 --format json |
277 jq -c '{
278 stop_reason,
279 model,
280 # A fallback_message entry in usage.iterations means a fallback model ran;
281 # pair it with stop_reason to confirm the fallback served the response.
282 served_by_fallback: (
283 any(.usage.iterations[]?; .type == "fallback_message")
284 and .stop_reason != "refusal"
285 )
286 }'
265 --beta server-side-fallback-2026-07-01
287266 ```
288267 
289268 ```python Python
Feedback