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

One read of Claude Developer Platformapi-20261023T000722Z

139 pages moved out of 812 read.

Pages moved 139 significant first
Pages read 812 in this capture
Captured 00:07 UTC
Corpus hash b5a82bae26cf index-hash

What this read moved

51-75 of 139, page 3 of 6

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

build-with-claude/structured-outputs Changed · +56 / -46 lines

from line 29
2929 Claude API: ga
3030 Claude Platform on AWS: ga
3131 Amazon Bedrock: ga
32 Google Cloud: ga
32 Claude Models on Google Cloud (Google managed): ga
3333 Microsoft Foundry: ga
3434---
3535 
from line 260
260260 {
261261 Model = Model.ClaudeOpus5_5,
262262 MaxTokens = 1024,
263 Messages = [new UserMessageParam {
264 Content = "Extract the key information from this email: "
265 + "John Smith ([email protected]) is interested in our Enterprise plan "
266 + "and wants to schedule a demo for next Tuesday at 2pm."
267 }],
263 Messages = [
264 new UserMessageParam
265 {
266 Content = "Extract the key information from this email: "
267 + "John Smith ([email protected]) is interested in our Enterprise plan "
268 + "and wants to schedule a demo for next Tuesday at 2pm."
269 }
270 ],
268271 });
269272 
270273 if (response.Content.Select(block => block.Parsed()).OfType<ContactInfo>().FirstOrDefault() is { } contact)
from line 395
392395 
393396 * **Local validation** occurs without sending requests to the remote AI model.
394397 * **Remote validation** is also performed by the AI model upon receiving the JSON schema.
395 * **Version compatibility:** Local validation might fail while remote validation succeeds if the SDK version is outdated.
398 * **Version compatibility:** If the SDK version is outdated, local validation might reject a schema that remote validation accepts.
396399 * **Disabling local validation:** Pass `JsonSchemaLocalValidation.NO` if you encounter compatibility issues:
397400 
398401 ```java
from line 463
460463 
461464 The JSON output has this nested structure:
462465 
463 ```json
466 ```text Output wrap
464467 {
465468 "composedA": { "a": "hello" },
466469 "composedB": { "b": "world" }
from line 484
481484 
482485 The JSON output has this flat structure:
483486 
484 ```json
487 ```text Output wrap
485488 {
486489 "a": "hello",
487490 "b": "world"
from line 502
499502 
500503 static class Person {
501504 
502 @JsonPropertyDescription("The first name and surname of the person")
503 public String name;
505 @JsonPropertyDescription("The first name and surname of the person")
506 public String name;
504507 
505 public int birthYear;
508 public int birthYear;
506509 
507 @JsonPropertyDescription("The year the person died, or 'present' if the person is living.")
508 public String deathYear;
510 @JsonPropertyDescription("The year the person died, or 'present' if the person is living.")
511 public String deathYear;
509512 }
510513 
511514 @JsonClassDescription("The details of one published book")
512515 static class Book {
513516 
514 public String title;
515 public Person author;
517 public String title;
518 public Person author;
516519 
517 @JsonPropertyDescription("The year in which the book was first published.")
518 public int publicationYear;
520 @JsonPropertyDescription("The year in which the book was first published.")
521 public int publicationYear;
519522 
520 @JsonIgnore
521 public String genre;
523 @JsonIgnore
524 public String genre;
522525 }
523526 
524527 static class BookList {
525 public List<Book> books;
528 public List<Book> books;
526529 }
527530 ```
528531 
from line 546
543546 
544547 static class Article {
545548 
546 @ArraySchema(minItems = 1)
547 public List<String> authors;
549 @ArraySchema(minItems = 1)
550 public List<String> authors;
548551 
549 public String title;
552 public String title;
550553 
551 @Schema(format = "date")
552 public String publicationDate;
554 @Schema(format = "date")
555 public String publicationDate;
553556 
554 public int pageCount;
557 public int pageCount;
555558 }
556559 ```
557560 
from line 943
940943 {
941944 Model = Model.ClaudeOpus5_5,
942945 MaxTokens = 1024,
943 Messages = [new UserMessageParam {
944 Content = "Extract the key information from this email: "
945 + "John Smith ([email protected]) is interested in our Enterprise plan "
946 + "and wants to schedule a demo for next Tuesday at 2pm."
947 }],
946 Messages = [
947 new UserMessageParam
948 {
949 Content = "Extract the key information from this email: "
950 + "John Smith ([email protected]) is interested in our Enterprise plan "
951 + "and wants to schedule a demo for next Tuesday at 2pm."
952 }
953 ],
948954 OutputConfig = new OutputConfig
949955 {
950956 Format = new JsonOutputFormat
from line 973
967973 },
968974 });
969975 
970 if (response.Content.Select(b => b.Value).OfType<TextBlock>().FirstOrDefault() is { } textBlock)
976 if (response.Content.Select(block => block.Value).OfType<TextBlock>().FirstOrDefault() is { } textBlock)
971977 {
972978 // JSON is guaranteed to match the schema
973979 var contact = JsonSerializer.Deserialize<JsonElement>(textBlock.Text);
from line 981
975981 }
976982 ```
977983 
978 The example above outputs:
984 The previous example outputs:
979985 
980986 ```text Output wrap
981987 {"name":"John Smith","email":"[email protected]","plan_interest":"Enterprise","demo_requested":true}
from line 1071
10651071 .build();
10661072 
10671073 OutputConfig outputConfig = OutputConfig.builder()
1068 .format(JsonOutputFormat.builder().schema(schema).build())
1074 .format(JsonOutputFormat.builder()
1075 .schema(schema)
1076 .build())
10691077 .build();
10701078 
10711079 MessageCreateParams createParams = MessageCreateParams.builder()
from line 1778
17701778 public string $sentiment;
17711779 }
17721780 
1773 $feedbackText = "Great product, fast shipping!";
1781 $feedbackText = 'Great product, fast shipping!';
17741782 
17751783 $message = $client->messages->create(
17761784 maxTokens: 1024,
from line 2348
23402348 ]
23412349 });
23422350 
2343 // Claude may call the tool first (tool_use) or respond with JSON (text)
2351 // Claude might call the tool first (tool_use) or respond with JSON (text)
23442352 console.log("Stop reason:", response.stop_reason);
23452353 for (const block of response.content) {
23462354 switch (block.type) {
from line 2486
24782486 .maxTokens(1024L)
24792487 .addUserMessage("Help me plan a trip to Paris departing May 15, 2026")
24802488 .outputConfig(OutputConfig.builder()
2481 .format(JsonOutputFormat.builder().schema(outputSchema).build())
2489 .format(JsonOutputFormat.builder()
2490 .schema(outputSchema)
2491 .build())
24822492 .build())
24832493 .addTool(Tool.builder()
24842494 .name("search_flights")
from line 2543
25332543 ],
25342544 );
25352545 
2536 // Claude may call the tool first (tool_use) or respond with JSON (text)
2546 // Claude might call the tool first (tool_use) or respond with JSON (text)
25372547 $plan = $message->parsedOutput();
25382548 if ($plan instanceof TripPlan) {
25392549 echo $plan->summary, "\n";

build-with-claude/task-budgets Changed · +6 / -3 lines

from line 172
172172 .addUserMessage("Review the codebase and propose a refactor plan.")
173173 .outputConfig(BetaOutputConfig.builder()
174174 .effort(BetaOutputConfig.Effort.HIGH)
175 .taskBudget(BetaTokenTaskBudget.builder().total(64000L).build())
175 .taskBudget(BetaTokenTaskBudget.builder()
176 .total(64000L)
177 .build())
176178 .build())
177179 .addBeta("task-budgets-2026-03-13")
178180 .build();
from line 191
189191 
190192 ```php PHP
191193 use Anthropic\Beta\Messages\BetaRawMessageDeltaEvent;
194 use Anthropic\Messages\Model;
192195 
193196 $client = new Client();
194197 
195198 $stream = $client->beta->messages->createStream(
196 model: 'claude-opus-5-5',
199 model: Model::CLAUDE_OPUS_5_5,
197200 maxTokens: 128000,
198201 messages: [
199202 ['role' => 'user', 'content' => 'Review the codebase and propose a refactor plan.'],
from line 618
615618 $client = new Client();
616619 
617620 $response = $client->messages->create(
618 model: 'claude-opus-5-5',
621 model: Model::CLAUDE_OPUS_5_5,
619622 maxTokens: 4096,
620623 messages: [
621624 ['role' => 'user', 'content' => 'Review the codebase and propose a refactor plan.'],

build-with-claude/thinking Changed · +11 / -9 lines

from line 243
243243 ```
244244 
245245 ```php PHP
246 use Anthropic\Messages\Model;
246247 use Anthropic\Messages\TextBlock;
247248 use Anthropic\Messages\ThinkingBlock;
248249 
from line 257
256257 'content' => 'What is the greatest common divisor of 1071 and 462?'
257258 ]
258259 ],
259 model: 'claude-opus-5-5',
260 model: Model::CLAUDE_OPUS_5_5,
260261 thinking: ['type' => 'adaptive', 'display' => 'summarized'],
261262 );
262263 
from line 434
433434 'content' => 'Summarize this article in one sentence.'
434435 ]
435436 ],
436 model: 'claude-sonnet-5',
437 model: Model::CLAUDE_SONNET_5,
437438 thinking: ['type' => 'disabled'],
438439 );
439440 ```
from line 533
532533Thinking works with [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming). Thinking blocks stream as `thinking_delta` events inside `content_block_delta` events, followed by a single `signature_delta` event just before the block's `content_block_stop`. Text blocks stream afterward as usual.
533534 
534535<Frame>
535 ![Streaming with thinking: thinking deltas (text only if the display setting returns it), a signature delta, then text deltas](https://platform.claude.com/docs/images/how-thinking-streams.svg)
536 ![Thinking stream: the block opens, deltas carry text only if display returns text, one signature delta closes it, then text](https://platform.claude.com/docs/images/how-thinking-streams.svg)
536537</Frame>
537538 
538539The following examples stream a response with adaptive thinking, printing thinking and text deltas as they arrive:
from line 721
720721 }
721722 case CONTENT_BLOCK_DELTA -> {
722723 var deltaEvent = event.asContentBlockDelta();
723 deltaEvent.delta().thinking().ifPresent(td ->
724 IO.print(td.thinking())
724 deltaEvent.delta().thinking().ifPresent(thinkingDelta ->
725 IO.print(thinkingDelta.thinking())
725726 );
726 deltaEvent.delta().text().ifPresent(td ->
727 IO.print(td.text())
727 deltaEvent.delta().text().ifPresent(textDelta ->
728 IO.print(textDelta.text())
728729 );
729730 }
730731 }
from line 735
734735 ```
735736 
736737 ```php PHP
738 use Anthropic\Messages\Model;
737739 use Anthropic\Messages\RawContentBlockDeltaEvent;
738740 use Anthropic\Messages\RawContentBlockStartEvent;
739741 use Anthropic\Messages\TextDelta;
from line 748
746748 messages: [
747749 ['role' => 'user', 'content' => 'What is the greatest common divisor of 1071 and 462?']
748750 ],
749 model: 'claude-opus-5-5',
751 model: Model::CLAUDE_OPUS_5_5,
750752 thinking: ['type' => 'adaptive', 'display' => 'summarized'],
751753 );
752754 
from line 908
9069081. **Tool choice limitation (manual mode):** tool use with manual extended thinking (`thinking: {type: "enabled"}`) only supports `tool_choice: {"type": "auto"}` (the default) or `tool_choice: {"type": "none"}`. Using `tool_choice: {"type": "any"}` or `tool_choice: {"type": "tool", "name": "..."}` results in an error because these options force tool use, which is incompatible with manual extended thinking. Adaptive thinking, including on models where thinking is on by default, supports forced tool use, except on Claude Opus 5.5, Claude Sonnet 5.5, Claude Fable 5.1, and Claude Mythos 5.1 (see [Response prefill and forced tool use](https://platform.claude.com/docs/en/build-with-claude/thinking#limits-and-feature-compatibility)). Where forced tool use is accepted, the response starts with the tool call and has no `thinking` block.
9079092. **Preserving thinking blocks:** when you return tool results, you must pass the thinking blocks from the assistant message back to the API, complete and unmodified. See [Preserving thinking blocks](https://platform.claude.com/docs/en/build-with-claude/thinking#preserving-thinking-blocks).
908910 
909**A tool-use loop is one assistant turn.** From the model's perspective, an assistant turn doesn't complete until Claude finishes its full response, which might include multiple tool calls and results. This whole sequence is a single assistant turn:
911**A tool-use loop is one assistant turn.** From the model's perspective, an assistant turn ends only when Claude completes its full response, which might include multiple tool calls and results. This whole sequence is a single assistant turn:
910912 
911913```text wrap
912914User: "What's the weather in Paris?"

build-with-claude/thinking-steering-and-cost Changed · +675 / -692 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 116
116116 
117117For multi-turn applications, this means you can pass back conversation history in whatever shape you have it:
118118 
119* Assistant turns without thinking are valid history as-is.
119* Assistant turns that Claude produced without thinking are valid history as-is.
120120* You can resume a conversation that started without thinking, or that used a different thinking configuration, without rewriting its history.
121121* History assembled from mixed sources doesn't need thinking blocks reinserted at the start of each assistant turn to pass validation.
122122 
from line 131
131131The following example demonstrates the invalidation with a multi-turn script you can run yourself:
132132 
133133<Accordion title="Effort changes invalidate the prompt cache">
134 <Tabs>
135 <Tab title="cURL">
136 <Note>
137 This workflow doesn't translate well to a one-off shell command. Use the Claude SDK to run it; per-turn HTTP requests follow the examples on the [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) page.
138 </Note>
139 </Tab>
140 
141 <Tab title="CLI">
142 <Note>
143 This workflow doesn't translate well to a one-off shell command. Use the Claude SDK to run it; per-turn CLI commands follow the examples on the [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) page.
144 </Note>
145 </Tab>
146 
147 <Tab title="Python">
148 ```python
149 import requests
150 
151 client = Anthropic()
152 
153 
154 def fetch_article_content(url):
155 text = requests.get(url).text
156 lines = (line.strip() for line in text.splitlines())
157 return "\n".join(line for line in lines if line)
158 
159 
160 # Fetch the content of the article
161 book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
162 book_content = fetch_article_content(book_url)
163 # Use just enough text for caching (first few chapters)
164 LARGE_TEXT = book_content[:10000]
165 
166 # No system prompt - caching in messages instead
167 MESSAGES = [
134 <CodeGroup exclude="shell:cURL, shell:CLI">
135 <CodeGroupItem language="cURL">
136 This workflow needs more logic than a single shell command. Use the Claude SDK to run it; per-turn HTTP requests follow the examples on the [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) page.
137 </CodeGroupItem>
138 
139 <CodeGroupItem language="CLI">
140 This workflow needs more logic than a single shell command. Use the Claude SDK to run it; per-turn CLI commands follow the examples on the [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) page.
141 </CodeGroupItem>
142 
143 ```python Python
144 import requests
145 
146 client = Anthropic()
147 
148 
149 def fetch_article_content(url):
150 text = requests.get(url).text
151 lines = (line.strip() for line in text.splitlines())
152 return "\n".join(line for line in lines if line)
153 
154 
155 # Fetch the content of the article
156 book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
157 book_content = fetch_article_content(book_url)
158 # Use just enough text for caching (first few chapters)
159 LARGE_TEXT = book_content[:10000]
160 
161 # No system prompt: caching in messages instead
162 MESSAGES = [
163 {
164 "role": "user",
165 "content": [
166 {
167 "type": "text",
168 "text": LARGE_TEXT,
169 "cache_control": {"type": "ephemeral"},
170 },
171 {"type": "text", "text": "Analyze the tone of this passage."},
172 ],
173 }
174 ]
175 
176 # First request: establish cache
177 print("First request - establishing cache")
178 response1 = client.messages.create(
179 model="claude-opus-5-5",
180 max_tokens=16000,
181 thinking={"type": "adaptive"},
182 messages=MESSAGES,
183 )
184 
185 print(f"First response usage: {response1.usage}")
186 
187 MESSAGES.append({"role": "assistant", "content": response1.content})
188 MESSAGES.append({"role": "user", "content": "Analyze the characters in this passage."})
189 
190 # Second request: same configuration (cache hit expected)
191 print("\nSecond request - same configuration (cache hit expected)")
192 response2 = client.messages.create(
193 model="claude-opus-5-5",
194 max_tokens=16000,
195 thinking={"type": "adaptive"},
196 messages=MESSAGES,
197 )
198 
199 print(f"Second response usage: {response2.usage}")
200 
201 MESSAGES.append({"role": "assistant", "content": response2.content})
202 MESSAGES.append({"role": "user", "content": "Analyze the setting in this passage."})
203 
204 # Third request: different effort level (cache miss expected)
205 print("\nThird request - different effort level (cache miss expected)")
206 response3 = client.messages.create(
207 model="claude-opus-5-5",
208 max_tokens=16000,
209 thinking={"type": "adaptive"},
210 output_config={"effort": "low"},
211 messages=MESSAGES,
212 )
213 
214 print(f"Third response usage: {response3.usage}")
215 ```
216 
217 ```typescript TypeScript
218 
219 const client = new Anthropic();
220 
221 async function fetchArticleContent(url: string): Promise<string> {
222 const response = await fetch(url);
223 const text = await response.text();
224 const lines = text.split("\n").map((line) => line.trim());
225 return lines.filter((line) => line).join("\n");
226 }
227 
228 const bookUrl = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt";
229 const bookContent = await fetchArticleContent(bookUrl);
230 const LARGE_TEXT = bookContent.substring(0, 10000);
231 
232 // No system prompt: caching in messages instead
233 const messages: Anthropic.MessageParam[] = [
234 {
235 role: "user",
236 content: [
168237 {
169 "role": "user",
170 "content": [
171 {
172 "type": "text",
173 "text": LARGE_TEXT,
174 "cache_control": {"type": "ephemeral"},
175 },
176 {"type": "text", "text": "Analyze the tone of this passage."},
177 ],
238 type: "text",
239 text: LARGE_TEXT,
240 cache_control: { type: "ephemeral" }
241 },
242 {
243 type: "text",
244 text: "Analyze the tone of this passage."
178245 }
179 ]
180 
181 # First request - establish cache
182 print("First request - establishing cache")
183 response1 = client.messages.create(
184 model="claude-opus-5-5",
185 max_tokens=16000,
186 thinking={"type": "adaptive"},
187 messages=MESSAGES,
188 )
189 
190 print(f"First response usage: {response1.usage}")
191 
192 MESSAGES.append({"role": "assistant", "content": response1.content})
193 MESSAGES.append({"role": "user", "content": "Analyze the characters in this passage."})
194 
195 # Second request - same configuration (cache hit expected)
196 print("\nSecond request - same configuration (cache hit expected)")
197 response2 = client.messages.create(
198 model="claude-opus-5-5",
199 max_tokens=16000,
200 thinking={"type": "adaptive"},
201 messages=MESSAGES,
202 )
203 
204 print(f"Second response usage: {response2.usage}")
205 
206 MESSAGES.append({"role": "assistant", "content": response2.content})
207 MESSAGES.append({"role": "user", "content": "Analyze the setting in this passage."})
208 
209 # Third request - different effort level (cache miss expected)
210 print("\nThird request - different effort level (cache miss expected)")
211 response3 = client.messages.create(
212 model="claude-opus-5-5",
213 max_tokens=16000,
214 thinking={"type": "adaptive"},
215 output_config={"effort": "low"},
216 messages=MESSAGES,
217 )
218 
219 print(f"Third response usage: {response3.usage}")
220 ```
221 </Tab>
222 
223 <Tab title="TypeScript">
224 ```typescript
225 
226 const client = new Anthropic();
227 
228 async function fetchArticleContent(url: string): Promise<string> {
229 const response = await fetch(url);
230 const text = await response.text();
231 const lines = text.split("\n").map((line) => line.trim());
232 return lines.filter((line) => line).join("\n");
246 ]
233247 }
234 
235 const bookUrl = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt";
236 const bookContent = await fetchArticleContent(bookUrl);
237 const LARGE_TEXT = bookContent.substring(0, 10000);
238 
239 // No system prompt - caching in messages instead
240 const messages: Anthropic.MessageParam[] = [
248 ];
249 
250 // First request: establish cache
251 console.log("First request - establishing cache");
252 const response1 = await client.messages.create({
253 model: "claude-opus-5-5",
254 max_tokens: 16000,
255 thinking: { type: "adaptive" },
256 messages
257 });
258 
259 console.log("First response usage: ", response1.usage);
260 
261 messages.push(
262 { role: "assistant", content: response1.content },
263 { role: "user", content: "Analyze the characters in this passage." }
264 );
265 
266 // Second request: same configuration (cache hit expected)
267 console.log("\nSecond request - same configuration (cache hit expected)");
268 const response2 = await client.messages.create({
269 model: "claude-opus-5-5",
270 max_tokens: 16000,
271 thinking: { type: "adaptive" },
272 messages
273 });
274 
275 console.log("Second response usage: ", response2.usage);
276 
277 messages.push(
278 { role: "assistant", content: response2.content },
279 { role: "user", content: "Analyze the setting in this passage." }
280 );
281 
282 // Third request: different effort level (cache miss expected)
283 console.log("\nThird request - different effort level (cache miss expected)");
284 const response3 = await client.messages.create({
285 model: "claude-opus-5-5",
286 max_tokens: 16000,
287 thinking: { type: "adaptive" },
288 output_config: { effort: "low" },
289 messages
290 });
291 
292 console.log("Third response usage: ", response3.usage);
293 ```
294 
295 ```csharp C#
296 AnthropicClient client = new();
297 
298 string bookUrl = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt";
299 string bookContent = await FetchArticleContent(bookUrl);
300 string largeText = bookContent.Substring(0, Math.Min(10000, bookContent.Length));
301 
302 Console.WriteLine("First request - establishing cache");
303 var parameters1 = new MessageCreateParams
304 {
305 Model = Model.ClaudeOpus5_5,
306 MaxTokens = 16000,
307 Thinking = new ThinkingConfigAdaptive(),
308 Messages =
309 [
310 new UserMessageParam
311 {
312 Content = new List<ContentBlockParam>
313 {
314 new ContentBlockParam(new TextBlockParam()
315 {
316 Text = largeText,
317 CacheControl = new CacheControlEphemeral(),
318 }),
319 new ContentBlockParam(new TextBlockParam()
320 {
321 Text = "Analyze the tone of this passage."
322 }),
323 }
324 }
325 ]
326 };
327 
328 var response1 = await client.Messages.Create(parameters1);
329 Console.WriteLine($"First response usage: {response1.Usage}");
330 
331 Console.WriteLine("\nSecond request - same configuration (cache hit expected)");
332 var parameters2 = new MessageCreateParams
333 {
334 Model = Model.ClaudeOpus5_5,
335 MaxTokens = 16000,
336 Thinking = new ThinkingConfigAdaptive(),
337 Messages =
338 [
339 new UserMessageParam
340 {
341 Content = new List<ContentBlockParam>
342 {
343 new ContentBlockParam(new TextBlockParam()
344 {
345 Text = largeText,
346 CacheControl = new CacheControlEphemeral(),
347 }),
348 new ContentBlockParam(new TextBlockParam()
349 {
350 Text = "Analyze the tone of this passage."
351 }),
352 }
353 },
354 new AssistantMessageParam
355 {
356 Content = response1.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
357 },
358 new UserMessageParam { Content = "Analyze the characters in this passage." }
359 ]
360 };
361 
362 var response2 = await client.Messages.Create(parameters2);
363 Console.WriteLine($"Second response usage: {response2.Usage}");
364 
365 Console.WriteLine("\nThird request - different effort level (cache miss expected)");
366 var parameters3 = new MessageCreateParams
367 {
368 Model = Model.ClaudeOpus5_5,
369 MaxTokens = 16000,
370 Thinking = new ThinkingConfigAdaptive(),
371 OutputConfig = new OutputConfig
241372 {
242 role: "user",
243 content: [
373 Effort = Effort.Low
374 },
375 Messages =
376 [
377 new UserMessageParam
244378 {
245 type: "text",
246 text: LARGE_TEXT,
247 cache_control: { type: "ephemeral" }
379 Content = new List<ContentBlockParam>
380 {
381 new ContentBlockParam(new TextBlockParam()
382 {
383 Text = largeText,
384 CacheControl = new CacheControlEphemeral(),
385 }),
386 new ContentBlockParam(new TextBlockParam()
387 {
388 Text = "Analyze the tone of this passage."
389 }),
390 }
248391 },
392 new AssistantMessageParam
249393 {
250 type: "text",
251 text: "Analyze the tone of this passage."
252 }
253 ]
254 }
255 ];
256 
257 // First request - establish cache
258 console.log("First request - establishing cache");
259 const response1 = await client.messages.create({
260 model: "claude-opus-5-5",
261 max_tokens: 16000,
262 thinking: { type: "adaptive" },
263 messages
264 });
265 
266 console.log("First response usage: ", response1.usage);
267 
268 messages.push(
269 { role: "assistant", content: response1.content },
270 { role: "user", content: "Analyze the characters in this passage." }
271 );
272 
273 // Second request - same configuration (cache hit expected)
274 console.log("\nSecond request - same configuration (cache hit expected)");
275 const response2 = await client.messages.create({
276 model: "claude-opus-5-5",
277 max_tokens: 16000,
278 thinking: { type: "adaptive" },
279 messages
280 });
281 
282 console.log("Second response usage: ", response2.usage);
283 
284 messages.push(
285 { role: "assistant", content: response2.content },
286 { role: "user", content: "Analyze the setting in this passage." }
287 );
288 
289 // Third request - different effort level (cache miss expected)
290 console.log("\nThird request - different effort level (cache miss expected)");
291 const response3 = await client.messages.create({
292 model: "claude-opus-5-5",
293 max_tokens: 16000,
294 thinking: { type: "adaptive" },
295 output_config: { effort: "low" },
296 messages
297 });
298 
299 console.log("Third response usage: ", response3.usage);
300 ```
301 </Tab>
302 
303 <Tab title="C#">
304 ```csharp
305 AnthropicClient client = new();
306 
307 string bookUrl = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt";
308 string bookContent = await FetchArticleContent(bookUrl);
309 string largeText = bookContent.Substring(0, Math.Min(10000, bookContent.Length));
310 
311 Console.WriteLine("First request - establishing cache");
312 var parameters1 = new MessageCreateParams
313 {
314 Model = Model.ClaudeOpus5_5,
315 MaxTokens = 16000,
316 Thinking = new ThinkingConfigAdaptive(),
317 Messages =
318 [
319 new UserMessageParam
320 {
321 Content = new List<ContentBlockParam>
322 {
323 new ContentBlockParam(new TextBlockParam()
324 {
325 Text = largeText,
326 CacheControl = new CacheControlEphemeral(),
327 }),
328 new ContentBlockParam(new TextBlockParam()
329 {
330 Text = "Analyze the tone of this passage."
331 }),
332 }
333 }
334 ]
335 };
336 
337 var response1 = await client.Messages.Create(parameters1);
338 Console.WriteLine($"First response usage: {response1.Usage}");
339 
340 Console.WriteLine("\nSecond request - same configuration (cache hit expected)");
341 var parameters2 = new MessageCreateParams
342 {
343 Model = Model.ClaudeOpus5_5,
344 MaxTokens = 16000,
345 Thinking = new ThinkingConfigAdaptive(),
346 Messages =
347 [
348 new UserMessageParam
349 {
350 Content = new List<ContentBlockParam>
351 {
352 new ContentBlockParam(new TextBlockParam()
353 {
354 Text = largeText,
355 CacheControl = new CacheControlEphemeral(),
356 }),
357 new ContentBlockParam(new TextBlockParam()
358 {
359 Text = "Analyze the tone of this passage."
360 }),
361 }
362 },
363 new AssistantMessageParam
364 {
365 Content = response1.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
366 },
367 new UserMessageParam { Content = "Analyze the characters in this passage." }
368 ]
369 };
370 
371 var response2 = await client.Messages.Create(parameters2);
372 Console.WriteLine($"Second response usage: {response2.Usage}");
373 
374 Console.WriteLine("\nThird request - different effort level (cache miss expected)");
375 var parameters3 = new MessageCreateParams
376 {
377 Model = Model.ClaudeOpus5_5,
378 MaxTokens = 16000,
379 Thinking = new ThinkingConfigAdaptive(),
380 OutputConfig = new OutputConfig
394 Content = response1.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
395 },
396 new UserMessageParam { Content = "Analyze the characters in this passage." },
397 new AssistantMessageParam
398 {
399 Content = response2.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
400 },
401 new UserMessageParam { Content = "Analyze the setting in this passage." }
402 ]
403 };
404 
405 var response3 = await client.Messages.Create(parameters3);
406 Console.WriteLine($"Third response usage: {response3.Usage}");
407 
408 static async Task<string> FetchArticleContent(string url)
409 {
410 using HttpClient httpClient = new();
411 string content = await httpClient.GetStringAsync(url);
412 return content;
413 }
414 ```
415 
416 ```go Go
417 client := anthropic.NewClient()
418 
419 bookURL := "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
420 bookContent, err := fetchArticleContent(bookURL)
421 if err != nil {
422 log.Fatal(err)
423 }
424 
425 largeText := bookContent
426 if len(largeText) > 10000 {
427 largeText = largeText[:10000]
428 }
429 
430 // No system prompt: caching in messages instead
431 messages := []anthropic.MessageParam{
432 anthropic.NewUserMessage(
433 anthropic.ContentBlockParamUnion{OfText: &anthropic.TextBlockParam{
434 Text: largeText,
435 CacheControl: anthropic.NewCacheControlEphemeralParam(),
436 }},
437 anthropic.NewTextBlock("Analyze the tone of this passage."),
438 ),
439 }
440 
441 // First request: establish cache
442 fmt.Println("First request - establishing cache")
443 response1, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
444 Model: anthropic.ModelClaudeOpus5_5,
445 MaxTokens: 16000,
446 Thinking: anthropic.ThinkingConfigParamUnion{
447 OfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{},
448 },
449 Messages: messages,
450 })
451 if err != nil {
452 log.Fatal(err)
453 }
454 fmt.Printf("First response usage: %s\n", response1.Usage.RawJSON())
455 
456 messages = append(messages, response1.ToParam())
457 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock("Analyze the characters in this passage.")))
458 
459 // Second request: same configuration (cache hit expected)
460 fmt.Println("\nSecond request - same configuration (cache hit expected)")
461 response2, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
462 Model: anthropic.ModelClaudeOpus5_5,
463 MaxTokens: 16000,
464 Thinking: anthropic.ThinkingConfigParamUnion{
465 OfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{},
466 },
467 Messages: messages,
468 })
469 if err != nil {
470 log.Fatal(err)
471 }
472 fmt.Printf("Second response usage: %s\n", response2.Usage.RawJSON())
473 
474 messages = append(messages, response2.ToParam())
475 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock("Analyze the setting in this passage.")))
476 
477 // Third request: different effort level (cache miss expected)
478 fmt.Println("\nThird request - different effort level (cache miss expected)")
479 response3, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
480 Model: anthropic.ModelClaudeOpus5_5,
481 MaxTokens: 16000,
482 Thinking: anthropic.ThinkingConfigParamUnion{
483 OfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{},
484 },
485 OutputConfig: anthropic.OutputConfigParam{
486 Effort: anthropic.OutputConfigEffortLow,
487 },
488 Messages: messages,
489 })
490 if err != nil {
491 log.Fatal(err)
492 }
493 fmt.Printf("Third response usage: %s\n", response3.Usage.RawJSON())
494 ```
495 
496 ```java Java
497 import com.anthropic.models.messages.CacheControlEphemeral;
498 // ...
499 void main() throws Exception {
500 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
501 
502 String bookUrl = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt";
503 String bookContent = fetchArticleContent(bookUrl);
504 String largeText = bookContent.substring(0, Math.min(10000, bookContent.length()));
505 
506 // First request: establish cache
507 IO.println("First request - establishing cache");
508 MessageCreateParams params1 = MessageCreateParams.builder()
509 .model(Model.CLAUDE_OPUS_5_5)
510 .maxTokens(16000L)
511 .thinking(ThinkingConfigAdaptive.builder().build())
512 .addUserMessageOfContentBlockParams(List.of(
513 ContentBlockParam.ofText(TextBlockParam.builder()
514 .text(largeText)
515 .cacheControl(CacheControlEphemeral.builder().build())
516 .build()),
517 ContentBlockParam.ofText(TextBlockParam.builder()
518 .text("Analyze the tone of this passage.")
519 .build())
520 ))
521 .build();
522 
523 Message response1 = client.messages().create(params1);
524 IO.println("First response usage: " + response1.usage());
525 
526 // Second request: same configuration (cache hit expected)
527 IO.println("\nSecond request - same configuration (cache hit expected)");
528 MessageCreateParams params2 = MessageCreateParams.builder()
529 .model(Model.CLAUDE_OPUS_5_5)
530 .maxTokens(16000L)
531 .thinking(ThinkingConfigAdaptive.builder().build())
532 .addUserMessageOfContentBlockParams(List.of(
533 ContentBlockParam.ofText(TextBlockParam.builder()
534 .text(largeText)
535 .cacheControl(CacheControlEphemeral.builder().build())
536 .build()),
537 ContentBlockParam.ofText(TextBlockParam.builder()
538 .text("Analyze the tone of this passage.")
539 .build())
540 ))
541 .addAssistantMessageOfContentBlockParams(response1.content().stream()
542 .map(block -> block.toParam())
543 .collect(Collectors.toList()))
544 .addUserMessage("Analyze the characters in this passage.")
545 .build();
546 
547 Message response2 = client.messages().create(params2);
548 IO.println("Second response usage: " + response2.usage());
549 
550 // Third request: different effort level (cache miss expected)
551 IO.println("\nThird request - different effort level (cache miss expected)");
552 MessageCreateParams params3 = MessageCreateParams.builder()
553 .model(Model.CLAUDE_OPUS_5_5)
554 .maxTokens(16000L)
555 .thinking(ThinkingConfigAdaptive.builder().build())
556 .outputConfig(OutputConfig.builder()
557 .effort(OutputConfig.Effort.LOW)
558 .build())
559 .addUserMessageOfContentBlockParams(List.of(
560 ContentBlockParam.ofText(TextBlockParam.builder()
561 .text(largeText)
562 .cacheControl(CacheControlEphemeral.builder().build())
563 .build()),
564 ContentBlockParam.ofText(TextBlockParam.builder()
565 .text("Analyze the tone of this passage.")
566 .build())
567 ))
568 .addAssistantMessageOfContentBlockParams(response1.content().stream()
569 .map(block -> block.toParam())
570 .collect(Collectors.toList()))
571 .addUserMessage("Analyze the characters in this passage.")
572 .addAssistantMessageOfContentBlockParams(response2.content().stream()
573 .map(block -> block.toParam())
574 .collect(Collectors.toList()))
575 .addUserMessage("Analyze the setting in this passage.")
576 .build();
577 
578 Message response3 = client.messages().create(params3);
579 IO.println("Third response usage: " + response3.usage());
580 }
581 
582 String fetchArticleContent(String url) throws Exception {
583 HttpClient client = HttpClient.newHttpClient();
584 HttpRequest request = HttpRequest.newBuilder()
585 .uri(URI.create(url))
586 .build();
587 HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
588 return response.body();
589 }
590 ```
591 
592 ```php PHP
593 function fetchArticleContent($url)
594 {
595 $content = file_get_contents($url);
596 $lines = explode("\n", $content);
597 $cleanedLines = array_filter(array_map('trim', $lines));
598 return implode("\n", $cleanedLines);
599 }
600 
601 $client = new Client();
602 
603 $bookUrl = 'https://www.gutenberg.org/cache/epub/1342/pg1342.txt';
604 $bookContent = fetchArticleContent($bookUrl);
605 $largeText = substr($bookContent, 0, 10000);
606 
607 echo "First request - establishing cache\n";
608 $response1 = $client->messages->create(
609 maxTokens: 16000,
610 messages: [[
611 'role' => 'user',
612 'content' => [
613 [
614 'type' => 'text',
615 'text' => $largeText,
616 'cache_control' => ['type' => 'ephemeral']
617 ],
618 [
619 'type' => 'text',
620 'text' => 'Analyze the tone of this passage.'
621 ]
622 ]
623 ]],
624 model: 'claude-opus-5-5',
625 thinking: ['type' => 'adaptive'],
626 );
627 
628 echo 'First response usage: ' . json_encode($response1->usage) . "\n";
629 
630 echo "\nSecond request - same configuration (cache hit expected)\n";
631 $response2 = $client->messages->create(
632 maxTokens: 16000,
633 messages: [
634 [
635 'role' => 'user',
636 'content' => [
637 [
638 'type' => 'text',
639 'text' => $largeText,
640 'cache_control' => ['type' => 'ephemeral']
641 ],
642 [
643 'type' => 'text',
644 'text' => 'Analyze the tone of this passage.'
645 ]
646 ]
647 ],
648 [
649 'role' => 'assistant',
650 'content' => $response1->content
651 ],
652 [
653 'role' => 'user',
654 'content' => 'Analyze the characters in this passage.'
655 ]
656 ],
657 model: 'claude-opus-5-5',
658 thinking: ['type' => 'adaptive'],
659 );
660 
661 echo 'Second response usage: ' . json_encode($response2->usage) . "\n";
662 
663 echo "\nThird request - different effort level (cache miss expected)\n";
664 $response3 = $client->messages->create(
665 maxTokens: 16000,
666 messages: [
667 [
668 'role' => 'user',
669 'content' => [
670 [
671 'type' => 'text',
672 'text' => $largeText,
673 'cache_control' => ['type' => 'ephemeral']
674 ],
675 [
676 'type' => 'text',
677 'text' => 'Analyze the tone of this passage.'
678 ]
679 ]
680 ],
681 [
682 'role' => 'assistant',
683 'content' => $response1->content
684 ],
685 [
686 'role' => 'user',
687 'content' => 'Analyze the characters in this passage.'
688 ],
689 [
690 'role' => 'assistant',
691 'content' => $response2->content
692 ],
693 [
694 'role' => 'user',
695 'content' => 'Analyze the setting in this passage.'
696 ]
697 ],
698 model: 'claude-opus-5-5',
699 thinking: ['type' => 'adaptive'],
700 outputConfig: ['effort' => 'low'],
701 );
702 
703 echo 'Third response usage: ' . json_encode($response3->usage) . "\n";
704 ```
705 
706 ```ruby Ruby
707 require "net/http"
708 require "uri"
709 
710 def fetch_article_content(url)
711 uri = URI.parse(url)
712 response = Net::HTTP.get_response(uri)
713 text = response.body
714 
715 lines = text.split("\n").map(&:strip)
716 lines.reject(&:empty?).join("\n")
717 end
718 
719 client = Anthropic::Client.new
720 
721 book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
722 book_content = fetch_article_content(book_url)
723 large_text = book_content[0...10000]
724 
725 puts "First request - establishing cache"
726 response1 = client.messages.create(
727 model: "claude-opus-5-5",
728 max_tokens: 16000,
729 thinking: {
730 type: "adaptive"
731 },
732 messages: [{
733 role: "user",
734 content: [
381735 {
382 Effort = Effort.Low
736 type: "text",
737 text: large_text,
738 cache_control: { type: "ephemeral" }
383739 },
384 Messages =
385 [
386 new UserMessageParam
387 {
388 Content = new List<ContentBlockParam>
389 {
390 new ContentBlockParam(new TextBlockParam()
391 {
392 Text = largeText,
393 CacheControl = new CacheControlEphemeral(),
394 }),
395 new ContentBlockParam(new TextBlockParam()
396 {
397 Text = "Analyze the tone of this passage."
398 }),
399 }
400 },
401 new AssistantMessageParam
402 {
403 Content = response1.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
404 },
405 new UserMessageParam { Content = "Analyze the characters in this passage." },
406 new AssistantMessageParam
407 {
408 Content = response2.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
409 },
410 new UserMessageParam { Content = "Analyze the setting in this passage." }
411 ]
412 };
413 
414 var response3 = await client.Messages.Create(parameters3);
415 Console.WriteLine($"Third response usage: {response3.Usage}");
416 
417 static async Task<string> FetchArticleContent(string url)
418 {
419 using HttpClient httpClient = new();
420 string content = await httpClient.GetStringAsync(url);
421 return content;
422 }
423 ```
424 </Tab>
425 
426 <Tab title="Go">
427 ```go
428 client := anthropic.NewClient()
429 
430 bookURL := "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
431 bookContent, err := fetchArticleContent(bookURL)
432 if err != nil {
433 log.Fatal(err)
434 }
435 
436 largeText := bookContent
437 if len(largeText) > 10000 {
438 largeText = largeText[:10000]
439 }
440 
441 // No system prompt - caching in messages instead
442 messages := []anthropic.MessageParam{
443 anthropic.NewUserMessage(
444 anthropic.ContentBlockParamUnion{OfText: &anthropic.TextBlockParam{
445 Text: largeText,
446 CacheControl: anthropic.NewCacheControlEphemeralParam(),
447 }},
448 anthropic.NewTextBlock("Analyze the tone of this passage."),
449 ),
450 }
451 
452 // First request - establish cache
453 fmt.Println("First request - establishing cache")
454 response1, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
455 Model: anthropic.ModelClaudeOpus5_5,
456 MaxTokens: 16000,
457 Thinking: anthropic.ThinkingConfigParamUnion{
458 OfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{},
459 },
460 Messages: messages,
461 })
462 if err != nil {
463 log.Fatal(err)
464 }
465 fmt.Printf("First response usage: %s\n", response1.Usage.RawJSON())
466 
467 messages = append(messages, response1.ToParam())
468 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock("Analyze the characters in this passage.")))
469 
470 // Second request - same configuration (cache hit expected)
471 fmt.Println("\nSecond request - same configuration (cache hit expected)")
472 response2, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
473 Model: anthropic.ModelClaudeOpus5_5,
474 MaxTokens: 16000,
475 Thinking: anthropic.ThinkingConfigParamUnion{
476 OfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{},
477 },
478 Messages: messages,
479 })
480 if err != nil {
481 log.Fatal(err)
482 }
483 fmt.Printf("Second response usage: %s\n", response2.Usage.RawJSON())
484 
485 messages = append(messages, response2.ToParam())
486 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock("Analyze the setting in this passage.")))
487 
488 // Third request - different effort level (cache miss expected)
489 fmt.Println("\nThird request - different effort level (cache miss expected)")
490 response3, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
491 Model: anthropic.ModelClaudeOpus5_5,
492 MaxTokens: 16000,
493 Thinking: anthropic.ThinkingConfigParamUnion{
494 OfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{},
495 },
496 OutputConfig: anthropic.OutputConfigParam{
497 Effort: anthropic.OutputConfigEffortLow,
498 },
499 Messages: messages,
500 })
501 if err != nil {
502 log.Fatal(err)
503 }
504 fmt.Printf("Third response usage: %s\n", response3.Usage.RawJSON())
505 ```
506 </Tab>
507 
508 <Tab title="Java">
509 ```java
510 import com.anthropic.models.messages.CacheControlEphemeral;
511 // ...
512 void main() throws Exception {
513 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
514 
515 String bookUrl = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt";
516 String bookContent = fetchArticleContent(bookUrl);
517 String largeText = bookContent.substring(0, Math.min(10000, bookContent.length()));
518 
519 // First request - establishing cache
520 IO.println("First request - establishing cache");
521 MessageCreateParams params1 = MessageCreateParams.builder()
522 .model(Model.CLAUDE_OPUS_5_5)
523 .maxTokens(16000L)
524 .thinking(ThinkingConfigAdaptive.builder().build())
525 .addUserMessageOfContentBlockParams(List.of(
526 ContentBlockParam.ofText(TextBlockParam.builder()
527 .text(largeText)
528 .cacheControl(CacheControlEphemeral.builder().build())
529 .build()),
530 ContentBlockParam.ofText(TextBlockParam.builder()
531 .text("Analyze the tone of this passage.")
532 .build())
533 ))
534 .build();
535 
536 Message response1 = client.messages().create(params1);
537 IO.println("First response usage: " + response1.usage());
538 
539 // Second request - same configuration (cache hit expected)
540 IO.println("\nSecond request - same configuration (cache hit expected)");
541 MessageCreateParams params2 = MessageCreateParams.builder()
542 .model(Model.CLAUDE_OPUS_5_5)
543 .maxTokens(16000L)
544 .thinking(ThinkingConfigAdaptive.builder().build())
545 .addUserMessageOfContentBlockParams(List.of(
546 ContentBlockParam.ofText(TextBlockParam.builder()
547 .text(largeText)
548 .cacheControl(CacheControlEphemeral.builder().build())
549 .build()),
550 ContentBlockParam.ofText(TextBlockParam.builder()
551 .text("Analyze the tone of this passage.")
552 .build())
553 ))
554 .addAssistantMessageOfContentBlockParams(response1.content().stream()
555 .map(block -> block.toParam())
556 .collect(Collectors.toList()))
557 .addUserMessage("Analyze the characters in this passage.")
558 .build();
559 
560 Message response2 = client.messages().create(params2);
561 IO.println("Second response usage: " + response2.usage());
562 
563 // Third request - different effort level (cache miss expected)
564 IO.println("\nThird request - different effort level (cache miss expected)");
565 MessageCreateParams params3 = MessageCreateParams.builder()
566 .model(Model.CLAUDE_OPUS_5_5)
567 .maxTokens(16000L)
568 .thinking(ThinkingConfigAdaptive.builder().build())
569 .outputConfig(OutputConfig.builder()
570 .effort(OutputConfig.Effort.LOW)
571 .build())
572 .addUserMessageOfContentBlockParams(List.of(
573 ContentBlockParam.ofText(TextBlockParam.builder()
574 .text(largeText)
575 .cacheControl(CacheControlEphemeral.builder().build())
576 .build()),
577 ContentBlockParam.ofText(TextBlockParam.builder()
578 .text("Analyze the tone of this passage.")
579 .build())
580 ))
581 .addAssistantMessageOfContentBlockParams(response1.content().stream()
582 .map(block -> block.toParam())
583 .collect(Collectors.toList()))
584 .addUserMessage("Analyze the characters in this passage.")
585 .addAssistantMessageOfContentBlockParams(response2.content().stream()
586 .map(block -> block.toParam())
587 .collect(Collectors.toList()))
588 .addUserMessage("Analyze the setting in this passage.")
589 .build();
590 
591 Message response3 = client.messages().create(params3);
592 IO.println("Third response usage: " + response3.usage());
593 }
594 
595 String fetchArticleContent(String url) throws Exception {
596 HttpClient client = HttpClient.newHttpClient();
597 HttpRequest request = HttpRequest.newBuilder()
598 .uri(URI.create(url))
599 .build();
600 HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
601 return response.body();
602 }
603 ```
604 </Tab>
605 
606 <Tab title="PHP">
607 ```php
608 function fetchArticleContent($url) {
609 $content = file_get_contents($url);
610 $lines = explode("\n", $content);
611 $cleanedLines = array_filter(array_map('trim', $lines));
612 return implode("\n", $cleanedLines);
613 }
614 
615 $client = new Client();
616 
617 $bookUrl = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt";
618 $bookContent = fetchArticleContent($bookUrl);
619 $largeText = substr($bookContent, 0, 10000);
620 
621 echo "First request - establishing cache\n";
622 $response1 = $client->messages->create(
623 maxTokens: 16000,
624 messages: [[
625 'role' => 'user',
626 'content' => [
627 [
628 'type' => 'text',
629 'text' => $largeText,
630 'cache_control' => ['type' => 'ephemeral']
631 ],
632 [
633 'type' => 'text',
634 'text' => 'Analyze the tone of this passage.'
635 ]
636 ]
637 ]],
638 model: 'claude-opus-5-5',
639 thinking: ['type' => 'adaptive'],
640 );
641 
642 echo "First response usage: " . json_encode($response1->usage) . "\n";
643 
644 echo "\nSecond request - same configuration (cache hit expected)\n";
645 $response2 = $client->messages->create(
646 maxTokens: 16000,
647 messages: [
648 [
649 'role' => 'user',
650 'content' => [
651 [
652 'type' => 'text',
653 'text' => $largeText,
654 'cache_control' => ['type' => 'ephemeral']
655 ],
656 [
657 'type' => 'text',
658 'text' => 'Analyze the tone of this passage.'
659 ]
660 ]
661 ],
662 [
663 'role' => 'assistant',
664 'content' => $response1->content
665 ],
666 [
667 'role' => 'user',
668 'content' => 'Analyze the characters in this passage.'
669 ]
670 ],
671 model: 'claude-opus-5-5',
672 thinking: ['type' => 'adaptive'],
673 );
674 
675 echo "Second response usage: " . json_encode($response2->usage) . "\n";
676 
677 echo "\nThird request - different effort level (cache miss expected)\n";
678 $response3 = $client->messages->create(
679 maxTokens: 16000,
680 messages: [
681 [
682 'role' => 'user',
683 'content' => [
684 [
685 'type' => 'text',
686 'text' => $largeText,
687 'cache_control' => ['type' => 'ephemeral']
688 ],
689 [
690 'type' => 'text',
691 'text' => 'Analyze the tone of this passage.'
692 ]
693 ]
694 ],
695 [
696 'role' => 'assistant',
697 'content' => $response1->content
698 ],
699 [
700 'role' => 'user',
701 'content' => 'Analyze the characters in this passage.'
702 ],
703 [
704 'role' => 'assistant',
705 'content' => $response2->content
706 ],
707 [
708 'role' => 'user',
709 'content' => 'Analyze the setting in this passage.'
710 ]
711 ],
712 model: 'claude-opus-5-5',
713 thinking: ['type' => 'adaptive'],
714 outputConfig: ['effort' => 'low'],
715 );
716 
717 echo "Third response usage: " . json_encode($response3->usage) . "\n";
718 ```
719 </Tab>
720 
721 <Tab title="Ruby">
722 ```ruby
723 require "net/http"
724 require "uri"
725 
726 def fetch_article_content(url)
727 uri = URI.parse(url)
728 response = Net::HTTP.get_response(uri)
729 text = response.body
730 
731 lines = text.split("\n").map(&:strip)
732 lines.reject(&:empty?).join("\n")
733 end
734 
735 client = Anthropic::Client.new
736 
737 book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
738 book_content = fetch_article_content(book_url)
739 large_text = book_content[0...10000]
740 
741 puts "First request - establishing cache"
742 response1 = client.messages.create(
743 model: "claude-opus-5-5",
744 max_tokens: 16000,
745 thinking: {
746 type: "adaptive"
747 },
748 messages: [{
740 {
741 type: "text",
742 text: "Analyze the tone of this passage."
743 }
744 ]
745 }]
746 )
747 
748 puts "First response usage: #{response1.usage}"
749 
750 puts "\nSecond request - same configuration (cache hit expected)"
751 response2 = client.messages.create(
752 model: "claude-opus-5-5",
753 max_tokens: 16000,
754 thinking: {
755 type: "adaptive"
756 },
757 messages: [
758 {
749759 role: "user",
750760 content: [
751761 {
from line 768
758768 text: "Analyze the tone of this passage."
759769 }
760770 ]
761 }]
762 )
763 
764 puts "First response usage: #{response1.usage}"
765 
766 puts "\nSecond request - same configuration (cache hit expected)"
767 response2 = client.messages.create(
768 model: "claude-opus-5-5",
769 max_tokens: 16000,
770 thinking: {
771 type: "adaptive"
772771 },
773 messages: [
774 {
775 role: "user",
776 content: [
777 {
778 type: "text",
779 text: large_text,
780 cache_control: { type: "ephemeral" }
781 },
782 {
783 type: "text",
784 text: "Analyze the tone of this passage."
785 }
786 ]
787 },
788 {
789 role: "assistant",
790 content: response1.content
791 },
792 {
793 role: "user",
794 content: "Analyze the characters in this passage."
795 }
796 ]
797 )
798 
799 puts "Second response usage: #{response2.usage}"
800 
801 puts "\nThird request - different effort level (cache miss expected)"
802 response3 = client.messages.create(
803 model: "claude-opus-5-5",
804 max_tokens: 16000,
805 thinking: {
806 type: "adaptive"
772 {
773 role: "assistant",
774 content: response1.content
807775 },
808 output_config: {
809 effort: "low"
776 {
777 role: "user",
778 content: "Analyze the characters in this passage."
779 }
780 ]
781 )
782 
783 puts "Second response usage: #{response2.usage}"
784 
785 puts "\nThird request - different effort level (cache miss expected)"
786 response3 = client.messages.create(
787 model: "claude-opus-5-5",
788 max_tokens: 16000,
789 thinking: {
790 type: "adaptive"
791 },
792 output_config: {
793 effort: "low"
794 },
795 messages: [
796 {
797 role: "user",
798 content: [
799 {
800 type: "text",
801 text: large_text,
802 cache_control: { type: "ephemeral" }
803 },
804 {
805 type: "text",
806 text: "Analyze the tone of this passage."
807 }
808 ]
810809 },
811 messages: [
812 {
813 role: "user",
814 content: [
815 {
816 type: "text",
817 text: large_text,
818 cache_control: { type: "ephemeral" }
819 },
820 {
821 type: "text",
822 text: "Analyze the tone of this passage."
823 }
824 ]
825 },
826 {
827 role: "assistant",
828 content: response1.content
829 },
830 {
831 role: "user",
832 content: "Analyze the characters in this passage."
833 },
834 {
835 role: "assistant",
836 content: response2.content
837 },
838 {
839 role: "user",
840 content: "Analyze the setting in this passage."
841 }
842 ]
843 )
844 
845 puts "Third response usage: #{response3.usage}"
846 ```
847 </Tab>
848 </Tabs>
810 {
811 role: "assistant",
812 content: response1.content
813 },
814 {
815 role: "user",
816 content: "Analyze the characters in this passage."
817 },
818 {
819 role: "assistant",
820 content: response2.content
821 },
822 {
823 role: "user",
824 content: "Analyze the setting in this passage."
825 }
826 ]
827 )
828 
829 puts "Third response usage: #{response3.usage}"
830 ```
831 </CodeGroup>
849832 
850833 Here is the output of the script (you might see slightly different numbers):
851834 
852835 

build-with-claude/thinking-tool-workflows Changed · +14 / -11 lines

from line 91
9191 },
9292 }
9393 
94 # First request - Claude responds with thinking and tool request
94 # First request: Claude responds with thinking and tool request
9595 response = client.messages.create(
9696 model="claude-opus-5-5",
9797 max_tokens=16000,
from line 117
117117 }
118118 };
119119 
120 // First request - Claude responds with thinking and tool request
120 // First request: Claude responds with thinking and tool request
121121 const response = await client.messages.create({
122122 model: "claude-opus-5-5",
123123 max_tokens: 16000,
from line 242
242242 messages: [
243243 ['role' => 'user', 'content' => "What's the weather in Paris?"]
244244 ],
245 model: 'claude-opus-5-5',
245 model: Model::CLAUDE_OPUS_5_5,
246246 thinking: ['type' => 'adaptive'],
247247 tools: [$weatherTool],
248248 );
from line 348
348348 # Pretend this is what the weather API returns
349349 weather_data = {"temperature": 88}
350350 
351 # Second request - Include the assistant turn and the tool result
351 # Second request: include the assistant turn and the tool result
352352 continuation = client.messages.create(
353353 model="claude-opus-5-5",
354354 max_tokens=16000,
from line 407
407407 const weatherData = { temperature: 88 };
408408 
409409 if (toolUseBlock) {
410 // Second request - Include the assistant turn and the tool result
410 // Second request: include the assistant turn and the tool result
411411 const continuation = await client.messages.create({
412412 model: "claude-opus-5-5",
413413 max_tokens: 16000,
from line 488
488488 new UserMessageParam { Content = "What's the weather in Paris?" },
489489 // response.Content includes the thinking blocks; passing them back is required
490490 new AssistantMessageParam { Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList() },
491 new UserMessageParam { Content = new List<ContentBlockParam>
491 new UserMessageParam
492492 {
493 new ContentBlockParam(new ToolResultBlockParam()
493 Content = new List<ContentBlockParam>
494494 {
495 ToolUseID = toolUseBlock?.ID ?? "",
496 Content = $"Current temperature: {weatherData.temperature}°F"
497 })
498 }}
495 new ContentBlockParam(new ToolResultBlockParam()
496 {
497 ToolUseID = toolUseBlock?.ID ?? "",
498 Content = $"Current temperature: {weatherData.temperature}°F"
499 })
500 }
501 }
499502 ]
500503 };
501504 

build-with-claude/token-counting Changed · +145 / -137 lines

from line 11
1111 Claude API: ga
1212 Claude Platform on AWS: ga
1313 Amazon Bedrock: ga
14 Google Cloud: ga
14 Claude Models on Google Cloud (Google managed): ga
1515 Microsoft Foundry: ga
1616---
1717 
from line 142
142142 
143143 public class CountTokensExample {
144144 
145 public static void main(String[] args) {
146 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
145 public static void main(String[] args) {
146 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
147147 
148 MessageCountTokensParams params = MessageCountTokensParams.builder()
149 .model(Model.CLAUDE_OPUS_5_5)
150 .system("You are a scientist")
151 .addUserMessage("Hello, Claude")
152 .build();
148 MessageCountTokensParams params = MessageCountTokensParams.builder()
149 .model(Model.CLAUDE_OPUS_5_5)
150 .system("You are a scientist")
151 .addUserMessage("Hello, Claude")
152 .build();
153153 
154 MessageTokensCount count = client.messages().countTokens(params);
155 System.out.println(count);
156 }
154 MessageTokensCount count = client.messages().countTokens(params);
155 System.out.println(count);
156 }
157157 }
158158 ```
159159 
from line 164
164164 messages: [
165165 ['role' => 'user', 'content' => 'Hello, Claude']
166166 ],
167 model: 'claude-opus-5-5',
167 model: Model::CLAUDE_OPUS_5_5,
168168 system: 'You are a scientist',
169169 );
170170 
from line 375
375375 import com.anthropic.models.messages.MessageCountTokensParams;
376376 import com.anthropic.models.messages.MessageTokensCount;
377377 // ...
378 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
378 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
379379 
380 InputSchema schema = InputSchema.builder()
381 .properties(
382 JsonValue.from(
383 Map.of(
384 "location",
385 Map.of(
386 "type",
387 "string",
388 "description",
389 "The city and state, e.g. San Francisco, CA"
380 InputSchema schema = InputSchema.builder()
381 .properties(
382 JsonValue.from(
383 Map.of(
384 "location",
385 Map.of(
386 "type",
387 "string",
388 "description",
389 "The city and state, e.g. San Francisco, CA"
390 )
391 )
392 )
390393 )
391 )
392 )
393 )
394 .putAdditionalProperty("required", JsonValue.from(List.of("location")))
395 .build();
394 .putAdditionalProperty("required", JsonValue.from(List.of("location")))
395 .build();
396396 
397 MessageCountTokensParams params = MessageCountTokensParams.builder()
398 .model(Model.CLAUDE_OPUS_5_5)
399 .addTool(
400 Tool.builder()
401 .name("get_weather")
402 .description("Get the current weather in a given location")
403 .inputSchema(schema)
404 .build()
405 )
406 .addUserMessage("What's the weather like in San Francisco?")
407 .build();
397 MessageCountTokensParams params = MessageCountTokensParams.builder()
398 .model(Model.CLAUDE_OPUS_5_5)
399 .addTool(
400 Tool.builder()
401 .name("get_weather")
402 .description("Get the current weather in a given location")
403 .inputSchema(schema)
404 .build()
405 )
406 .addUserMessage("What's the weather like in San Francisco?")
407 .build();
408408 
409 MessageTokensCount count = client.messages().countTokens(params);
410 System.out.println(count);
409 MessageTokensCount count = client.messages().countTokens(params);
410 System.out.println(count);
411411 ```
412412 
413413 ```php PHP
from line 417
417417 messages: [
418418 ['role' => 'user', 'content' => "What's the weather like in San Francisco?"]
419419 ],
420 model: 'claude-opus-5-5',
420 model: Model::CLAUDE_OPUS_5_5,
421421 tools: [
422422 [
423423 'name' => 'get_weather',
from line 673
673673 import com.anthropic.models.messages.MessageCountTokensParams;
674674 import com.anthropic.models.messages.MessageTokensCount;
675675 // ...
676 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
676 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
677677 
678 String imageUrl =
679 "https://platform.claude.com/docs/images/vision-example.jpg";
680 String imageMediaType = "image/jpeg";
678 String imageUrl =
679 "https://platform.claude.com/docs/images/vision-example.jpg";
680 String imageMediaType = "image/jpeg";
681681 
682 HttpClient httpClient = HttpClient.newHttpClient();
683 HttpRequest request = HttpRequest.newBuilder().uri(URI.create(imageUrl)).build();
684 byte[] imageBytes = httpClient
685 .send(request, HttpResponse.BodyHandlers.ofByteArray())
686 .body();
687 String imageBase64 = Base64.getEncoder().encodeToString(imageBytes);
682 HttpClient httpClient = HttpClient.newHttpClient();
683 HttpRequest request = HttpRequest.newBuilder()
684 .uri(URI.create(imageUrl))
685 .build();
686 byte[] imageBytes = httpClient
687 .send(request, HttpResponse.BodyHandlers.ofByteArray())
688 .body();
689 String imageBase64 = Base64.getEncoder().encodeToString(imageBytes);
688690 
689 ContentBlockParam imageBlock = ContentBlockParam.ofImage(
690 ImageBlockParam.builder()
691 .source(
692 Base64ImageSource.builder()
693 .mediaType(Base64ImageSource.MediaType.IMAGE_JPEG)
694 .data(imageBase64)
695 .build()
696 )
697 .build()
698 );
691 ContentBlockParam imageBlock = ContentBlockParam.ofImage(
692 ImageBlockParam.builder()
693 .source(
694 Base64ImageSource.builder()
695 .mediaType(Base64ImageSource.MediaType.IMAGE_JPEG)
696 .data(imageBase64)
697 .build()
698 )
699 .build()
700 );
699701 
700 ContentBlockParam textBlock = ContentBlockParam.ofText(
701 TextBlockParam.builder().text("Describe this image").build()
702 );
702 ContentBlockParam textBlock = ContentBlockParam.ofText(
703 TextBlockParam.builder()
704 .text("Describe this image")
705 .build()
706 );
703707 
704 MessageCountTokensParams params = MessageCountTokensParams.builder()
705 .model(Model.CLAUDE_OPUS_5_5)
706 .addUserMessageOfContentBlockParams(List.of(imageBlock, textBlock))
707 .build();
708 MessageCountTokensParams params = MessageCountTokensParams.builder()
709 .model(Model.CLAUDE_OPUS_5_5)
710 .addUserMessageOfContentBlockParams(List.of(imageBlock, textBlock))
711 .build();
708712 
709 MessageTokensCount count = client.messages().countTokens(params);
710 System.out.println(count);
713 MessageTokensCount count = client.messages().countTokens(params);
714 System.out.println(count);
711715 ```
712716 
713717 ```php PHP
714 $imageUrl = "https://platform.claude.com/docs/images/vision-example.jpg";
715 $imageMediaType = "image/jpeg";
718 $imageUrl = 'https://platform.claude.com/docs/images/vision-example.jpg';
719 $imageMediaType = 'image/jpeg';
716720 $imageData = base64_encode(file_get_contents($imageUrl));
717721 
718722 $client = new Client();
from line 998
994998 import com.anthropic.models.messages.ThinkingBlockParam;
995999 import com.anthropic.models.messages.ThinkingConfigAdaptive;
9961000 // ...
997 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
1001 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
9981002 
999 List<ContentBlockParam> assistantBlocks = List.of(
1000 ContentBlockParam.ofThinking(
1001 ThinkingBlockParam.builder()
1002 .thinking(
1003 "This is a nice number theory question. Let's think about it step by step..."
1004 )
1005 .signature(
1006 "EuYBCkQYAiJAgCs1le6/Pol5Z4/JMomVOouGrWdhYNsH3ukzUECbB6iWrSQtsQuRHJID6lWV..."
1007 )
1008 .build()
1009 ),
1010 ContentBlockParam.ofText(
1011 TextBlockParam.builder()
1012 .text("Yes, there are infinitely many prime numbers p such that p mod 4 = 3...")
1013 .build()
1014 )
1015 );
1003 List<ContentBlockParam> assistantBlocks = List.of(
1004 ContentBlockParam.ofThinking(
1005 ThinkingBlockParam.builder()
1006 .thinking(
1007 "This is a nice number theory question. Let's think about it step by step..."
1008 )
1009 .signature(
1010 "EuYBCkQYAiJAgCs1le6/Pol5Z4/JMomVOouGrWdhYNsH3ukzUECbB6iWrSQtsQuRHJID6lWV..."
1011 )
1012 .build()
1013 ),
1014 ContentBlockParam.ofText(
1015 TextBlockParam.builder()
1016 .text("Yes, there are infinitely many prime numbers p such that p mod 4 = 3...")
1017 .build()
1018 )
1019 );
10161020 
1017 MessageCountTokensParams params = MessageCountTokensParams.builder()
1018 .model(Model.CLAUDE_OPUS_5_5)
1019 .thinking(ThinkingConfigAdaptive.builder().build())
1020 .addUserMessage("Are there an infinite number of prime numbers such that n mod 4 == 3?")
1021 .addAssistantMessageOfContentBlockParams(assistantBlocks)
1022 .addUserMessage("Can you write a formal proof?")
1023 .build();
1021 MessageCountTokensParams params = MessageCountTokensParams.builder()
1022 .model(Model.CLAUDE_OPUS_5_5)
1023 .thinking(ThinkingConfigAdaptive.builder().build())
1024 .addUserMessage("Are there an infinite number of prime numbers such that n mod 4 == 3?")
1025 .addAssistantMessageOfContentBlockParams(assistantBlocks)
1026 .addUserMessage("Can you write a formal proof?")
1027 .build();
10241028 
1025 MessageTokensCount count = client.messages().countTokens(params);
1026 System.out.println(count);
1029 MessageTokensCount count = client.messages().countTokens(params);
1030 System.out.println(count);
10271031 ```
10281032 
10291033 ```php PHP
from line 1296
12921296 import com.anthropic.models.messages.MessageCountTokensParams;
12931297 import com.anthropic.models.messages.MessageTokensCount;
12941298 // ...
1295 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
1299 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
12961300 
1297 byte[] fileBytes = Files.readAllBytes(Path.of("/path/to/document.pdf"));
1298 String pdfBase64 = Base64.getEncoder().encodeToString(fileBytes);
1301 byte[] fileBytes = Files.readAllBytes(Path.of("/path/to/document.pdf"));
1302 String pdfBase64 = Base64.getEncoder().encodeToString(fileBytes);
12991303 
1300 ContentBlockParam documentBlock = ContentBlockParam.ofDocument(
1301 DocumentBlockParam.builder()
1302 .source(Base64PdfSource.builder().data(pdfBase64).build())
1303 .build()
1304 );
1304 ContentBlockParam documentBlock = ContentBlockParam.ofDocument(
1305 DocumentBlockParam.builder()
1306 .source(Base64PdfSource.builder()
1307 .data(pdfBase64)
1308 .build())
1309 .build()
1310 );
13051311 
1306 ContentBlockParam textBlock = ContentBlockParam.ofText(
1307 TextBlockParam.builder().text("Please summarize this document.").build()
1308 );
1312 ContentBlockParam textBlock = ContentBlockParam.ofText(
1313 TextBlockParam.builder()
1314 .text("Please summarize this document.")
1315 .build()
1316 );
13091317 
1310 MessageCountTokensParams params = MessageCountTokensParams.builder()
1311 .model(Model.CLAUDE_OPUS_5_5)
1312 .addUserMessageOfContentBlockParams(List.of(documentBlock, textBlock))
1313 .build();
1318 MessageCountTokensParams params = MessageCountTokensParams.builder()
1319 .model(Model.CLAUDE_OPUS_5_5)
1320 .addUserMessageOfContentBlockParams(List.of(documentBlock, textBlock))
1321 .build();
13141322 
1315 MessageTokensCount count = client.messages().countTokens(params);
1316 System.out.println(count);
1323 MessageTokensCount count = client.messages().countTokens(params);
1324 System.out.println(count);
13171325 ```
13181326 
13191327 ```php PHP
13201328 $client = new Client();
13211329 
1322 $pdfBase64 = base64_encode(file_get_contents("/path/to/document.pdf"));
1330 $pdfBase64 = base64_encode(file_get_contents('/path/to/document.pdf'));
13231331 
13241332 $response = $client->messages->countTokens(
13251333 messages: [

build-with-claude/vision Changed · +95 / -81 lines

from line 128
128128 source: {
129129 type: "base64",
130130 media_type: "image/jpeg",
131 data: imageData // Base64-encoded image data as string
131 data: imageData // base64-encoded image data as string
132132 }
133133 },
134134 {
from line 203
203203 ```java Java
204204 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
205205 String imageData =
206 "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
206 "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
207207 
208208 List<ContentBlockParam> contentBlockParams = List.of(
209 ContentBlockParam.ofImage(
210 ImageBlockParam.builder()
211 .source(
212 Base64ImageSource.builder()
213 .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
214 .data(imageData)
215 .build()
216 )
217 .build()
218 ),
219 ContentBlockParam.ofText(TextBlockParam.builder().text("Describe this image.").build())
209 ContentBlockParam.ofImage(
210 ImageBlockParam.builder()
211 .source(
212 Base64ImageSource.builder()
213 .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
214 .data(imageData)
215 .build()
216 )
217 .build()
218 ),
219 ContentBlockParam.ofText(TextBlockParam.builder()
220 .text("Describe this image.")
221 .build())
220222 );
221223 Message message = client
222 .messages()
223 .create(
224 MessageCreateParams.builder()
225 .model(Model.CLAUDE_OPUS_5_5)
226 .maxTokens(1024)
227 .addUserMessageOfContentBlockParams(contentBlockParams)
228 .build()
229 );
224 .messages()
225 .create(
226 MessageCreateParams.builder()
227 .model(Model.CLAUDE_OPUS_5_5)
228 .maxTokens(1024)
229 .addUserMessageOfContentBlockParams(contentBlockParams)
230 .build()
231 );
230232 
231233 IO.println(message);
232234 ```
from line 236
234236 ```php PHP
235237 $client = new Client();
236238 
237 $imageData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
239 $imageData = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC';
238240 
239241 $message = $client->messages->create(
240242 maxTokens: 1024,
from line 256
254256 ],
255257 ],
256258 ],
257 model: 'claude-opus-5-5',
259 model: Model::CLAUDE_OPUS_5_5,
258260 );
259261 
260262 echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
from line 451
449451 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
450452 
451453 List<ContentBlockParam> contentBlockParams = List.of(
452 ContentBlockParam.ofImage(
453 ImageBlockParam.builder()
454 .source(
455 UrlImageSource.builder()
456 .url("https://platform.claude.com/docs/images/vision-example.jpg")
457 .build()
458 )
459 .build()
460 ),
461 ContentBlockParam.ofText(TextBlockParam.builder().text("Describe this image.").build())
454 ContentBlockParam.ofImage(
455 ImageBlockParam.builder()
456 .source(
457 UrlImageSource.builder()
458 .url("https://platform.claude.com/docs/images/vision-example.jpg")
459 .build()
460 )
461 .build()
462 ),
463 ContentBlockParam.ofText(TextBlockParam.builder()
464 .text("Describe this image.")
465 .build())
462466 );
463467 Message message = client
464 .messages()
465 .create(
466 MessageCreateParams.builder()
467 .model(Model.CLAUDE_OPUS_5_5)
468 .maxTokens(1024)
469 .addUserMessageOfContentBlockParams(contentBlockParams)
470 .build()
471 );
468 .messages()
469 .create(
470 MessageCreateParams.builder()
471 .model(Model.CLAUDE_OPUS_5_5)
472 .maxTokens(1024)
473 .addUserMessageOfContentBlockParams(contentBlockParams)
474 .build()
475 );
472476 System.out.println(message);
473477 ```
474478 
from line 752
748752 import com.anthropic.models.files.FileMetadata;
749753 import com.anthropic.models.files.FileUploadParams;
750754 // ...
751 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
755 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
752756 
753 // Upload the image file
754 FileMetadata file = client.files().upload(
755 FileUploadParams.builder()
756 .file(
757 MultipartField.<InputStream>builder()
758 .value(Files.newInputStream(Path.of("vision-example.jpg")))
759 .filename("vision-example.jpg")
760 .contentType("image/jpeg")
761 .build()
762 )
763 .build()
764 );
757 // Upload the image file
758 FileMetadata file = client.files().upload(
759 FileUploadParams.builder()
760 .file(
761 MultipartField.<InputStream>builder()
762 .value(Files.newInputStream(Path.of("vision-example.jpg")))
763 .filename("vision-example.jpg")
764 .contentType("image/jpeg")
765 .build()
766 )
767 .build()
768 );
765769 
766 // Use the uploaded file in a message
767 ImageBlockParam imageParam = ImageBlockParam.builder().fileSource(file.id()).build();
770 // Use the uploaded file in a message
771 ImageBlockParam imageParam = ImageBlockParam.builder()
772 .fileSource(file.id())
773 .build();
768774 
769 MessageCreateParams params = MessageCreateParams.builder()
770 .model(Model.CLAUDE_OPUS_5_5)
771 .maxTokens(1024)
772 .addUserMessageOfContentBlockParams(
773 List.of(
774 ContentBlockParam.ofImage(imageParam),
775 ContentBlockParam.ofText(
776 TextBlockParam.builder().text("Describe this image.").build()
777 )
778 )
779 )
780 .build();
775 MessageCreateParams params = MessageCreateParams.builder()
776 .model(Model.CLAUDE_OPUS_5_5)
777 .maxTokens(1024)
778 .addUserMessageOfContentBlockParams(
779 List.of(
780 ContentBlockParam.ofImage(imageParam),
781 ContentBlockParam.ofText(
782 TextBlockParam.builder()
783 .text("Describe this image.")
784 .build()
785 )
786 )
787 )
788 .build();
781789 
782 Message message = client.messages().create(params);
783 System.out.println(message.content());
790 Message message = client.messages().create(params);
791 System.out.println(message.content());
784792 ```
785793 
786794 ```php PHP
from line 1097
10891097 String image2Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC";
10901098 
10911099 List<ContentBlockParam> contentBlockParams = List.of(
1092 ContentBlockParam.ofText(TextBlockParam.builder().text("Image 1:").build()),
1100 ContentBlockParam.ofText(TextBlockParam.builder()
1101 .text("Image 1:")
1102 .build()),
10931103 ContentBlockParam.ofImage(
10941104 ImageBlockParam.builder()
10951105 .source(
from line 1110
11001110 )
11011111 .build()
11021112 ),
1103 ContentBlockParam.ofText(TextBlockParam.builder().text("Image 2:").build()),
1113 ContentBlockParam.ofText(TextBlockParam.builder()
1114 .text("Image 2:")
1115 .build()),
11041116 ContentBlockParam.ofImage(
11051117 ImageBlockParam.builder()
11061118 .source(
from line 1124
11121124 .build()
11131125 ),
11141126 ContentBlockParam.ofText(
1115 TextBlockParam.builder().text("How are these images different?").build()
1127 TextBlockParam.builder()
1128 .text("How are these images different?")
1129 .build()
11161130 )
11171131 );
11181132 
from line 1177
11631177 ],
11641178 ],
11651179 ],
1166 model: 'claude-opus-5-5',
1180 model: Model::CLAUDE_OPUS_5_5,
11671181 );
11681182 
11691183 echo $message;
from line 1244
12301244 
12311245The maximum size per image is:
12321246 
1233* 10 MB (base64-encoded) when using the Claude API directly.
1234* 5 MB (base64-encoded) on Amazon Bedrock and Claude Models on Google Cloud (Google managed).
1247* 10 MB (base64-encoded) on the Claude API and Claude Models on Google Cloud (Google managed).
1248* 5 MB (base64-encoded) on Amazon Bedrock.
12351249* 10 MB on [claude.ai](https://claude.ai/).
12361250 
12371251<Note>
1238 Although the API supports up to 600 images per request, [request size limits](https://platform.claude.com/docs/en/api/overview#request-size-limits) can be reached first: 32 MB for standard endpoints, and lower on some partner-operated platforms, such as Amazon Bedrock and Claude Models on Google Cloud (Google managed). For many images, consider uploading with the [Files API](https://platform.claude.com/docs/en/build-with-claude/vision#files-api-image-example) and referencing by `file_id` to keep request payloads small.
1252 Although the API supports up to 600 images for each request, [request size limits](https://platform.claude.com/docs/en/api/overview#request-size-limits) can be reached first: 32 MB for standard endpoints, and lower on some partner-operated platforms, such as Amazon Bedrock and Claude Models on Google Cloud (Google managed). For many images, consider [uploading with the Files API](https://platform.claude.com/docs/en/build-with-claude/vision#files-api-image-example) and referencing by `file_id` to keep request payloads small.
12391253 
12401254 Even when using the Files API, requests with many large images can fail before reaching the 600-image count. Reduce image dimensions or file sizes (for example, by downsampling) before uploading (see [Resolution and token cost](https://platform.claude.com/docs/en/build-with-claude/vision#evaluate-image-size)).
12411255</Note>

build-with-claude/vision-coordinates Changed · +9 / -9 lines

from line 57
5757 
5858 
5959 def count_image_tokens(width: int, height: int) -> int:
60 """Visual tokens consumed by an image: one token per 28x28 pixel patch."""
60 """Visual tokens consumed by an image: one token for each 28x28 pixel patch."""
6161 return math.ceil(width / 28) * math.ceil(height / 28)
6262 
6363 
from line 108
108108 ```
109109 
110110 ```typescript TypeScript
111 /** Visual tokens consumed by an image: one token per 28x28 pixel patch. */
111 /** Visual tokens consumed by an image: one token for each 28x28 pixel patch. */
112112 function countImageTokens(width: number, height: number): number {
113113 return Math.ceil(width / 28) * Math.ceil(height / 28);
114114 }
from line 176
176176 ```
177177 
178178 ```csharp C#
179 // Visual tokens consumed by an image: one token per 28x28 pixel patch.
179 // Visual tokens consumed by an image: one token for each 28x28 pixel patch.
180180 static int CountImageTokens(int width, int height)
181181 {
182182 return (width + 27) / 28 * ((height + 27) / 28); // ceil(w/28) * ceil(h/28)
from line 233
233233 ```
234234 
235235 ```go Go
236 // countImageTokens is the visual tokens consumed by an image: one token per
236 // countImageTokens is the visual tokens consumed by an image: one token for each
237237 // 28x28 pixel patch.
238238 func countImageTokens(width, height int) int {
239239 return ((width + 27) / 28) * ((height + 27) / 28) // ceil(w/28) * ceil(h/28)
from line 282
282282 /** A resized image size, as returned by resizedSize. */
283283 record Size(int width, int height) {}
284284 
285 /** Visual tokens consumed by an image: one token per 28x28 pixel patch. */
285 /** Visual tokens consumed by an image: one token for each 28x28 pixel patch. */
286286 static int countImageTokens(int width, int height) {
287287 return Math.ceilDiv(width, 28) * Math.ceilDiv(height, 28);
288288 }
from line 325
325325 
326326 private static boolean fits(int width, int height, int maxEdge, int maxTokens) {
327327 return Math.ceilDiv(width, 28) * 28 <= maxEdge
328 && Math.ceilDiv(height, 28) * 28 <= maxEdge
329 && countImageTokens(width, height) <= maxTokens;
328 && Math.ceilDiv(height, 28) * 28 <= maxEdge
329 && countImageTokens(width, height) <= maxTokens;
330330 }
331331 
332332 private static int shortEdge(int longEdge, double aspectRatio) {
from line 335
335335 ```
336336 
337337 ```php PHP
338 // Visual tokens consumed by an image: one token per 28x28 pixel patch.
338 // Visual tokens consumed by an image: one token for each 28x28 pixel patch.
339339 function countImageTokens(int $width, int $height): int
340340 {
341341 return intdiv($width + 27, 28) * intdiv($height + 27, 28);
from line 388
388388 ```
389389 
390390 ```ruby Ruby
391 # Visual tokens consumed by an image: one token per 28x28 pixel patch.
391 # Visual tokens consumed by an image: one token for each 28x28 pixel patch.
392392 def count_image_tokens(width, height)
393393 width.ceildiv(28) * height.ceildiv(28)
394394 end

build-with-claude/working-with-messages Changed · +15 / -13 lines

from line 114
114114 $message = $client->messages->create(
115115 maxTokens: 1024,
116116 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
117 model: 'claude-opus-5-5',
117 model: Model::CLAUDE_OPUS_5_5,
118118 );
119119 echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
120120 ```
from line 278
278278 ['role' => 'assistant', 'content' => 'Hello!'],
279279 ['role' => 'user', 'content' => 'Can you describe LLMs to me?'],
280280 ],
281 model: 'claude-opus-5-5',
281 model: Model::CLAUDE_OPUS_5_5,
282282 );
283283 
284284 echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
from line 507
507507 ```bash cURL
508508 #!/bin/sh
509509 
510 # Option 1: Base64-encoded image
510 # Option 1: base64-encoded image
511511 IMAGE_BASE64=$(curl "https://platform.claude.com/docs/images/vision-example.jpg" | base64 | tr -d '\n')
512512 
513513 curl https://api.anthropic.com/v1/messages \
from line 552
552552 ```
553553 
554554 ```bash CLI
555 # Option 1: Base64-encoded image (CLI auto-encodes binary @file refs)
555 # Option 1: base64-encoded image (CLI auto-encodes binary @file refs)
556556 curl -s "https://platform.claude.com/docs/images/vision-example.jpg" \
557557 -o ./vision-example.jpg
558558 
from line 591
591591 import base64
592592 import httpx2
593593 
594 # Option 1: Base64-encoded image
594 # Option 1: base64-encoded image
595595 image_url = "https://platform.claude.com/docs/images/vision-example.jpg"
596596 image_media_type = "image/jpeg"
597597 image_data = base64.standard_b64encode(httpx2.get(image_url).content).decode("utf-8")
from line 644
644644 ```typescript TypeScript
645645 const anthropic = new Anthropic();
646646 
647 // Option 1: Base64-encoded image
647 // Option 1: base64-encoded image
648648 const imageUrl = "https://platform.claude.com/docs/images/vision-example.jpg";
649649 const imageMediaType = "image/jpeg";
650650 const imageArrayBuffer = await (await fetch(imageUrl)).arrayBuffer();
from line 709
709709 
710710 AnthropicClient client = new();
711711 
712 // Option 1: Base64-encoded image
712 // Option 1: base64-encoded image
713713 string imageUrl = "https://platform.claude.com/docs/images/vision-example.jpg";
714714 
715715 using HttpClient httpClient = new();
from line 772
772772 ```go Go
773773 client := anthropic.NewClient()
774774 
775 // Option 1: Base64-encoded image
775 // Option 1: base64-encoded image
776776 imageURL := "https://platform.claude.com/docs/images/vision-example.jpg"
777777 
778778 req, err := http.NewRequest("GET", imageURL, nil)
from line 830
830830 ```java Java
831831 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
832832 
833 // Option 1: Base64-encoded image
833 // Option 1: base64-encoded image
834834 String imageUrl = "https://platform.claude.com/docs/images/vision-example.jpg";
835835 
836836 HttpClient httpClient = HttpClient.newHttpClient();
837 HttpRequest request = HttpRequest.newBuilder().uri(URI.create(imageUrl)).build();
837 HttpRequest request = HttpRequest.newBuilder()
838 .uri(URI.create(imageUrl))
839 .build();
838840 HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
839841 String imageData = Base64.getEncoder().encodeToString(response.body());
840842 
from line 888
886888 ```php PHP
887889 $client = new Client();
888890 
889 // Option 1: Base64-encoded image
891 // Option 1: base64-encoded image
890892 $image_url = 'https://platform.claude.com/docs/images/vision-example.jpg';
891 $image_media_type = "image/jpeg";
893 $image_media_type = 'image/jpeg';
892894 $image_data = base64_encode(file_get_contents($image_url));
893895 
894896 $message = $client->messages->create(
from line 950
948950 
949951 client = Anthropic::Client.new
950952 
951 # Option 1: Base64-encoded image
953 # Option 1: base64-encoded image
952954 image_url = "https://platform.claude.com/docs/images/vision-example.jpg"
953955 image_media_type = "image/jpeg"
954956 image_data = Base64.strict_encode64(Net::HTTP.get(URI(image_url)))

claude_api_primer Changed · +4 / -4 lines

from line 145
145145 
146146<CodeGroup exclude="shell:cURL, typescript, csharp, go, java, php, ruby">
147147 ```bash CLI
148 # Option 1: Base64-encoded image (@ prefix auto-encodes binary files as base64)
148 # Option 1: base64-encoded image (@ prefix auto-encodes binary files as base64)
149149 curl -sSo vision-example.jpg \
150150 https://platform.claude.com/docs/images/vision-example.jpg
151151 
from line 185
185185 import base64
186186 import httpx2
187187 
188 # Option 1: Base64-encoded image
188 # Option 1: base64-encoded image
189189 image_url = "https://platform.claude.com/docs/images/vision-example.jpg"
190190 image_media_type = "image/jpeg"
191191 image_data = base64.standard_b64encode(httpx2.get(image_url).content).decode("utf-8")
from line 339
339339 
340340 weather_data = {"temperature": 72}
341341 
342 # First request - Claude responds with thinking and tool request
342 # First request: Claude responds with thinking and tool request
343343 response = client.messages.create(
344344 model="claude-opus-5-5",
345345 max_tokens=16000,
from line 356
356356 (block for block in response.content if block.type == "tool_use"), None
357357 )
358358 
359 # Second request - Include thinking block and tool result
359 # Second request: include thinking block and tool result
360360 continuation = client.messages.create(
361361 model="claude-opus-5-5",
362362 max_tokens=16000,

cli-sdks-libraries/libraries/openai-sdk Changed · +13 / -13 lines

from line 18
1818 
1919## Getting started with the OpenAI SDK
2020 
21To use the OpenAI SDK compatibility feature, you'll need to:
21To use the OpenAI SDK compatibility feature, you need to:
2222 
23231. Use an official OpenAI SDK.
2424 
from line 137
137137 public class QuickStart {
138138 public static void main(String[] args) {
139139 OpenAIClient client = OpenAIOkHttpClient.builder()
140 .apiKey(System.getenv("ANTHROPIC_API_KEY")) // Your Claude API key
141 .baseUrl("https://api.anthropic.com/v1/") // the Claude API endpoint
142 .build();
140 .apiKey(System.getenv("ANTHROPIC_API_KEY")) // Your Claude API key
141 .baseUrl("https://api.anthropic.com/v1/") // the Claude API endpoint
142 .build();
143143 
144144 ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
145 .model("claude-opus-5-5") // Claude model name
146 .addSystemMessage("You are a helpful assistant.")
147 .addUserMessage("Who are you?")
148 .build();
145 .model("claude-opus-5-5") // Claude model name
146 .addSystemMessage("You are a helpful assistant.")
147 .addUserMessage("Who are you?")
148 .build();
149149 
150150 ChatCompletion completion = client.chat().completions().create(params);
151151 System.out.println(completion.choices().get(0).message().content().orElse(""));
from line 251
251251 
252252 ```java Java
253253 ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
254 .model("claude-sonnet-4-6")
255 .addUserMessage("Who are you?")
256 .putAdditionalBodyProperty("thinking",
257 JsonValue.from(Map.of("type", "enabled", "budget_tokens", 2000)))
258 .build();
254 .model("claude-sonnet-4-6")
255 .addUserMessage("Who are you?")
256 .putAdditionalBodyProperty("thinking",
257 JsonValue.from(Map.of("type", "enabled", "budget_tokens", 2000)))
258 .build();
259259 
260260 ChatCompletion completion = client.chat().completions().create(params);
261261 ```

cli-sdks-libraries/sdks/java Changed · +197 / -186 lines

from line 47
4747import com.anthropic.models.messages.MessageCreateParams;
4848import com.anthropic.models.messages.Model;
4949 
50// Configures using the `anthropic.apiKey`, `anthropic.authToken` and `anthropic.baseUrl` system properties
51// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_BASE_URL` environment variables
50// Configures using the `anthropic.apiKey`, `anthropic.authToken`, and `anthropic.baseUrl` system properties
51// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and `ANTHROPIC_BASE_URL` environment variables
5252AnthropicClient client = AnthropicOkHttpClient.fromEnv();
5353 
5454MessageCreateParams params = MessageCreateParams.builder()
55 .maxTokens(1024L)
56 .addUserMessage("Hello, Claude")
57 .model(Model.CLAUDE_OPUS_5_5)
58 .build();
55 .maxTokens(1024L)
56 .addUserMessage("Hello, Claude")
57 .model(Model.CLAUDE_OPUS_5_5)
58 .build();
5959 
6060Message message = client.messages().create(params);
6161```
from line 70
7070import com.anthropic.client.AnthropicClient;
7171import com.anthropic.client.okhttp.AnthropicOkHttpClient;
7272 
73// Configures using the `anthropic.apiKey`, `anthropic.authToken` and `anthropic.baseUrl` system properties
74// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_BASE_URL` environment variables
73// Configures using the `anthropic.apiKey`, `anthropic.authToken`, and `anthropic.baseUrl` system properties
74// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and `ANTHROPIC_BASE_URL` environment variables
7575AnthropicClient client = AnthropicOkHttpClient.fromEnv();
7676```
7777 
from line 82
8282import com.anthropic.client.okhttp.AnthropicOkHttpClient;
8383 
8484AnthropicClient client = AnthropicOkHttpClient.builder()
85 .apiKey("my-anthropic-api-key")
86 .build();
85 .apiKey("my-anthropic-api-key")
86 .build();
8787```
8888 
8989Or use a combination of both approaches:
from line 93
9393import com.anthropic.client.okhttp.AnthropicOkHttpClient;
9494 
9595AnthropicClient client = AnthropicOkHttpClient.builder()
96 // Configures using system properties or environment variables
97 .fromEnv()
98 .apiKey("my-anthropic-api-key")
99 .build();
96 // Configures using system properties or environment variables
97 .fromEnv()
98 .apiKey("my-anthropic-api-key")
99 .build();
100100```
101101 
102102For authentication options including Workload Identity Federation, see [Authentication](https://platform.claude.com/docs/en/manage-claude/authentication). If your API key is a [personal or service account key](https://platform.claude.com/docs/en/manage-claude/authentication#key-types) that isn't scoped to a workspace, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](https://platform.claude.com/docs/en/manage-claude/authentication#select-a-workspace) shows the per-request option for this SDK.
from line 123
123123import com.anthropic.client.AnthropicClient;
124124 
125125AnthropicClient clientWithOptions = client.withOptions(optionsBuilder -> {
126 optionsBuilder.baseUrl("https://example.com");
127 optionsBuilder.maxRetries(42);
126 optionsBuilder.baseUrl("https://example.com");
127 optionsBuilder.maxRetries(42);
128128});
129129```
130130 
from line 144
144144AnthropicClient client = AnthropicOkHttpClient.fromEnv();
145145 
146146MessageCreateParams params = MessageCreateParams.builder()
147 .maxTokens(1024L)
148 .addUserMessage("Hello, Claude")
149 .model(Model.CLAUDE_OPUS_5_5)
150 .build();
147 .maxTokens(1024L)
148 .addUserMessage("Hello, Claude")
149 .model(Model.CLAUDE_OPUS_5_5)
150 .build();
151151 
152152CompletableFuture<Message> message = client.async().messages().create(params);
153153```
from line 164
164164AnthropicClientAsync client = AnthropicOkHttpClientAsync.fromEnv();
165165 
166166MessageCreateParams params = MessageCreateParams.builder()
167 .maxTokens(1024L)
168 .addUserMessage("Hello, Claude")
169 .model(Model.CLAUDE_OPUS_5_5)
170 .build();
167 .maxTokens(1024L)
168 .addUserMessage("Hello, Claude")
169 .model(Model.CLAUDE_OPUS_5_5)
170 .build();
171171 
172172CompletableFuture<Message> message = client.messages().create(params);
173173```
from line 256
256256import com.anthropic.client.okhttp.AnthropicOkHttpClient;
257257 
258258AnthropicClient client = AnthropicOkHttpClient.builder()
259 .fromEnv()
260 .streamHandlerExecutor(Executors.newFixedThreadPool(4))
261 .build();
259 .fromEnv()
260 .streamHandlerExecutor(Executors.newFixedThreadPool(4))
261 .build();
262262```
263263 
264264### Streaming with message accumulator
from line 278
278278try (StreamResponse<RawMessageStreamEvent> streamResponse =
279279 client.messages().createStreaming(createParams)) {
280280 streamResponse.stream()
281 .peek(messageAccumulator::accumulate)
282 .flatMap(event -> event.contentBlockDelta().stream())
283 .flatMap(deltaEvent -> deltaEvent.delta().text().stream())
284 .forEach(textDelta -> IO.print(textDelta.text()));
281 .peek(messageAccumulator::accumulate)
282 .flatMap(event -> event.contentBlockDelta().stream())
283 .flatMap(deltaEvent -> deltaEvent.delta().text().stream())
284 .forEach(textDelta -> IO.print(textDelta.text()));
285285}
286286 
287287Message message = messageAccumulator.message();
from line 296
296296MessageAccumulator messageAccumulator = MessageAccumulator.create();
297297 
298298client.async().messages()
299 .createStreaming(createParams)
300 .subscribe(event -> messageAccumulator.accumulate(event).contentBlockDelta().stream()
301 .flatMap(deltaEvent -> deltaEvent.delta().text().stream())
302 .forEach(textDelta -> IO.print(textDelta.text())))
303 .onCompleteFuture()
304 .join();
299 .createStreaming(createParams)
300 .subscribe(event -> messageAccumulator.accumulate(event).contentBlockDelta().stream()
301 .flatMap(deltaEvent -> deltaEvent.delta().text().stream())
302 .forEach(textDelta -> IO.print(textDelta.text())))
303 .onCompleteFuture()
304 .join();
305305 
306306Message message = messageAccumulator.message();
307307```
from line 331
331331import com.fasterxml.jackson.annotation.JsonPropertyDescription;
332332 
333333enum Unit {
334 CELSIUS,
335 FAHRENHEIT;
334 CELSIUS,
335 FAHRENHEIT;
336336 
337 public String toString() {
338 return switch (this) {
339 case CELSIUS -> "C";
340 case FAHRENHEIT -> "F";
341 };
342 }
337 public String toString() {
338 return switch (this) {
339 case CELSIUS -> "C";
340 case FAHRENHEIT -> "F";
341 };
342 }
343343 
344 public double fromKelvin(double temperatureK) {
345 return switch (this) {
346 case CELSIUS -> temperatureK - 273.15;
347 case FAHRENHEIT -> (temperatureK - 273.15) * 1.8 + 32.0;
348 };
349 }
344 public double fromKelvin(double temperatureK) {
345 return switch (this) {
346 case CELSIUS -> temperatureK - 273.15;
347 case FAHRENHEIT -> (temperatureK - 273.15) * 1.8 + 32.0;
348 };
349 }
350350}
351351 
352352@JsonClassDescription("Get the weather in a given location")
353353static class GetWeather {
354354 
355 @JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
356 public String location;
355 @JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
356 public String location;
357357 
358 @JsonPropertyDescription("The unit of temperature")
359 public Unit unit;
358 @JsonPropertyDescription("The unit of temperature")
359 public Unit unit;
360360 
361 public Weather execute() {
362 double temperatureK = switch (location) {
363 case "San Francisco, CA" -> 300.0;
364 case "New York, NY" -> 310.0;
365 case "Dallas, TX" -> 305.0;
366 default -> 295;
367 };
368 return new Weather(String.format("%.0f%s", unit.fromKelvin(temperatureK), unit));
369 }
361 public Weather execute() {
362 double temperatureK = switch (location) {
363 case "San Francisco, CA" -> 300.0;
364 case "New York, NY" -> 310.0;
365 case "Dallas, TX" -> 305.0;
366 default -> 295;
367 };
368 return new Weather(String.format("%.0f%s", unit.fromKelvin(temperatureK), unit));
369 }
370370}
371371 
372372static class Weather {
373373 
374 public String temperature;
374 public String temperature;
375375 
376 public Weather(String temperature) {
377 this.temperature = temperature;
378 }
376 public Weather(String temperature) {
377 this.temperature = temperature;
378 }
379379}
380380```
381381 
from line 394
394394AnthropicClient client = AnthropicOkHttpClient.fromEnv();
395395 
396396MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
397 .model(Model.CLAUDE_OPUS_5_5)
398 .maxTokens(2048)
399 .addTool(GetWeather.class)
400 .addUserMessage("What's the temperature in New York?");
397 .model(Model.CLAUDE_OPUS_5_5)
398 .maxTokens(2048)
399 .addTool(GetWeather.class)
400 .addUserMessage("What's the temperature in New York?");
401401 
402402client.beta().messages().create(createParamsBuilder.build()).content().stream()
403 .flatMap(contentBlock -> contentBlock.toolUse().stream())
404 .forEach(toolUseBlock -> createParamsBuilder
405 // Add a message indicating that the tool use was requested.
406 .addAssistantMessageOfContentBlockParams(
407 List.of(BetaContentBlockParam.ofToolUse(BetaToolUseBlockParam.builder()
408 .name(toolUseBlock.name())
409 .id(toolUseBlock.id())
410 .input(toolUseBlock._input())
411 .build())))
412 // Add a message with the result of the requested tool use.
413 .addUserMessageOfContentBlockParams(
414 List.of(BetaContentBlockParam.ofToolResult(BetaToolResultBlockParam.builder()
415 .toolUseId(toolUseBlock.id())
416 .contentAsJson(callTool(toolUseBlock))
417 .build()))));
403 .flatMap(contentBlock -> contentBlock.toolUse().stream())
404 .forEach(toolUseBlock -> createParamsBuilder
405 // Add a message indicating that the tool use was requested.
406 .addAssistantMessageOfContentBlockParams(
407 List.of(BetaContentBlockParam.ofToolUse(BetaToolUseBlockParam.builder()
408 .name(toolUseBlock.name())
409 .id(toolUseBlock.id())
410 .input(toolUseBlock._input())
411 .build())))
412 // Add a message with the result of the requested tool use.
413 .addUserMessageOfContentBlockParams(
414 List.of(BetaContentBlockParam.ofToolResult(BetaToolResultBlockParam.builder()
415 .toolUseId(toolUseBlock.id())
416 .contentAsJson(callTool(toolUseBlock))
417 .build()))));
418418 
419419client.beta().messages().create(createParamsBuilder.build()).content().stream()
420 .flatMap(contentBlock -> contentBlock.text().stream())
421 .forEach(textBlock -> IO.println(textBlock.text()));
420 .flatMap(contentBlock -> contentBlock.text().stream())
421 .forEach(textBlock -> IO.println(textBlock.text()));
422422 
423423private static Object callTool(BetaToolUseBlock toolUseBlock) {
424 if (!"get_weather".equals(toolUseBlock.name())) {
425 throw new IllegalArgumentException("Unknown tool: " + toolUseBlock.name());
426 }
424 if (!"get_weather".equals(toolUseBlock.name())) {
425 throw new IllegalArgumentException("Unknown tool: " + toolUseBlock.name());
426 }
427427 
428 GetWeather tool = toolUseBlock.input(GetWeather.class);
429 return tool != null ? tool.execute() : new Weather("unknown");
428 GetWeather tool = toolUseBlock.input(GetWeather.class);
429 return tool != null ? tool.execute() : new Weather("unknown");
430430}
431431```
432432 
from line 440
440440 
441441```java
442442MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
443 .model(Model.CLAUDE_OPUS_5_5)
444 .maxTokens(2048)
445 .addTool(GetWeather.class, JsonSchemaLocalValidation.NO)
446 .addUserMessage("What's the temperature in New York?");
443 .model(Model.CLAUDE_OPUS_5_5)
444 .maxTokens(2048)
445 .addTool(GetWeather.class, JsonSchemaLocalValidation.NO)
446 .addUserMessage("What's the temperature in New York?");
447447```
448448 
449449### Annotating tool classes
from line 470
470470import com.anthropic.models.files.FileUploadParams;
471471 
472472FileUploadParams params = FileUploadParams.builder()
473 .file(
474 MultipartField.<InputStream>builder()
475 .value(Files.newInputStream(Paths.get("/path/to/file.pdf")))
476 .contentType("application/pdf")
477 .build()
478 )
479 .build();
473 .file(
474 MultipartField.<InputStream>builder()
475 .value(Files.newInputStream(Paths.get("/path/to/file.pdf")))
476 .contentType("application/pdf")
477 .build()
478 )
479 .build();
480480 
481481FileMetadata fileMetadata = client.files().upload(params);
482482```
from line 489
489489import com.anthropic.models.files.FileUploadParams;
490490 
491491FileUploadParams params = FileUploadParams.builder()
492 .file(
493 MultipartField.<InputStream>builder()
494 .value(URI.create("https://example.com/path/to/file").toURL().openStream())
495 .filename("document.pdf")
496 .contentType("application/pdf")
497 .build()
498 )
499 .build();
492 .file(
493 MultipartField.<InputStream>builder()
494 .value(URI.create("https://example.com/path/to/file").toURL().openStream())
495 .filename("document.pdf")
496 .contentType("application/pdf")
497 .build()
498 )
499 .build();
500500 
501501FileMetadata fileMetadata = client.files().upload(params);
502502```
from line 509
509509import com.anthropic.models.files.FileUploadParams;
510510 
511511FileUploadParams params = FileUploadParams.builder()
512 .file(
513 MultipartField.<InputStream>builder()
514 .value(new ByteArrayInputStream("content".getBytes()))
515 .filename("document.txt")
516 .contentType("text/plain")
517 .build()
518 )
519 .build();
512 .file(
513 MultipartField.<InputStream>builder()
514 .value(new ByteArrayInputStream("content".getBytes()))
515 .filename("document.txt")
516 .contentType("text/plain")
517 .build()
518 )
519 .build();
520520 
521521FileMetadata fileMetadata = client.files().upload(params);
522522```
from line 637
637637import com.anthropic.client.AnthropicClient;
638638import com.anthropic.client.okhttp.AnthropicOkHttpClient;
639639 
640AnthropicClient client = AnthropicOkHttpClient.builder().fromEnv().maxRetries(4).build();
640AnthropicClient client = AnthropicOkHttpClient.builder()
641 .fromEnv()
642 .maxRetries(4)
643 .build();
641644```
642645 
643646## Timeouts
from line 671
668671import com.anthropic.models.messages.Message;
669672 
670673Message message = client
671 .messages()
672 .create(params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build());
674 .messages()
675 .create(params, RequestOptions.builder()
676 .timeout(Duration.ofSeconds(30))
677 .build());
673678```
674679 
675680Or configure the default for all method calls at the client level:
from line 684
679684import com.anthropic.client.okhttp.AnthropicOkHttpClient;
680685 
681686AnthropicClient client = AnthropicOkHttpClient.builder()
682 .fromEnv()
683 .timeout(Duration.ofSeconds(30))
684 .build();
687 .fromEnv()
688 .timeout(Duration.ofSeconds(30))
689 .build();
685690```
686691 
687692## Long requests
from line 802
797802 
798803```java
799804MessageCreateParams params = MessageCreateParams.builder()
800 .maxTokens(1024L)
801 .addUserMessage("Hello, Claude")
802 .model(Model.CLAUDE_OPUS_5_5)
803 .build();
805 .maxTokens(1024L)
806 .addUserMessage("Hello, Claude")
807 .model(Model.CLAUDE_OPUS_5_5)
808 .build();
804809 
805810// Create a modified copy using toBuilder()
806MessageCreateParams modified = params.toBuilder().maxTokens(2048L).build();
811MessageCreateParams modified = params.toBuilder()
812 .maxTokens(2048L)
813 .build();
807814```
808815 
809816Because each class is immutable, builder modification never affects already built class instances.
from line 830
823830import com.anthropic.models.messages.MessageCreateParams;
824831 
825832MessageCreateParams params = MessageCreateParams.builder()
826 .putAdditionalHeader("Secret-Header", "42")
827 .putAdditionalQueryParam("secret_query_param", "42")
828 .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
829 .build();
833 .putAdditionalHeader("Secret-Header", "42")
834 .putAdditionalQueryParam("secret_query_param", "42")
835 .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
836 .build();
830837```
831838 
832839These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.
from line 850
843850import com.anthropic.models.messages.Metadata;
844851 
845852MessageCreateParams params = MessageCreateParams.builder()
846 .metadata(
847 Metadata.builder().putAdditionalProperty("secretProperty", JsonValue.from("42")).build()
848 )
849 .build();
853 .metadata(
854 Metadata.builder()
855 .putAdditionalProperty("secretProperty", JsonValue.from("42"))
856 .build()
857 )
858 .build();
850859```
851860 
852861These properties can be accessed on the nested built object later using the `_additionalProperties()` method.
from line 868
859868import com.anthropic.models.messages.Model;
860869 
861870MessageCreateParams params = MessageCreateParams.builder()
862 .maxTokens(JsonValue.from(3.14))
863 .addUserMessage("Hello, Claude")
864 .model(Model.CLAUDE_OPUS_5_5)
865 .build();
871 .maxTokens(JsonValue.from(3.14))
872 .addUserMessage("Hello, Claude")
873 .model(Model.CLAUDE_OPUS_5_5)
874 .build();
866875```
867876 
868877### JsonValue creation
from line 911
902911import com.anthropic.models.messages.Model;
903912 
904913MessageCreateParams params = MessageCreateParams.builder()
905 .addUserMessage("Hello, world")
906 .model(Model.CLAUDE_OPUS_5_5)
907 .maxTokens(JsonMissing.of())
908 .build();
914 .addUserMessage("Hello, world")
915 .model(Model.CLAUDE_OPUS_5_5)
916 .maxTokens(JsonMissing.of())
917 .build();
909918```
910919 
911920### Response properties
from line 925
916925import com.anthropic.core.JsonValue;
917926 
918927Map<String, JsonValue> additionalProperties = client
919 .messages()
920 .create(params)
921 ._additionalProperties();
928 .messages()
929 .create(params)
930 ._additionalProperties();
922931 
923932JsonValue secretPropertyValue = additionalProperties.get("secretProperty");
924933 
from line 962
953962JsonField<StopReason> stopReason = client.messages().create(params)._stopReason();
954963 
955964if (stopReason.isMissing()) {
956 // The property is absent from the JSON response
965 // The property is absent from the JSON response
957966} else if (stopReason.isNull()) {
958 // The property was set to literal null
967 // The property was set to literal null
959968} else {
960 // Check if value was provided as a string
961 // Other methods include `asNumber()`, `asBoolean()`, etc.
962 Optional<String> jsonString = stopReason.asString();
969 // Check if value was provided as a string
970 // Other methods include `asNumber()` and `asBoolean()`.
971 Optional<String> jsonString = stopReason.asString();
963972 
964 // Try to deserialize into a custom type
965 MyClass myObject = stopReason.asUnknown().orElseThrow().convert(MyClass.class);
973 // Try to deserialize into a custom type
974 MyClass myObject = stopReason.asUnknown().orElseThrow().convert(MyClass.class);
966975}
967976```
968977 
from line 993
984993import com.anthropic.models.messages.Message;
985994 
986995Message message = client
987 .messages()
988 .create(params, RequestOptions.builder().responseValidation(true).build());
996 .messages()
997 .create(params, RequestOptions.builder()
998 .responseValidation(true)
999 .build());
9891000```
9901001 
9911002Or configure the default for all method calls at the client level:
from line 1006
9951006import com.anthropic.client.okhttp.AnthropicOkHttpClient;
9961007 
9971008AnthropicClient client = AnthropicOkHttpClient.builder()
998 .fromEnv()
999 .responseValidation(true)
1000 .build();
1009 .fromEnv()
1010 .responseValidation(true)
1011 .build();
10011012```
10021013 
10031014## HTTP client customization
from line 1021
10101021import java.net.Proxy;
10111022 
10121023AnthropicClient client = AnthropicOkHttpClient.builder()
1013 .fromEnv()
1014 .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("https://example.com", 8080)))
1015 .build();
1024 .fromEnv()
1025 .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("https://example.com", 8080)))
1026 .build();
10161027```
10171028 
10181029### HTTPS / SSL configuration
from line 1037
10261037import com.anthropic.client.okhttp.AnthropicOkHttpClient;
10271038 
10281039AnthropicClient client = AnthropicOkHttpClient.builder()
1029 .fromEnv()
1030 .sslSocketFactory(yourSSLSocketFactory)
1031 .trustManager(yourTrustManager)
1032 .hostnameVerifier(yourHostnameVerifier)
1033 .build();
1040 .fromEnv()
1041 .sslSocketFactory(yourSSLSocketFactory)
1042 .trustManager(yourTrustManager)
1043 .hostnameVerifier(yourHostnameVerifier)
1044 .build();
10341045```
10351046 
10361047### Custom HTTP client
from line 1115
11041115import com.anthropic.models.messages.Model;
11051116 
11061117MessageCreateParams params = MessageCreateParams.builder()
1107 .maxTokens(1024L)
1108 .addUserMessage("Hello, Claude")
1109 .model(Model.CLAUDE_OPUS_5_5)
1110 .build();
1118 .maxTokens(1024L)
1119 .addUserMessage("Hello, Claude")
1120 .model(Model.CLAUDE_OPUS_5_5)
1121 .build();
11111122 
11121123HttpResponseFor<Message> message = client.messages().withRawResponse().create(params);
11131124 
from line 1201
11901201import com.anthropic.models.messages.MessageCreateParams;
11911202 
11921203MessageCreateParams params = MessageCreateParams.builder()
1193 .model("some-new-model") // same as .model(Model.of("some-new-model"))
1194 .maxTokens(1024L)
1195 .addUserMessage("Hello, Claude")
1196 .build();
1204 .model("some-new-model") // same as .model(Model.of("some-new-model"))
1205 .maxTokens(1024L)
1206 .addUserMessage("Hello, Claude")
1207 .build();
11971208```
11981209 
11991210Prefer the well-typed constants (for example, `Model.CLAUDE_OPUS_5`) so you get auto-complete and deprecation warnings. The `String` overloads and `of(...)` are primarily for setting the field to an undocumented or not yet supported value while waiting for an SDK release that includes it.

cli-sdks-libraries/sdks/php Changed · +42 / -40 lines

from line 30
3030$client = new Client();
3131 
3232$message = $client->messages->create(
33 maxTokens: 1024,
34 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
35 model: 'claude-opus-5-5',
33 maxTokens: 1024,
34 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
35 model: 'claude-opus-5-5',
3636);
3737 
3838$textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
from line 43
4343 
4444## Value objects
4545 
46The recommended way to initialize value objects is the static `with` constructor `Base64ImageSource::with(data: "U3RhaW5sZXNzIHJvY2tz", ...)` with named parameters.
46The recommended way to initialize value objects is the static `with` constructor `Base64ImageSource::with(data: 'U3RhaW5sZXNzIHJvY2tz', ...)` with named parameters.
4747 
48However, builders are also provided `(new Base64ImageSource)->withData("U3RhaW5sZXNzIHJvY2tz")`.
48However, builders are also provided, for example `(new Base64ImageSource())->withData('U3RhaW5sZXNzIHJvY2tz')`.
4949 
5050## Streaming
5151 
from line 55
5555$client = new Client();
5656 
5757$stream = $client->messages->createStream(
58 maxTokens: 1024,
59 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
60 model: 'claude-opus-5-5',
58 maxTokens: 1024,
59 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
60 model: Model::CLAUDE_OPUS_5_5,
6161);
6262 
6363foreach ($stream as $event) {
64 echo $event->type . PHP_EOL;
64 echo $event->type . PHP_EOL;
6565}
6666```
6767 
from line 69
6969 
7070```php
7171$client = new Client(
72 requestOptions: RequestOptions::with(streamingTransporter: $myStreamingClient),
72 requestOptions: RequestOptions::with(streamingTransporter: $myStreamingClient),
7373);
7474```
7575 
from line 83
8383use Anthropic\Core\Exceptions\APIConnectionException;
8484use Anthropic\Core\Exceptions\APIStatusException;
8585use Anthropic\Core\Exceptions\RateLimitException;
86use Anthropic\Messages\Model;
8687// ...
8788try {
88 $message = $client->messages->create(
89 maxTokens: 1024,
90 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
91 model: 'claude-opus-5-5',
92 );
89 $message = $client->messages->create(
90 maxTokens: 1024,
91 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
92 model: Model::CLAUDE_OPUS_5_5,
93 );
9394} catch (APIConnectionException $e) {
94 echo "The server could not be reached", PHP_EOL;
95 echo $e->getPrevious()?->getMessage(), PHP_EOL;
95 echo 'The server could not be reached', PHP_EOL;
96 echo $e->getPrevious()?->getMessage(), PHP_EOL;
9697} catch (RateLimitException $_) {
97 echo "A 429 status code was received; we should back off a bit.", PHP_EOL;
98 echo 'A 429 status code was received; we should back off a bit.', PHP_EOL;
9899} catch (APIStatusException $e) {
99 echo "Another non-200-range status code was received", PHP_EOL;
100 echo $e->getMessage();
100 echo 'Another non-200-range status code was received', PHP_EOL;
101 echo $e->getMessage();
101102}
102103```
103104 
from line 127
126127You can use the `maxRetries` option to configure or disable this:
127128 
128129```php
130use Anthropic\Messages\Model;
129131use Anthropic\RequestOptions;
130132// ...
131133// Configure the default for all requests:
from line 135
133135 
134136// Or, configure per-request:
135137$result = $client->messages->create(
136 maxTokens: 1024,
137 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
138 model: 'claude-opus-5-5',
139 requestOptions: RequestOptions::with(maxRetries: 5),
138 maxTokens: 1024,
139 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
140 model: Model::CLAUDE_OPUS_5_5,
141 requestOptions: RequestOptions::with(maxRetries: 5),
140142);
141143```
142144 
from line 155
153155 
154156// fetch items from the current page
155157foreach ($page->getItems() as $item) {
156 echo $item->id, PHP_EOL;
158 echo $item->id, PHP_EOL;
157159}
158160// make additional network requests to fetch items from all pages, including and after the current page
159161// ...
160162foreach ($page->pagingEachItem() as $item) {
161 echo $item->id, PHP_EOL;
163 echo $item->id, PHP_EOL;
162164// ...
163165}
164166```
from line 181
179181use Anthropic\RequestOptions;
180182// ...
181183$message = $client->messages->create(
182 maxTokens: 1024,
183 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
184 model: 'claude-opus-5-5',
185 requestOptions: RequestOptions::with(
186 extraQueryParams: ['my_query_parameter' => 'value'],
187 extraBodyParams: ['my_body_parameter' => 'value'],
188 extraHeaders: ['my-header' => 'value'],
189 ),
184 maxTokens: 1024,
185 messages: [['role' => 'user', 'content' => 'Hello, Claude']],
186 model: 'claude-opus-5-5',
187 requestOptions: RequestOptions::with(
188 extraQueryParams: ['my_query_parameter' => 'value'],
189 extraBodyParams: ['my_body_parameter' => 'value'],
190 extraHeaders: ['my-header' => 'value'],
191 ),
190192);
191193```
192194 
from line 204
202204$client = new Client();
203205 
204206$response = $client->request(
205 method: "post",
206 path: '/undocumented/endpoint',
207 query: ['dog' => 'woof'],
208 headers: ['useful-header' => 'interesting-value'],
209 body: ['hello' => 'world']
207 method: 'post',
208 path: '/undocumented/endpoint',
209 query: ['dog' => 'woof'],
210 headers: ['useful-header' => 'interesting-value'],
211 body: ['hello' => 'world']
210212);
211213```
212214 

manage-claude/authentication Changed · +28 / -28 lines

from line 80
8080 // const client = new Anthropic();
8181 ```
8282 
83 ```csharp C#
84 using Anthropic;
85 
86 AnthropicClient client = new() { ApiKey = "my-anthropic-api-key" };
87 // Or, with ANTHROPIC_API_KEY set in the environment:
88 // AnthropicClient client = new();
89 ```
90 
8391 ```go Go
8492 client := anthropic.NewClient(
8593 option.WithAPIKey("sk-ant-api03-..."), // defaults to os.LookupEnv("ANTHROPIC_API_KEY")
from line 100
92100 
93101 // Explicit
94102 AnthropicClient client = AnthropicOkHttpClient.builder()
95 .apiKey("my-anthropic-api-key")
96 .build();
103 .apiKey("my-anthropic-api-key")
104 .build();
97105 
98106 // From ANTHROPIC_API_KEY (or anthropic.apiKey system property)
99107 AnthropicClient clientFromEnv = AnthropicOkHttpClient.fromEnv();
100108 ```
101109 
102 ```csharp C#
103 using Anthropic;
104 
105 AnthropicClient client = new() { ApiKey = "my-anthropic-api-key" };
106 // Or, with ANTHROPIC_API_KEY set in the environment:
107 // AnthropicClient client = new();
108 ```
109 
110110 ```php PHP
111111 // Reads ANTHROPIC_API_KEY from the environment
112112 $client = new Client();
from line 133
133133 
134134<CodeGroup>
135135 ```bash cURL
136 # Required on every request for a multi-workspace key.
137 # Omit the anthropic-workspace-id header for a single-workspace key.
136 # Required on every request for a key that isn't scoped to a workspace.
137 # Omit the anthropic-workspace-id header for a key scoped to one workspace.
138138 curl https://api.anthropic.com/v1/messages \
139139 -H "x-api-key: $ANTHROPIC_API_KEY" \
140140 -H "anthropic-version: 2023-06-01" \
from line 148
148148 ```
149149 
150150 ```bash CLI
151 # Required on every command for a multi-workspace key.
152 # Omit --workspace-id for a single-workspace key.
151 # Required on every command for a key that isn't scoped to a workspace.
152 # Omit --workspace-id for a key scoped to one workspace.
153153 ant messages create \
154154 --workspace-id wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ \
155155 --model claude-opus-5-5 \
from line 160
160160 ```python Python
161161 client = Anthropic() # reads ANTHROPIC_API_KEY
162162 
163 # Required on every request for a multi-workspace key.
164 # Omit extra_headers for a single-workspace key.
163 # Required on every request for a key that isn't scoped to a workspace.
164 # Omit extra_headers for a key scoped to one workspace.
165165 message = client.messages.create(
166166 model="claude-opus-5-5",
167167 max_tokens=1024,
from line 179
179179 ```typescript TypeScript
180180 const client = new Anthropic(); // reads ANTHROPIC_API_KEY
181181 
182 // Required on every request for a multi-workspace key.
183 // Omit the second argument for a single-workspace key.
182 // Required on every request for a key that isn't scoped to a workspace.
183 // Omit the second argument for a key scoped to one workspace.
184184 const message = await client.messages.create(
185185 {
186186 model: "claude-opus-5-5",
from line 207
207207 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
208208 };
209209 
210 // Required on every request for a multi-workspace key.
211 // Call client.Messages.Create(parameters) directly for a single-workspace key.
210 // Required on every request for a key that isn't scoped to a workspace.
211 // Call client.Messages.Create(parameters) directly for a key scoped to one workspace.
212212 var message = await client
213213 .WithOptions(options =>
214214 options with
from line 235
235235 ```go Go
236236 client := anthropic.NewClient() // reads ANTHROPIC_API_KEY
237237 
238 // Required on every request for a multi-workspace key.
239 // Omit the option for a single-workspace key.
238 // Required on every request for a key that isn't scoped to a workspace.
239 // Omit the option for a key scoped to one workspace.
240240 message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
241241 Model: anthropic.ModelClaudeOpus5_5,
242242 MaxTokens: 1024,
from line 258
258258 ```java Java
259259 AnthropicClient client = AnthropicOkHttpClient.fromEnv(); // reads ANTHROPIC_API_KEY
260260 
261 // Required on every request for a multi-workspace key.
262 // Omit putAdditionalHeader for a single-workspace key.
261 // Required on every request for a key that isn't scoped to a workspace.
262 // Omit putAdditionalHeader for a key scoped to one workspace.
263263 Message message = client.messages().create(MessageCreateParams.builder()
264264 .model(Model.CLAUDE_OPUS_5_5)
265265 .maxTokens(1024)
from line 279
279279 ```php PHP
280280 $client = new Client(); // reads ANTHROPIC_API_KEY
281281 
282 // Required on every request for a multi-workspace key.
283 // Omit requestOptions for a single-workspace key.
282 // Required on every request for a key that isn't scoped to a workspace.
283 // Omit requestOptions for a key scoped to one workspace.
284284 $message = $client->messages->create(
285285 model: Model::CLAUDE_OPUS_5_5,
286286 maxTokens: 1024,
from line 296
296296 ```ruby Ruby
297297 client = Anthropic::Client.new # reads ANTHROPIC_API_KEY
298298 
299 # Required on every request for a multi-workspace key.
300 # Omit request_options for a single-workspace key.
299 # Required on every request for a key that isn't scoped to a workspace.
300 # Omit request_options for a key scoped to one workspace.
301301 message = client.messages.create(
302302 model: Anthropic::Model::CLAUDE_OPUS_5_5,
303303 max_tokens: 1024,

manage-claude/data-residency Changed · +8 / -8 lines

from line 149
149149 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
150150 
151151 Message response = client.messages().create(
152 MessageCreateParams.builder()
153 .model(Model.CLAUDE_OPUS_5_5)
154 .maxTokens(1024L)
155 .inferenceGeo("us")
156 .addUserMessage("Summarize the key points of this document.")
157 .build());
152 MessageCreateParams.builder()
153 .model(Model.CLAUDE_OPUS_5_5)
154 .maxTokens(1024L)
155 .inferenceGeo("us")
156 .addUserMessage("Summarize the key points of this document.")
157 .build());
158158 
159159 response.content().stream()
160 .flatMap(block -> block.text().stream())
161 .forEach(textBlock -> IO.println(textBlock.text()));
160 .flatMap(block -> block.text().stream())
161 .forEach(textBlock -> IO.println(textBlock.text()));
162162 // Check where inference actually ran
163163 IO.println("Inference geo: " + response.usage().inferenceGeo().get());
164164 ```

manage-claude/opentelemetry Changed · +17 / -1 lines

from line 45
4545| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
4646| [Messages API](https://platform.claude.com/docs/en/api/messages/create) requests | ✅ | ✅ | ✅ |
4747| [Sessions API](https://platform.claude.com/docs/en/managed-agents/sessions) requests | ✅ | ✅ | ✅ |
48| [Token counts](https://platform.claude.com/docs/en/manage-claude/opentelemetry#count-tokens) | ✅ | ✅ | ✅ |
48| Token usage (see [Counting tokens](https://platform.claude.com/docs/en/manage-claude/opentelemetry#count-tokens)) | ✅ | ✅ | ✅ |
4949| Each model run within a request | — | ✅ | ✅ |
5050| [Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) turns | — | ✅ | ✅ |
5151| [Server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), run by the API | — | ✅ | ✅ |
from line 80
8080 AI-powered observability and security.
8181 </Card>
8282 
83 <Card title="Dynatrace" image="https://platform.claude.com/docs/images/partner-logos/dynatrace.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/dynatrace-dark.svg" imageHeight={22} href="https://docs.dynatrace.com/docs/observe/dynatrace-for-ai-observability/integrations/claude-platform">
84 Observability built for the age of AI.
85 </Card>
86 
87 <Card title="Elastic" image="https://platform.claude.com/docs/images/partner-logos/elastic.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/elastic-dark.svg" imageHeight={32} href="https://www.elastic.co/docs/reference/opentelemetry/motlp">
88 Observability built on open source.
89 </Card>
90 
8391 <Card title="Grafana Cloud" image="https://platform.claude.com/docs/images/partner-logos/grafana.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/grafana-dark.svg" imageHeight={34} imageOffsetLeft={2} href="https://grafana.com/docs/grafana-cloud/observe-and-act/agent-observability/guides/send-claude-platform-traces/">
8492 Fully managed open observability platform.
8593 </Card>
from line 112
104112 All-in-one platform for building successful products.
105113 </Card>
106114 
115 <Card title="Pydantic Logfire" image="https://platform.claude.com/docs/images/partner-logos/pydantic.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/pydantic-dark.svg" imageHeight={24} href="https://pydantic.dev/docs/logfire/integrations/llms/claude-platform/">
116 Production-grade AI & general observability.
117 </Card>
118 
107119 <Card title="Sentry" image="https://platform.claude.com/docs/images/partner-logos/sentry.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/sentry-dark.svg" imageHeight={25} href="https://sentry.io/cookbook/trace-claude-platform-with-opentelemetry/">
108120 Debug your broken code and misbehaving agents.
121 </Card>
122 
123 <Card title="Snowflake" image="https://platform.claude.com/docs/images/partner-logos/snowflake.svg" imageHeight={30} imageOffsetLeft={2} href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-ai-gateway/observability#trace-claude-requests">
124 AI Data Cloud.
109125 </Card>
110126 
111127 <Card title="Splunk" image="https://platform.claude.com/docs/images/partner-logos/splunk.svg" imageDark="https://platform.claude.com/docs/images/partner-logos/splunk-dark.svg" imageHeight={27} imageOffsetLeft={1} href="https://agent-observability-docs.splunk.com/sdk-api/third-party-integrations/claude">

manage-claude/opentelemetry/api Changed · +11 / -11 lines

from line 62
6262You can also add a destination with the [Admin API](https://platform.claude.com/docs/en/manage-claude/admin-api) instead of the Console. Sign in to the CLI as an admin, or use another [Admin API credential](https://platform.claude.com/docs/en/manage-claude/admin-api#authentication), such as an [Admin API key](https://platform.claude.com/docs/en/manage-claude/admin-api-keys) or a service account key in `ANTHROPIC_API_KEY`. On Claude Platform on AWS, send the requests to `https://aws-external-anthropic.{region}.api.aws` instead, signed with your AWS credentials as in [Making requests](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws#making-requests). There, your [IAM policy](https://platform.claude.com/docs/en/api/claude-platform-on-aws-iam-actions) determines who can manage destinations. A request can include only the workspaces of your AWS account in that Region.
6363 
6464<CodeGroup>
65 ```bash CLI
66 # Sign in once as an admin, which opens a browser.
67 ant auth login --profile admin --scope "org:admin"
68 ant --profile admin beta:organization:telemetry-destinations create \
69 --endpoint-url https://gateway.example.com/otlp \
70 --headers '{authorization: "Bearer YOUR_OBSERVABILITY_API_KEY"}' \
71 --traces.content-mode metadata_only \
72 --workspaces.type all
73 ```
74 
7565 ```bash cURL
7666 curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/telemetry_destinations" \
7767 -H "x-api-key: $ANTHROPIC_API_KEY" \
from line 76
8676 }'
8777 ```
8878 
79 ```bash CLI
80 # Sign in once as an admin, which opens a browser.
81 ant auth login --profile admin --scope "org:admin"
82 ant --profile admin beta:organization:telemetry-destinations create \
83 --endpoint-url https://gateway.example.com/otlp \
84 --headers '{authorization: "Bearer YOUR_OBSERVABILITY_API_KEY"}' \
85 --traces.content-mode metadata_only \
86 --workspaces.type all
87 ```
88 
8989 ```python Python
9090 client = anthropic.Anthropic()
9191 
from line 221
221221* **Selected:** Only the workspaces that you select.
222222* **All except:** Like **All**, but without the workspaces that you select.
223223 
224For **Selected** or **All except**, select the workspaces in the search box that appears under the options. To change a destination's workspaces later, open its **More actions** menu on the [OpenTelemetry settings page](https://platform.claude.com/settings/telemetry-destinations) in the Console while the **Workspace** filter shows **All**. Click **Edit**, then choose another **Workspaces** option or change the workspaces in the search box.
224For **Selected** or **All except**, select the workspaces in the search box that appears under the options. To change a destination's workspaces later, open its **More actions** menu on the [OpenTelemetry settings page](https://platform.claude.com/settings/telemetry-destinations) in the Console while the **Workspace** filter shows **All**. Click **Edit**, then select another **Workspaces** option or change the workspaces in the search box.
225225 
226226If you have the developer role in the organization and the Workspace Admin role in a workspace, you can add that workspace to a **Selected** destination or remove it. Set the **Workspace** filter to that workspace, then open the destination's **More actions** menu. On Claude Platform on AWS, the Console has no Workspace Admin role. There, your [IAM policy](https://platform.claude.com/docs/en/api/claude-platform-on-aws-iam-actions) determines who can add a workspace to a destination or remove it.
227227 

manage-claude/opentelemetry/sdk Changed · +11 / -4 lines

from line 11
1111 Claude API: beta
1212 Claude Platform on AWS: beta
1313 Amazon Bedrock: beta
14 Google Cloud: beta
14 Claude Models on Google Cloud (Google managed): beta
1515 Microsoft Foundry: beta
1616 rows:
1717 - label: Works with
from line 481
481481 .build();
482482 
483483 // Turn spans off for one client.
484 var quietClient = AnthropicOkHttpClient.builder().fromEnv().openTelemetry(false).build();
484 var quietClient = AnthropicOkHttpClient.builder()
485 .fromEnv()
486 .openTelemetry(false)
487 .build();
485488 
486489 // Set a conversation ID in the request options of one call.
487490 var params = MessageCreateParams.builder()
from line 493
490493 .addUserMessage("Hello, Claude")
491494 .build();
492495 var message = client.messages().create(params, RequestOptions.builder()
493 .openTelemetry(OpenTelemetryRequestOptions.builder().conversationId("conv_42").build())
496 .openTelemetry(OpenTelemetryRequestOptions.builder()
497 .conversationId("conv_42")
498 .build())
494499 .build());
495500 ```
496501 
from line 630
625630 var envOptions = OpenTelemetryOptions.fromEnv();
626631 var client = AnthropicOkHttpClient.builder()
627632 .fromEnv()
628 .openTelemetry(envOptions.toBuilder().propagation(false).build())
633 .openTelemetry(envOptions.toBuilder()
634 .propagation(false)
635 .build())
629636 .build();
630637 ```
631638 

manage-claude/plugins-api Changed · +7 / -7 lines

from line 40
4040* Your primary owner creates an Admin API key with the `read:plugins` scope, the `write:plugins` scope, or both in [claude.ai > Organization settings > API](https://claude.ai/admin-settings/api-access). See [Create a key for a Claude Enterprise organization](https://platform.claude.com/docs/en/manage-claude/admin-api-keys#create-a-key-for-a-claude-enterprise-organization).
4141* Every request carries three headers: `x-api-key`, `anthropic-version: 2023-06-01`, and `anthropic-beta: ce-plugins-2026-09-01`.
4242 
43The SDK exposes these endpoints under `client.beta.organization` (csharp, go: `client.Beta.Organization`; java: `client.beta().organization()`; php: `$client->beta->organization`), and the [`ant` CLI](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/quickstart) under `ant beta:organization`; they send the `anthropic-version` and `anthropic-beta` headers for you. The examples on this page use the SDK's default client, which, like the CLI, reads the Admin API key from the `ANTHROPIC_API_KEY` environment variable; the curl examples read the key from the same variable and pass it in the `x-api-key` header. Where an SDK or the CLI fetches more pages for you, `limit` sets the page size, not the total (see [Pagination](https://platform.claude.com/docs/en/manage-claude/plugins-api#pagination)).
43In Python, TypeScript, C#, Go, Java, PHP, and Ruby, the SDK exposes these endpoints under `client.beta.organization` (csharp, go: `client.Beta.Organization`; java: `client.beta().organization()`; php: `$client->beta->organization`), and the [`ant` CLI](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/quickstart) under `ant beta:organization`; they send the `anthropic-version` and `anthropic-beta` headers for you. The examples on this page use the SDK's default client, which, like the CLI, reads the Admin API key from the `ANTHROPIC_API_KEY` environment variable; the curl examples read the key from the same variable and pass it in the `x-api-key` header. Where an SDK or the CLI fetches more pages for you, `limit` sets the page size, not the total (see [Pagination](https://platform.claude.com/docs/en/manage-claude/plugins-api#pagination)).
4444 
4545API keys belong to the organization and keep working after the person who created them leaves. Do not share them or check them into source control.
4646 
from line 762
762762 void main() throws Exception {
763763 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
764764 
765 // Build the `files[]` part by hand so the archive is sent with its filename,
765 // Build the `files[]` part by hand so the archive is sent with its file name,
766766 // which the upload requires.
767767 var archive = MultipartField.<List<InputStream>>builder()
768768 .value(List.of(Files.newInputStream(Path.of("dist/sales-toolkit.zip"))))
from line 944
944944 }
945945 defer skillMd.Close()
946946 
947 // anthropic.File keeps each file's path within the plugin as the part's filename;
947 // anthropic.File keeps each file's path within the plugin as the part's file name;
948948 // a bare *os.File would be sent under its base name and the manifest would not be found.
949949 plugin, err := client.Beta.Organization.Plugins.New(context.Background(), anthropic.BetaOrganizationPluginNewParams{
950950 Files: []io.Reader{
from line 968
968968 void main() throws Exception {
969969 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
970970 
971 // The `files` setters on PluginCreateParams take one filename for the whole
971 // The `files` setters on PluginCreateParams take one file name for the whole
972972 // list, so upload the plugin as one archive instead: a single hand-built
973 // part that carries the archive's filename, which the upload requires.
973 // part that carries the archive's file name, which the upload requires.
974974 var archive = MultipartField.<List<InputStream>>builder()
975975 .value(List.of(Files.newInputStream(Path.of("dist/sales-toolkit.zip"))))
976976 .filename("sales-toolkit.zip")
from line 1624
16241624 void main() throws Exception {
16251625 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
16261626 
1627 // Build the `files[]` part by hand so the archive is sent with its filename,
1627 // Build the `files[]` part by hand so the archive is sent with its file name,
16281628 // which the upload requires.
16291629 var archive = MultipartField.<List<InputStream>>builder()
16301630 .value(List.of(Files.newInputStream(Path.of("dist/sales-toolkit.zip"))))
from line 3086
30863086 void main() {
30873087 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
30883088 
3089 // Pass a Path so the `archive` part is sent with a filename, which this endpoint requires.
3089 // Pass a Path so the `archive` part is sent with a file name, which this endpoint requires.
30903090 var params = PluginMarketplaceValidateArchiveParams.builder()
30913091 .archive(Path.of("marketplace.zip"))
30923092 .build();

manage-claude/wif-providers/aws Changed · +177 / -175 lines

from line 135
135135 }'
136136 ```
137137 
138 ```bash CLI
139 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
140 aws sts get-web-identity-token \
141 --region us-east-1 \
142 --audience "https://api.anthropic.com" \
143 --signing-algorithm RS256 \
144 --duration-seconds 900 \
145 --query WebIdentityToken --output text > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
146 
147 # ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
148 # ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID are read from the environment
149 ant messages create \
150 --model claude-opus-5-5 \
151 --max-tokens 1024 \
152 --message '{role: user, content: "Hello from AWS"}'
153 ```
154 
138155 ```python Python
139156 import os
140157 
from line 230
213230 }
214231 ```
215232 
233 ```csharp C#
234 using Amazon.SecurityToken;
235 using Amazon.SecurityToken.Model;
236 // ...
237 using Anthropic.Credentials;
238 // ...
239 
240 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
241 {
242 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
243 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
244 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
245 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
246 IdentityTokenProvider = new StsTokenProvider(),
247 });
248 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
249 
250 var message = await client.Messages.Create(new()
251 {
252 Model = Model.ClaudeOpus5_5,
253 MaxTokens = 1024,
254 Messages = [new UserMessageParam { Content = "Hello from AWS" }],
255 });
256 foreach (var block in message.Content)
257 {
258 if (block.Value is TextBlock textBlock)
259 {
260 Console.WriteLine(textBlock.Text);
261 }
262 }
263 
264 class StsTokenProvider : IIdentityTokenProvider
265 {
266 private readonly AmazonSecurityTokenServiceClient _sts = new(Amazon.RegionEndpoint.USEast1);
267 
268 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
269 {
270 var resp = await _sts.GetWebIdentityTokenAsync(new GetWebIdentityTokenRequest
271 {
272 Audience = ["https://api.anthropic.com"],
273 SigningAlgorithm = "RS256",
274 DurationSeconds = 900,
275 }, ct);
276 return resp.WebIdentityToken;
277 }
278 }
279 ```
280 
216281 ```go Go
217282 ctx := context.TODO()
218283 cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
from line 337
272337 import software.amazon.awssdk.services.sts.model.GetWebIdentityTokenRequest;
273338 
274339 void main() {
275 StsClient sts = StsClient.builder().region(Region.US_EAST_1).build();
340 StsClient sts = StsClient.builder()
341 .region(Region.US_EAST_1)
342 .build();
276343 
277344 // The Java SDK takes the token itself or a file path, not a function; with
278345 // source("value"), path(...) holds the token. The SDK sends that same token
from line 348
281348 // process, pass source("file") and write a new token to the file before
282349 // each refresh.
283350 String stsToken = sts.getWebIdentityToken(
284 GetWebIdentityTokenRequest.builder()
285 .audience("https://api.anthropic.com")
286 .signingAlgorithm("RS256")
287 .durationSeconds(900)
288 .build())
289 .webIdentityToken();
351 GetWebIdentityTokenRequest.builder()
352 .audience("https://api.anthropic.com")
353 .signingAlgorithm("RS256")
354 .durationSeconds(900)
355 .build())
356 .webIdentityToken();
290357 
291358 AnthropicClient client = AnthropicOkHttpClient.builder()
292 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
293 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
294 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
295 .authentication(AuthenticationConfig.builder()
296 .type(AuthenticationType.OIDC_FEDERATION)
297 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
298 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
299 .identityToken(IdentityTokenConfig.builder()
300 .source("value")
301 .path(stsToken)
302 .build())
303 .build())
304 .build()))
305 .build();
359 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
360 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
361 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
362 .authentication(AuthenticationConfig.builder()
363 .type(AuthenticationType.OIDC_FEDERATION)
364 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
365 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
366 .identityToken(IdentityTokenConfig.builder()
367 .source("value")
368 .path(stsToken)
369 .build())
370 .build())
371 .build()))
372 .build();
306373 
307374 var message = client.messages().create(MessageCreateParams.builder()
308 .model(Model.CLAUDE_OPUS_5_5)
309 .maxTokens(1024)
310 .addUserMessage("Hello from AWS")
311 .build());
375 .model(Model.CLAUDE_OPUS_5_5)
376 .maxTokens(1024)
377 .addUserMessage("Hello from AWS")
378 .build());
312379 
313380 IO.println(message.content());
314381 }
315382 ```
316383 
317 ```csharp C#
318 using Amazon.SecurityToken;
319 using Amazon.SecurityToken.Model;
320 // ...
321 using Anthropic.Credentials;
322 // ...
323 
324 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
325 {
326 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
327 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
328 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
329 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
330 IdentityTokenProvider = new StsTokenProvider(),
331 });
332 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
333 
334 var message = await client.Messages.Create(new()
335 {
336 Model = Model.ClaudeOpus5_5,
337 MaxTokens = 1024,
338 Messages = [new UserMessageParam { Content = "Hello from AWS" }],
339 });
340 foreach (var block in message.Content)
341 {
342 if (block.Value is TextBlock textBlock)
343 {
344 Console.WriteLine(textBlock.Text);
345 }
346 }
347 
348 class StsTokenProvider : IIdentityTokenProvider
349 {
350 private readonly AmazonSecurityTokenServiceClient _sts = new(Amazon.RegionEndpoint.USEast1);
351 
352 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
353 {
354 var resp = await _sts.GetWebIdentityTokenAsync(new GetWebIdentityTokenRequest
355 {
356 Audience = ["https://api.anthropic.com"],
357 SigningAlgorithm = "RS256",
358 DurationSeconds = 900,
359 }, ct);
360 return resp.WebIdentityToken;
361 }
362 }
363 ```
364 
365 ```bash CLI
366 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
367 aws sts get-web-identity-token \
368 --region us-east-1 \
369 --audience "https://api.anthropic.com" \
370 --signing-algorithm RS256 \
371 --duration-seconds 900 \
372 --query WebIdentityToken --output text > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
373 
374 # ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
375 # ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID are read from the environment
376 ant messages create \
377 --model claude-opus-5-5 \
378 --max-tokens 1024 \
379 --message '{role: user, content: "Hello from AWS"}'
380 ```
381 
382384 ```php PHP
383385 use Anthropic\Client;
384386 use Anthropic\Lib\Credentials\CredentialResult;
from line 480
478480JSON
479481```
480482 
481A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common AWS-side cause is an `iss` mismatch (the per-account STS issuer URL must match the registered `issuer_url` exactly).
483A successful exchange returns an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common AWS-side cause is an `iss` mismatch (the per-account STS issuer URL must match the registered `issuer_url` exactly).
482484 
483485## Use EKS projected service-account tokens
484486 
from line 643
641643 }'
642644 ```
643645 
646 ```bash CLI
647 # Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
648 # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
649 ant messages create \
650 --model claude-opus-5-5 \
651 --max-tokens 1024 \
652 --message '{role: user, content: "Hello from EKS"}'
653 ```
654 
644655 ```python Python
645656 import os
646657 
from line 707
696707 }
697708 ```
698709 
710 ```csharp C#
711 // Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
712 // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
713 // from the pod's environment.
714 using var client = new AnthropicClient();
715 
716 var message = await client.Messages.Create(new()
717 {
718 Model = Model.ClaudeOpus5_5,
719 MaxTokens = 1024,
720 Messages = [new UserMessageParam { Content = "Hello from EKS" }],
721 });
722 foreach (var block in message.Content)
723 {
724 if (block.Value is TextBlock textBlock)
725 {
726 Console.WriteLine(textBlock.Text);
727 }
728 }
729 ```
730 
699731 ```go Go
700732 tokenPath := os.Getenv("ANTHROPIC_IDENTITY_TOKEN_FILE")
701733 
from line 770
738770 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
739771 
740772 var message = client.messages().create(MessageCreateParams.builder()
741 .model(Model.CLAUDE_OPUS_5_5)
742 .maxTokens(1024)
743 .addUserMessage("Hello from EKS")
744 .build());
773 .model(Model.CLAUDE_OPUS_5_5)
774 .maxTokens(1024)
775 .addUserMessage("Hello from EKS")
776 .build());
745777 
746778 IO.println(message.content());
747779 ```
748780 
749 ```csharp C#
750 // Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
751 // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
752 // from the pod's environment.
753 using var client = new AnthropicClient();
754 
755 var message = await client.Messages.Create(new()
756 {
757 Model = Model.ClaudeOpus5_5,
758 MaxTokens = 1024,
759 Messages = [new UserMessageParam { Content = "Hello from EKS" }],
760 });
761 foreach (var block in message.Content)
762 {
763 if (block.Value is TextBlock textBlock)
764 {
765 Console.WriteLine(textBlock.Text);
766 }
767 }
768 ```
769 
770 ```bash CLI
771 # Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
772 # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
773 ant messages create \
774 --model claude-opus-5-5 \
775 --max-tokens 1024 \
776 --message '{role: user, content: "Hello from EKS"}'
777 ```
778 
779781 ```php PHP
780782 use Anthropic\Client;
781783 
from line 835
833835JSON
834836```
835837 
836A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common EKS-side cause is the projected token's `aud` not matching the rule (project a token with `audience: https://api.anthropic.com`, not the IRSA default `sts.amazonaws.com`).
838A successful exchange returns an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common EKS-side cause is the projected token's `aud` not matching the rule (project a token with `audience: https://api.anthropic.com`, not the IRSA default `sts.amazonaws.com`).
837839 
838840## Scope your rule
839841 

manage-claude/wif-providers/azure Changed · +289 / -289 lines

from line 316
316316 }
317317 ```
318318 
319 ```csharp C#
320 using Anthropic.Credentials;
321 // ...
322 
323 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
324 {
325 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
326 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
327 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
328 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
329 IdentityTokenProvider = new EntraTokenProvider(),
330 });
331 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
332 
333 var message = await client.Messages.Create(new()
334 {
335 Model = Model.ClaudeOpus5_5,
336 MaxTokens = 1024,
337 Messages = [new UserMessageParam { Content = "Hello from Azure" }],
338 });
339 foreach (var block in message.Content)
340 {
341 if (block.Value is TextBlock textBlock)
342 {
343 Console.WriteLine(textBlock.Text);
344 }
345 }
346 
347 class EntraTokenProvider : IIdentityTokenProvider
348 {
349 // The audience app registration's identifier URI (see Register the token audience).
350 private const string Audience = "api://<APP_ID>";
351 
352 private static readonly HttpClient httpClient = new();
353 
354 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
355 {
356 // App Service, Functions, and Container Apps inject IDENTITY_ENDPOINT;
357 // VMs and VM Scale Sets use IMDS.
358 // With multiple user-assigned identities, append &client_id=<IDENTITY_CLIENT_ID>.
359 var identityEndpoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT");
360 using var request = identityEndpoint is not null
361 ? new HttpRequestMessage(HttpMethod.Get,
362 $"{identityEndpoint}?api-version=2019-08-01&resource={Audience}")
363 {
364 Headers = { { "X-IDENTITY-HEADER", Environment.GetEnvironmentVariable("IDENTITY_HEADER") } },
365 }
366 : new HttpRequestMessage(HttpMethod.Get,
367 $"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={Audience}")
368 {
369 Headers = { { "Metadata", "true" } },
370 };
371 using var response = await httpClient.SendAsync(request, ct);
372 response.EnsureSuccessStatusCode();
373 using var json = await JsonDocument.ParseAsync(
374 await response.Content.ReadAsStreamAsync(ct), default, ct);
375 return json.RootElement.GetProperty("access_token").GetString()!;
376 }
377 }
378 ```
379 
319380 ```go Go
320381 package main
321382 
from line 476
415476 // With multiple user-assigned identities, append &client_id=<IDENTITY_CLIENT_ID>.
416477 String identityEndpoint = System.getenv("IDENTITY_ENDPOINT");
417478 HttpRequest tokenRequest = identityEndpoint != null
418 ? HttpRequest.newBuilder(URI.create(identityEndpoint + "?api-version=2019-08-01&resource=" + audience))
419 .header("X-IDENTITY-HEADER", System.getenv("IDENTITY_HEADER"))
420 .build()
421 : HttpRequest.newBuilder(URI.create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + audience))
422 .header("Metadata", "true")
423 .build();
479 ? HttpRequest.newBuilder(URI.create(identityEndpoint + "?api-version=2019-08-01&resource=" + audience))
480 .header("X-IDENTITY-HEADER", System.getenv("IDENTITY_HEADER"))
481 .build()
482 : HttpRequest.newBuilder(URI.create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + audience))
483 .header("Metadata", "true")
484 .build();
424485 
425486 // The Java SDK takes the token itself or a file path, not a function; with
426487 // source("value"), path(...) holds the token. The SDK sends that same token
from line 493
432493 String entraToken = new ObjectMapper().readTree(response.body()).get("access_token").asText();
433494 
434495 AnthropicClient client = AnthropicOkHttpClient.builder()
435 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
436 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
437 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
438 .authentication(AuthenticationConfig.builder()
439 .type(AuthenticationType.OIDC_FEDERATION)
440 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
441 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
442 .identityToken(IdentityTokenConfig.builder()
443 .source("value")
444 .path(entraToken)
445 .build())
446 .build())
447 .build()))
448 .build();
496 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
497 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
498 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
499 .authentication(AuthenticationConfig.builder()
500 .type(AuthenticationType.OIDC_FEDERATION)
501 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
502 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
503 .identityToken(IdentityTokenConfig.builder()
504 .source("value")
505 .path(entraToken)
506 .build())
507 .build())
508 .build()))
509 .build();
449510 
450511 var message = client.messages().create(MessageCreateParams.builder()
451 .model(Model.CLAUDE_OPUS_5_5)
452 .maxTokens(1024)
453 .addUserMessage("Hello from Azure")
454 .build());
512 .model(Model.CLAUDE_OPUS_5_5)
513 .maxTokens(1024)
514 .addUserMessage("Hello from Azure")
515 .build());
455516 
456517 IO.println(message.content());
457518 }
458519 ```
459520 
460 ```csharp C#
461 using Anthropic.Credentials;
462 // ...
463 
464 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
465 {
466 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
467 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
468 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
469 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
470 IdentityTokenProvider = new EntraTokenProvider(),
471 });
472 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
473 
474 var message = await client.Messages.Create(new()
475 {
476 Model = Model.ClaudeOpus5_5,
477 MaxTokens = 1024,
478 Messages = [new UserMessageParam { Content = "Hello from Azure" }],
479 });
480 foreach (var block in message.Content)
481 {
482 if (block.Value is TextBlock textBlock)
483 {
484 Console.WriteLine(textBlock.Text);
485 }
486 }
487 
488 class EntraTokenProvider : IIdentityTokenProvider
489 {
490 // The audience app registration's identifier URI (see Register the token audience).
491 private const string Audience = "api://<APP_ID>";
492 
493 private static readonly HttpClient httpClient = new();
494 
495 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
496 {
497 // App Service, Functions, and Container Apps inject IDENTITY_ENDPOINT;
498 // VMs and VM Scale Sets use IMDS.
499 // With multiple user-assigned identities, append &client_id=<IDENTITY_CLIENT_ID>.
500 var identityEndpoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT");
501 using var request = identityEndpoint is not null
502 ? new HttpRequestMessage(HttpMethod.Get,
503 $"{identityEndpoint}?api-version=2019-08-01&resource={Audience}")
504 {
505 Headers = { { "X-IDENTITY-HEADER", Environment.GetEnvironmentVariable("IDENTITY_HEADER") } },
506 }
507 : new HttpRequestMessage(HttpMethod.Get,
508 $"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={Audience}")
509 {
510 Headers = { { "Metadata", "true" } },
511 };
512 using var response = await httpClient.SendAsync(request, ct);
513 response.EnsureSuccessStatusCode();
514 using var json = await JsonDocument.ParseAsync(
515 await response.Content.ReadAsStreamAsync(ct), default, ct);
516 return json.RootElement.GetProperty("access_token").GetString()!;
517 }
518 }
519 ```
520 
521521 ```php PHP
522522 use Anthropic\Client;
523523 use Anthropic\Lib\Credentials\CredentialResult;
from line 614
614614 
615615### Verify the setup
616616 
617From your Azure resource, run the cURL exchange shown in [Acquire and use the token](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#acquire-and-use-the-token) and confirm that `POST /v1/oauth/token` returns a `200` with an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason, then decode the Entra token (see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange) for the command) and check the most common Azure-side causes:
617From your Azure resource, run the cURL exchange shown in [Acquire and use the token](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#acquire-and-use-the-token) and confirm that `POST /v1/oauth/token` returns a `200` with an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason, then decode the Entra token (see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange) for the command) and check the most common Azure-side causes:
618618 
619619* **Issuer mismatch:** The registered `issuer_url` must match the token's `iss` claim exactly. A v2.0 token carries `https://login.microsoftonline.com/<TENANT_ID>/v2.0`; if the decoded `ver` claim is `1.0`, see [If your tokens are v1.0](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#if-your-tokens-are-v1-0).
620620* **Token lifetime:** Managed identity tokens carry up to 24 hours between `iat` and `exp`. If the issuer still has the wizard's `7500` (or the 1-hour default), raise `max_jwt_lifetime_seconds` to `86400` as described in [Configure Anthropic](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#configure-anthropic).
from line 951
951951 }
952952 ```
953953 
954 ```csharp C#
955 using Anthropic.Credentials;
956 // ...
957 
958 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
959 {
960 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
961 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
962 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
963 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
964 IdentityTokenProvider = new EntraFederationTokenProvider(),
965 });
966 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
967 
968 var message = await client.Messages.Create(new()
969 {
970 Model = Model.ClaudeOpus5_5,
971 MaxTokens = 1024,
972 Messages = [new UserMessageParam { Content = "Hello from Azure" }],
973 });
974 foreach (var block in message.Content)
975 {
976 if (block.Value is TextBlock textBlock)
977 {
978 Console.WriteLine(textBlock.Text);
979 }
980 }
981 
982 class EntraFederationTokenProvider : IIdentityTokenProvider
983 {
984 private static readonly HttpClient Http = new();
985 
986 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
987 {
988 var federatedToken = await File.ReadAllTextAsync(
989 Environment.GetEnvironmentVariable("AZURE_FEDERATED_TOKEN_FILE")!, ct);
990 var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
991 var form = new FormUrlEncodedContent(new Dictionary<string, string>
992 {
993 ["client_id"] = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID")!,
994 ["grant_type"] = "client_credentials",
995 ["scope"] = "api://<APP_ID>/.default",
996 ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
997 ["client_assertion"] = federatedToken,
998 });
999 var response = await Http.PostAsync(
1000 $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", form, ct);
1001 response.EnsureSuccessStatusCode();
1002 using var json = await JsonDocument.ParseAsync(
1003 await response.Content.ReadAsStreamAsync(ct), default, ct);
1004 return json.RootElement.GetProperty("access_token").GetString()!;
1005 }
1006 }
1007 ```
1008 
9541009 ```go Go
9551010 package main
9561011 
from line 1103
10481103 
10491104 void main() throws Exception {
10501105 var form = Map.of(
1051 "client_id", System.getenv("AZURE_CLIENT_ID"),
1052 "grant_type", "client_credentials",
1053 "scope", "api://<APP_ID>/.default",
1054 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
1055 "client_assertion", Files.readString(Path.of(System.getenv("AZURE_FEDERATED_TOKEN_FILE"))))
1056 .entrySet().stream()
1057 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
1058 .collect(Collectors.joining("&"));
1106 "client_id", System.getenv("AZURE_CLIENT_ID"),
1107 "grant_type", "client_credentials",
1108 "scope", "api://<APP_ID>/.default",
1109 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
1110 "client_assertion", Files.readString(Path.of(System.getenv("AZURE_FEDERATED_TOKEN_FILE"))))
1111 .entrySet().stream()
1112 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
1113 .collect(Collectors.joining("&"));
10591114 var request = HttpRequest.newBuilder(URI.create(
1060 "https://login.microsoftonline.com/" + System.getenv("AZURE_TENANT_ID") + "/oauth2/v2.0/token"))
1061 .header("content-type", "application/x-www-form-urlencoded")
1062 .POST(HttpRequest.BodyPublishers.ofString(form))
1063 .build();
1115 "https://login.microsoftonline.com/" + System.getenv("AZURE_TENANT_ID") + "/oauth2/v2.0/token"))
1116 .header("content-type", "application/x-www-form-urlencoded")
1117 .POST(HttpRequest.BodyPublishers.ofString(form))
1118 .build();
10641119 
10651120 // The Java SDK takes the token itself or a file path, not a function; with
10661121 // source("value"), path(...) holds the token. The SDK sends that same token
from line 1127
10721127 String entraToken = new ObjectMapper().readTree(response.body()).get("access_token").asText();
10731128 
10741129 AnthropicClient client = AnthropicOkHttpClient.builder()
1075 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
1076 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
1077 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
1078 .authentication(AuthenticationConfig.builder()
1079 .type(AuthenticationType.OIDC_FEDERATION)
1080 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
1081 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
1082 .identityToken(IdentityTokenConfig.builder()
1083 .source("value")
1084 .path(entraToken)
1085 .build())
1086 .build())
1087 .build()))
1088 .build();
1130 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
1131 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
1132 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
1133 .authentication(AuthenticationConfig.builder()
1134 .type(AuthenticationType.OIDC_FEDERATION)
1135 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
1136 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
1137 .identityToken(IdentityTokenConfig.builder()
1138 .source("value")
1139 .path(entraToken)
1140 .build())
1141 .build())
1142 .build()))
1143 .build();
10891144 
10901145 var message = client.messages().create(MessageCreateParams.builder()
1091 .model(Model.CLAUDE_OPUS_5_5)
1092 .maxTokens(1024)
1093 .addUserMessage("Hello from Azure")
1094 .build());
1146 .model(Model.CLAUDE_OPUS_5_5)
1147 .maxTokens(1024)
1148 .addUserMessage("Hello from Azure")
1149 .build());
10951150 
10961151 IO.println(message.content());
10971152 }
10981153 ```
10991154 
1100 ```csharp C#
1101 using Anthropic.Credentials;
1102 // ...
1103 
1104 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
1105 {
1106 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
1107 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
1108 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
1109 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
1110 IdentityTokenProvider = new EntraFederationTokenProvider(),
1111 });
1112 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
1113 
1114 var message = await client.Messages.Create(new()
1115 {
1116 Model = Model.ClaudeOpus5_5,
1117 MaxTokens = 1024,
1118 Messages = [new UserMessageParam { Content = "Hello from Azure" }],
1119 });
1120 foreach (var block in message.Content)
1121 {
1122 if (block.Value is TextBlock textBlock)
1123 {
1124 Console.WriteLine(textBlock.Text);
1125 }
1126 }
1127 
1128 class EntraFederationTokenProvider : IIdentityTokenProvider
1129 {
1130 private static readonly HttpClient Http = new();
1131 
1132 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
1133 {
1134 var federatedToken = await File.ReadAllTextAsync(
1135 Environment.GetEnvironmentVariable("AZURE_FEDERATED_TOKEN_FILE")!, ct);
1136 var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
1137 var form = new FormUrlEncodedContent(new Dictionary<string, string>
1138 {
1139 ["client_id"] = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID")!,
1140 ["grant_type"] = "client_credentials",
1141 ["scope"] = "api://<APP_ID>/.default",
1142 ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
1143 ["client_assertion"] = federatedToken,
1144 });
1145 var response = await Http.PostAsync(
1146 $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", form, ct);
1147 response.EnsureSuccessStatusCode();
1148 using var json = await JsonDocument.ParseAsync(
1149 await response.Content.ReadAsStreamAsync(ct), default, ct);
1150 return json.RootElement.GetProperty("access_token").GetString()!;
1151 }
1152 }
1153 ```
1154 
11551155 ```php PHP
11561156 use Anthropic\Client;
11571157 use Anthropic\Lib\Credentials\CredentialResult;
from line 1241
12411241 
12421242### Verify the setup
12431243 
1244From inside a labeled pod, run the cURL exchange shown in [Acquire and use the token on AKS](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#acquire-and-use-the-token-2) and confirm that `POST /v1/oauth/token` returns a `200` with an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason, then decode the Entra-issued token from step 1 (see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange) for the command) and check the most common Azure-side causes:
1244From inside a labeled pod, run the cURL exchange shown in [Acquire and use the token on AKS](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#acquire-and-use-the-token-2) and confirm that `POST /v1/oauth/token` returns a `200` with an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason, then decode the Entra-issued token from step 1 (see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange) for the command) and check the most common Azure-side causes:
12451245 
12461246* **Issuer mismatch:** The registered `issuer_url` must match the token's `iss` claim exactly. A v2.0 token carries `https://login.microsoftonline.com/<TENANT_ID>/v2.0`; if the decoded `ver` claim is `1.0`, see [If your tokens are v1.0](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#if-your-tokens-are-v1-0).
12471247* **Token lifetime:** If a tenant token-lifetime policy or CAE extends the `client_credentials` token past 7,500 seconds, raise the issuer's `max_jwt_lifetime_seconds` as described in [Configure Anthropic for Entra Workload Identity on AKS](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#configure-anthropic-2).

manage-claude/wif-providers/gcp Changed · +82 / -82 lines

from line 162
162162 }'
163163 ```
164164 
165 ```bash CLI
166 # Write the Google-signed identity token to a file the CLI can read
167 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
168 curl -sS -H "Metadata-Flavor: Google" \
169 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full" \
170 > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
171 
172 # ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
173 # ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID are read from the environment.
174 ant messages create \
175 --model claude-opus-5-5 \
176 --max-tokens 1024 \
177 --message '{role: user, content: "Hello from Cloud Run"}'
178 ```
179 
165180 ```python Python
166181 import os
167182 import anthropic
from line 248
233248 }
234249 ```
235250 
251 ```csharp C#
252 using Anthropic.Credentials;
253 // ...
254 
255 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
256 {
257 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
258 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
259 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
260 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
261 IdentityTokenProvider = new MetadataTokenProvider(),
262 });
263 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
264 
265 var message = await client.Messages.Create(new()
266 {
267 Model = Model.ClaudeOpus5_5,
268 MaxTokens = 1024,
269 Messages = [new UserMessageParam { Content = "Hello from Cloud Run" }],
270 });
271 foreach (var block in message.Content)
272 {
273 if (block.Value is TextBlock textBlock)
274 {
275 Console.WriteLine(textBlock.Text);
276 }
277 }
278 
279 class MetadataTokenProvider : IIdentityTokenProvider
280 {
281 private const string METADATA_URL =
282 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full";
283 
284 private static readonly HttpClient httpClient = new()
285 {
286 DefaultRequestHeaders = { { "Metadata-Flavor", "Google" } },
287 };
288 
289 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
290 {
291 return await httpClient.GetStringAsync(METADATA_URL, ct);
292 }
293 }
294 ```
295 
236296 ```go Go
237297 const audience = "https://api.anthropic.com"
238298 
from line 350
290350 void main() throws Exception {
291351 HttpClient http = HttpClient.newHttpClient();
292352 HttpRequest metadataRequest = HttpRequest.newBuilder()
293 .uri(URI.create("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"))
294 .header("Metadata-Flavor", "Google")
295 .build();
353 .uri(URI.create("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"))
354 .header("Metadata-Flavor", "Google")
355 .build();
296356 
297357 // The Java SDK takes the token itself or a file path, not a function; with
298358 // source("value"), path(...) holds the token. The SDK sends that same token
from line 363
303363 String googleIdentityToken = http.send(metadataRequest, HttpResponse.BodyHandlers.ofString()).body();
304364 
305365 AnthropicClient client = AnthropicOkHttpClient.builder()
306 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
307 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
308 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
309 .authentication(AuthenticationConfig.builder()
310 .type(AuthenticationType.OIDC_FEDERATION)
311 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
312 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
313 .identityToken(IdentityTokenConfig.builder()
314 .source("value")
315 .path(googleIdentityToken)
316 .build())
317 .build())
318 .build()))
319 .build();
366 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
367 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
368 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
369 .authentication(AuthenticationConfig.builder()
370 .type(AuthenticationType.OIDC_FEDERATION)
371 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
372 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
373 .identityToken(IdentityTokenConfig.builder()
374 .source("value")
375 .path(googleIdentityToken)
376 .build())
377 .build())
378 .build()))
379 .build();
320380 
321381 var message = client.messages().create(MessageCreateParams.builder()
322 .model(Model.CLAUDE_OPUS_5_5)
323 .maxTokens(1024)
324 .addUserMessage("Hello from Cloud Run")
325 .build());
382 .model(Model.CLAUDE_OPUS_5_5)
383 .maxTokens(1024)
384 .addUserMessage("Hello from Cloud Run")
385 .build());
326386 
327387 IO.println(message.content());
328388 }
329389 ```
330390 
331 ```csharp C#
332 using Anthropic.Credentials;
333 // ...
334 
335 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
336 {
337 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
338 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
339 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
340 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
341 IdentityTokenProvider = new MetadataTokenProvider(),
342 });
343 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
344 
345 var message = await client.Messages.Create(new()
346 {
347 Model = Model.ClaudeOpus5_5,
348 MaxTokens = 1024,
349 Messages = [new UserMessageParam { Content = "Hello from Cloud Run" }],
350 });
351 foreach (var block in message.Content)
352 {
353 if (block.Value is TextBlock textBlock)
354 {
355 Console.WriteLine(textBlock.Text);
356 }
357 }
358 
359 class MetadataTokenProvider : IIdentityTokenProvider
360 {
361 private const string METADATA_URL =
362 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full";
363 
364 private static readonly HttpClient httpClient = new()
365 {
366 DefaultRequestHeaders = { { "Metadata-Flavor", "Google" } },
367 };
368 
369 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
370 {
371 return await httpClient.GetStringAsync(METADATA_URL, ct);
372 }
373 }
374 ```
375 
376 ```bash CLI
377 # Write the Google-signed identity token to a file the CLI can read
378 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
379 curl -sS -H "Metadata-Flavor: Google" \
380 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full" \
381 > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
382 
383 # ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
384 # ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID are read from the environment.
385 ant messages create \
386 --model claude-opus-5-5 \
387 --max-tokens 1024 \
388 --message '{role: user, content: "Hello from Cloud Run"}'
389 ```
390 
391391 ```php PHP
392392 use Anthropic\Client;
393393 use Anthropic\Lib\Credentials\CredentialResult;
from line 465
465465 | jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson'
466466```
467467 
468Check that `iss` is `https://accounts.google.com`, `aud` is `https://api.anthropic.com`, and `email` matches the value in your federation rule. Then run the exchange from the previous section. A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common Google Cloud-side cause is the `email` claim missing (request the token with `format=full` so it is included).
468Check that `iss` is `https://accounts.google.com`, `aud` is `https://api.anthropic.com`, and `email` matches the value in your federation rule. Then run the exchange from the previous section. A successful exchange returns an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common Google Cloud-side cause is the `email` claim missing (request the token with `format=full` so it is included).
469469 
470470## Scope your rule
471471 

manage-claude/wif-providers/github-actions Changed · +45 / -45 lines

from line 170
170170 }'
171171 ```
172172 
173 ```bash CLI
174 # Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
175 # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
176 # from the job environment.
177 ant messages create \
178 --model claude-opus-5-5 \
179 --max-tokens 1024 \
180 --message '{role: user, content: "Hello, Claude"}'
181 ```
182 
173183 ```python Python
174184 import anthropic
175185 
from line 216
206216 }
207217 ```
208218 
219 ```csharp C#
220 // Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
221 // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
222 // from the job environment.
223 using var client = new AnthropicClient();
224 
225 var message = await client.Messages.Create(new()
226 {
227 Model = Model.ClaudeOpus5_5,
228 MaxTokens = 1024,
229 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
230 });
231 foreach (var block in message.Content)
232 {
233 if (block.Value is TextBlock textBlock)
234 {
235 Console.WriteLine(textBlock.Text);
236 }
237 }
238 ```
239 
209240 ```go Go
210241 // Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
211242 // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
from line 265
234265 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
235266 
236267 var message = client.messages().create(MessageCreateParams.builder()
237 .model(Model.CLAUDE_OPUS_5_5)
238 .maxTokens(1024)
239 .addUserMessage("Hello, Claude")
240 .build());
268 .model(Model.CLAUDE_OPUS_5_5)
269 .maxTokens(1024)
270 .addUserMessage("Hello, Claude")
271 .build());
241272 
242273 IO.println(message.content());
243274 ```
244275 
245 ```csharp C#
246 // Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
247 // ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
248 // from the job environment.
249 using var client = new AnthropicClient();
250 
251 var message = await client.Messages.Create(new()
252 {
253 Model = Model.ClaudeOpus5_5,
254 MaxTokens = 1024,
255 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
256 });
257 foreach (var block in message.Content)
258 {
259 if (block.Value is TextBlock textBlock)
260 {
261 Console.WriteLine(textBlock.Text);
262 }
263 }
264 ```
265 
266 ```bash CLI
267 # Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
268 # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
269 # from the job environment.
270 ant messages create \
271 --model claude-opus-5-5 \
272 --max-tokens 1024 \
273 --message '{role: user, content: "Hello, Claude"}'
274 ```
275 
276276 ```php PHP
277277 use Anthropic\Client;
278278 
from line 311
311311 
312312## Verify the setup
313313 
314A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. A denied exchange returns an opaque `401` `authentication_error` with the fixed message `Authentication failed`, whichever check failed; in most cases the deny reason is recorded on the attempt's entry in the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console, and [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange) walks the checks in order. The most common GitHub Actions-side cause is the `sub` claim format not matching (its trailing segment varies among `ref:...`, `environment:...`, and `pull_request` events); the history entry shows reason `match_subject_prefix`.
314A successful exchange returns an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. A denied exchange returns an opaque `401` `authentication_error` with the fixed message `Authentication failed`, whichever check failed; in most cases the deny reason is recorded on the attempt's entry in the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console, and [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange) walks the checks in order. The most common GitHub Actions-side cause is the `sub` claim format not matching (its trailing segment varies among `ref:...`, `environment:...`, and `pull_request` events); the history entry shows reason `match_subject_prefix`.
315315 
316316## Restrict which workflows can authenticate
317317 

manage-claude/wif-providers/kubernetes Changed · +44 / -44 lines

from line 155
155155 }'
156156 ```
157157 
158 ```bash CLI
159 # Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
160 # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
161 ant messages create \
162 --model claude-opus-5-5 \
163 --max-tokens 1024 \
164 --message '{role: user, content: "Hello, Claude"}'
165 ```
166 
158167 ```python Python
159168 import anthropic
160169 
from line 200
191200 }
192201 ```
193202 
203 ```csharp C#
204 // Reads ANTHROPIC_IDENTITY_TOKEN_FILE, ANTHROPIC_FEDERATION_RULE_ID,
205 // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID
206 // from the pod's environment.
207 using var client = new AnthropicClient();
208 
209 var message = await client.Messages.Create(new()
210 {
211 Model = Model.ClaudeOpus5_5,
212 MaxTokens = 1024,
213 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
214 });
215 foreach (var block in message.Content)
216 {
217 if (block.Value is TextBlock textBlock)
218 {
219 Console.WriteLine(textBlock.Text);
220 }
221 }
222 ```
223 
194224 ```go Go
195225 // Reads ANTHROPIC_IDENTITY_TOKEN_FILE, ANTHROPIC_FEDERATION_RULE_ID,
196226 // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID
from line 249
219249 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
220250 
221251 var message = client.messages().create(MessageCreateParams.builder()
222 .model(Model.CLAUDE_OPUS_5_5)
223 .maxTokens(1024)
224 .addUserMessage("Hello, Claude")
225 .build());
252 .model(Model.CLAUDE_OPUS_5_5)
253 .maxTokens(1024)
254 .addUserMessage("Hello, Claude")
255 .build());
226256 
227257 IO.println(message.content());
228258 ```
229259 
230 ```csharp C#
231 // Reads ANTHROPIC_IDENTITY_TOKEN_FILE, ANTHROPIC_FEDERATION_RULE_ID,
232 // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID
233 // from the pod's environment.
234 using var client = new AnthropicClient();
235 
236 var message = await client.Messages.Create(new()
237 {
238 Model = Model.ClaudeOpus5_5,
239 MaxTokens = 1024,
240 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
241 });
242 foreach (var block in message.Content)
243 {
244 if (block.Value is TextBlock textBlock)
245 {
246 Console.WriteLine(textBlock.Text);
247 }
248 }
249 ```
250 
251 ```bash CLI
252 # Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
253 # ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
254 ant messages create \
255 --model claude-opus-5-5 \
256 --max-tokens 1024 \
257 --message '{role: user, content: "Hello, Claude"}'
258 ```
259 
260260 ```php PHP
261261 use Anthropic\Client;
262262 
from line 291
291291 
292292## Verify the setup
293293 
294A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common Kubernetes-side cause is a JWKS key mismatch (for `inline` mode, re-fetch with `kubectl get --raw /openid/v1/jwks` and update the issuer).
294A successful exchange returns an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common Kubernetes-side cause is a JWKS key mismatch (for `inline` mode, re-fetch with `kubectl get --raw /openid/v1/jwks` and update the issuer).
295295 
296296## Scope your rule
297297 

manage-claude/wif-providers/okta Changed · +101 / -101 lines

from line 113
113113 }'
114114 ```
115115 
116 ```bash CLI
117 # 1. Request an access token from Okta and write it to a temp file.
118 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
119 curl -sS "$OKTA_ISSUER/v1/token" \
120 -d grant_type=client_credentials \
121 -d scope=anthropic.access \
122 -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
123 --data-urlencode client_assertion="$SIGNED_CLIENT_ASSERTION" \
124 | jq -r .access_token > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
125 
126 # 2. Call the Claude API. The CLI reads ANTHROPIC_FEDERATION_RULE_ID,
127 # ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and
128 # ANTHROPIC_IDENTITY_TOKEN_FILE and performs the exchange.
129 ant messages create \
130 --model claude-opus-5-5 \
131 --max-tokens 1024 \
132 --message '{role: user, content: "Hello, Claude"}'
133 ```
134 
116135 ```python Python
117136 import os
118137 import httpx2
from line 216
197216 }
198217 ```
199218 
219 ```csharp C#
220 using Anthropic.Credentials;
221 // ...
222 
223 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
224 {
225 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
226 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
227 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
228 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
229 IdentityTokenProvider = new OktaTokenProvider(),
230 });
231 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
232 
233 var message = await client.Messages.Create(new()
234 {
235 Model = Model.ClaudeOpus5_5,
236 MaxTokens = 1024,
237 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
238 });
239 foreach (var block in message.Content)
240 {
241 if (block.Value is TextBlock textBlock)
242 {
243 Console.WriteLine(textBlock.Text);
244 }
245 }
246 
247 class OktaTokenProvider : IIdentityTokenProvider
248 {
249 private static readonly HttpClient Http = new();
250 
251 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
252 {
253 var form = new FormUrlEncodedContent(new Dictionary<string, string>
254 {
255 ["grant_type"] = "client_credentials",
256 ["scope"] = "anthropic.access",
257 ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
258 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
259 ["client_assertion"] = BuildSignedClientAssertion(),
260 });
261 var response = await Http.PostAsync(
262 $"{Environment.GetEnvironmentVariable("OKTA_ISSUER")}/v1/token", form, ct);
263 response.EnsureSuccessStatusCode();
264 using var json = await JsonDocument.ParseAsync(
265 await response.Content.ReadAsStreamAsync(ct), default, ct);
266 return json.RootElement.GetProperty("access_token").GetString()!;
267 }
268 }
269 ```
270 
200271 ```go Go
201272 package main
202273 
from line 359
288359 
289360 void main() throws Exception {
290361 var form = Map.of(
291 "grant_type", "client_credentials",
292 "scope", "anthropic.access",
293 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
294 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
295 "client_assertion", buildSignedClientAssertion())
296 .entrySet().stream()
297 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
298 .collect(Collectors.joining("&"));
362 "grant_type", "client_credentials",
363 "scope", "anthropic.access",
364 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
365 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
366 "client_assertion", buildSignedClientAssertion())
367 .entrySet().stream()
368 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
369 .collect(Collectors.joining("&"));
299370 var request = HttpRequest.newBuilder(URI.create(System.getenv("OKTA_ISSUER") + "/v1/token"))
300 .header("content-type", "application/x-www-form-urlencoded")
301 .POST(HttpRequest.BodyPublishers.ofString(form))
302 .build();
371 .header("content-type", "application/x-www-form-urlencoded")
372 .POST(HttpRequest.BodyPublishers.ofString(form))
373 .build();
303374 
304375 // The Java SDK takes the token itself or a file path, not a function; with
305376 // source("value"), path(...) holds the token. The SDK sends that same token
from line 382
311382 String oktaToken = new ObjectMapper().readTree(response.body()).get("access_token").asText();
312383 
313384 AnthropicClient client = AnthropicOkHttpClient.builder()
314 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
315 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
316 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
317 .authentication(AuthenticationConfig.builder()
318 .type(AuthenticationType.OIDC_FEDERATION)
319 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
320 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
321 .identityToken(IdentityTokenConfig.builder()
322 .source("value")
323 .path(oktaToken)
324 .build())
325 .build())
326 .build()))
327 .build();
385 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
386 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
387 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
388 .authentication(AuthenticationConfig.builder()
389 .type(AuthenticationType.OIDC_FEDERATION)
390 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
391 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
392 .identityToken(IdentityTokenConfig.builder()
393 .source("value")
394 .path(oktaToken)
395 .build())
396 .build())
397 .build()))
398 .build();
328399 
329400 var message = client.messages().create(MessageCreateParams.builder()
330 .model(Model.CLAUDE_OPUS_5_5)
331 .maxTokens(1024)
332 .addUserMessage("Hello, Claude")
333 .build());
401 .model(Model.CLAUDE_OPUS_5_5)
402 .maxTokens(1024)
403 .addUserMessage("Hello, Claude")
404 .build());
334405 
335406 IO.println(message.content());
336407 }
337408 ```
338409 
339 ```csharp C#
340 using Anthropic.Credentials;
341 // ...
342 
343 var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
344 {
345 FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
346 OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
347 ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
348 WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
349 IdentityTokenProvider = new OktaTokenProvider(),
350 });
351 using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });
352 
353 var message = await client.Messages.Create(new()
354 {
355 Model = Model.ClaudeOpus5_5,
356 MaxTokens = 1024,
357 Messages = [new UserMessageParam { Content = "Hello, Claude" }],
358 });
359 foreach (var block in message.Content)
360 {
361 if (block.Value is TextBlock textBlock)
362 {
363 Console.WriteLine(textBlock.Text);
364 }
365 }
366 
367 class OktaTokenProvider : IIdentityTokenProvider
368 {
369 private static readonly HttpClient Http = new();
370 
371 public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
372 {
373 var form = new FormUrlEncodedContent(new Dictionary<string, string>
374 {
375 ["grant_type"] = "client_credentials",
376 ["scope"] = "anthropic.access",
377 ["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
378 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
379 ["client_assertion"] = BuildSignedClientAssertion(),
380 });
381 var response = await Http.PostAsync(
382 $"{Environment.GetEnvironmentVariable("OKTA_ISSUER")}/v1/token", form, ct);
383 response.EnsureSuccessStatusCode();
384 using var json = await JsonDocument.ParseAsync(
385 await response.Content.ReadAsStreamAsync(ct), default, ct);
386 return json.RootElement.GetProperty("access_token").GetString()!;
387 }
388 }
389 ```
390 
391 ```bash CLI
392 # 1. Request an access token from Okta and write it to a temp file.
393 export ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
394 curl -sS "$OKTA_ISSUER/v1/token" \
395 -d grant_type=client_credentials \
396 -d scope=anthropic.access \
397 -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
398 --data-urlencode client_assertion="$SIGNED_CLIENT_ASSERTION" \
399 | jq -r .access_token > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
400 
401 # 2. Call the Claude API. The CLI reads ANTHROPIC_FEDERATION_RULE_ID,
402 # ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and
403 # ANTHROPIC_IDENTITY_TOKEN_FILE and performs the exchange.
404 ant messages create \
405 --model claude-opus-5-5 \
406 --max-tokens 1024 \
407 --message '{role: user, content: "Hello, Claude"}'
408 ```
409 
410410 ```php PHP
411411 use Anthropic\Client;
412412 use Anthropic\Lib\Credentials\CredentialResult;
from line 496
496496 
497497## Verify the setup
498498 
499A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common Okta-side cause is an `issuer_url` mismatch (it must include the `/oauth2/<auth-server-id>` path; the Okta org authorization server is not usable).
499A successful exchange returns an `access_token` starting with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console for the deny reason and see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange); the most common Okta-side cause is an `issuer_url` mismatch (it must include the `/oauth2/<auth-server-id>` path; the Okta org authorization server is not usable).
500500 
501501## Scope your rule
502502 
Feedback