Before changedapi/migration-2026-10-21
Nearest release: v2.1.312, published under an hour before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Recorded here
Lines+57added
Lines−57removed
From line
10
where the diff opens
First seen
23 Oct 2026
this site's first read of the page
Recorded edits2to this page, all time
The whole hunk
from line 10, old and new numbered
/
from line 10
1010
1111<Steps>
1212 <Step title="Switch to 2026-10-21">
13 If you use an SDK, your requests use the API version that was current when your SDK's major version was released. The versions shown for your language are the first to use `2026-10-21`, so upgrade to them. If you call the API without an SDK, set the `anthropic-version` header to `2026-10-21`.
13 If you use an SDK, your requests use the API version that was current when your SDK’s major version was released. The versions shown for your language are the first to use `2026-10-21`, so upgrade to them. If you call the API without an SDK, set the `anthropic-version` header to `2026-10-21`.
1414
1515 <Tabs>
1616 <Tab title="cURL">
from line 32
3232 <Tab title="CLI">
3333 Upgrade the `ant` CLI to version 2.0.0. The Homebrew command and the release downloads stay the same.
3434
35 Go requires the path of a module at major version 2 or later to end in that version, so the CLI's module path is now `github.com/anthropics/anthropic-cli/v2`. If you install with `go install`, add `/v2` to the path:
35 Go requires the path of a module at major version 2 or later to end in that version, so the CLI’s module path is now `github.com/anthropics/anthropic-cli/v2`. If you install with `go install`, add `/v2` to the path:
3636
3737 ```bash
3838 # Before
from line 108
108108
109109Affects you if you send `citations` on `text` blocks in `user` messages, including inside `search_result` blocks.
110110
111In `2026-10-21`, a request that sends a `text` block with a non-empty `citations` array in a `user` message returns a 400 error. Remove `citations` from those blocks. Citations on `text` blocks in `assistant` messages, such as Claude's earlier replies that you send back, are still accepted, and so is the `"citations": {"enabled": true}` setting on documents and search results.
111In `2026-10-21`, a request that sends a `text` block with a non-empty `citations` array in a `user` message returns a 400 error. Remove `citations` from those blocks. Citations on `text` blocks in `assistant` messages, such as Claude’s earlier replies that you send back, are still accepted, and so is the `"citations": {"enabled": true}` setting on documents and search results.
112112
113113#### Sampling parameters are removed
114114
115115Affects you if you send `temperature`, `top_p`, or `top_k` in Messages requests or Message Batch requests.
116116
117In `2026-10-21`, a request that sends `temperature`, `top_p`, or `top_k` returns a 400 error, even at the parameter's default value.
117In `2026-10-21`, a request that sends `temperature`, `top_p`, or `top_k` returns a 400 error, even at the parameter’s default value.
118118
119119Remove the three parameters from your requests.
120120
from line 135
135135| [RBAC groups](https://platform.claude.com/docs/en/api/beta/organization/rbac_groups/list): create, list, retrieve, and update | `roles` | `role_ids` |
136136| [RBAC group members](https://platform.claude.com/docs/en/api/beta/organization/rbac_groups/members/list): list and add, and the response to [removing a member](https://platform.claude.com/docs/en/api/beta/organization/rbac_groups/members/remove) | `group_id` | `rbac_group_id` |
137137| [RBAC roles](https://platform.claude.com/docs/en/api/beta/organization/rbac_roles/list): list and retrieve | `name` | `display_name` |
138| [Organization](https://platform.claude.com/docs/en/api/organization/rate_limits/list) and [workspace](https://platform.claude.com/docs/en/api/organization/workspaces/rate_limits/list) rate limits: list, and the response to setting a workspace's rate limits | `group_type` | `group.type` |
138| [Organization](https://platform.claude.com/docs/en/api/organization/rate_limits/list) and [workspace](https://platform.claude.com/docs/en/api/organization/workspaces/rate_limits/list) rate limits: list, and the response to setting a workspace’s rate limits | `group_type` | `group.type` |
139139| [API keys](https://platform.claude.com/docs/en/api/organization/api_keys/list): list, retrieve, and update | `workspace_id` | `scope` (different values; see [`workspace_id` on API keys](https://platform.claude.com/docs/en/api/migration-2026-10-21#api-key-workspace-id)) |
140140
141141Claude Enterprise Analytics API:
from line 151
151151
152152Affects you if your code reads `workspace_id` on API keys.
153153
154In `2023-06-01`, an API key's `workspace_id` is the ID of the workspace the key is bound to, or `null` for a key bound to the Default Workspace and for a key that isn't bound to one workspace (an all-workspaces key).
154In `2023-06-01`, an API key’s `workspace_id` is the ID of the workspace the key is bound to, or `null` for a key bound to the Default Workspace and for a key that isn’t bound to one workspace (an all-workspaces key).
155155
156In `2026-10-21`, `workspace_id` is removed. Read `scope` instead: for a key bound to a workspace, including the Default Workspace, `scope.workspace_id` is that workspace's real ID, and an all-workspaces key has a `scope` of `{"type": "organization"}`, with no `workspace_id`.
156In `2026-10-21`, `workspace_id` is removed. Read `scope` instead: for a key bound to a workspace, including the Default Workspace, `scope.workspace_id` is that workspace’s real ID, and an all-workspaces key has a `scope` of `{"type": "organization"}`, with no `workspace_id`.
157157
158If your code treated a `null` `workspace_id` as the Default Workspace, compare `scope.workspace_id` with your Default Workspace's ID instead. To find that ID, call [List Workspaces](https://platform.claude.com/docs/en/api/organization/workspaces/list) with `include_default=true` and take the workspace named `"Default"`.
158If your code treated a `null` `workspace_id` as the Default Workspace, compare `scope.workspace_id` with your Default Workspace’s ID instead. To find that ID, call [List Workspaces](https://platform.claude.com/docs/en/api/organization/workspaces/list) with `include_default=true` and take the workspace named `"Default"`.
159159
160160## SDK and CLI changes
161161
162Earlier SDK and CLI versions sent `anthropic-version: 2023-06-01` with every request. The versions under [Switch to 2026-10-21](https://platform.claude.com/docs/en/api/migration-2026-10-21#upgrade) send `2026-10-21`, including their Amazon Bedrock and Google Cloud clients. The changes in [Breaking changes](https://platform.claude.com/docs/en/api/migration-2026-10-21#breaking-changes) apply to them too: the removed parameters and the old field names are also gone from the SDK's types and from the CLI's flags and output.
162Earlier SDK and CLI versions sent `anthropic-version: 2023-06-01` with every request. The versions under [Switch to 2026-10-21](https://platform.claude.com/docs/en/api/migration-2026-10-21#upgrade) send `2026-10-21`, including their Amazon Bedrock and Google Cloud clients. The changes in [Breaking changes](https://platform.claude.com/docs/en/api/migration-2026-10-21#breaking-changes) apply to them too: the removed parameters and the old field names are also gone from the SDK’s types and from the CLI’s flags and output.
163163
164164### `MessageParam` is a union with one type for each role
165165
from line 200
200200
201201 Two more apply to `BetaMessageParam` only:
202202
203 * `clear_at` and `output_config` are keys of `BetaSystemMessageParam`, so a user or assistant message can't have them.
203 * `clear_at` and `output_config` are keys of `BetaSystemMessageParam`, so a user or assistant message can’t have them.
204204 * `content` is optional in `BetaSystemMessageParam`, so pyright reports `message["content"]`. Read it with `message.get("content")`, or check first that `message["role"]` is not `"system"`.
205205 </Tab>
206206
from line 233
233233
234234 The type `BetaMessageParam["content"]` includes `undefined` too. For the content of a user or assistant message, use `BetaMessageContentParam`.
235235
236 An interface can't extend a union, and a class can't implement one. Use a type alias, or name the type of one role instead.
236 An interface can’t extend a union, and a class can’t implement one. Use a type alias, or name the type of one role instead.
237237
238238 ```typescript
239239 // Before
from line 267
267267 * A system message has a content class of its own. A `SystemMessageParamContent` holds a string or a list of `TextBlockParam`, and a `BetaSystemMessageParamContent` holds a string or a list of `SystemMessageContentBlock`. A `List<ContentBlockParam>` or a `List<BetaContentBlockParam>` no longer converts to it.
268268 * `ClearAt` and `OutputConfig` are on `BetaSystemMessageParam` only.
269269
270 When you read a message, the union still has `Role` and `Content`, but you can't set them, so a `with` expression goes on the class for the role. `Role` now holds a `MessageParamRole` or a `BetaMessageParamRole`. `BetaMessageParam.Content` can be `null`, because `Content` is optional on `BetaSystemMessageParam`.
270 When you read a message, the union still has `Role` and `Content`, but you can’t set them, so a `with` expression goes on the class for the role. `Role` now holds a `MessageParamRole` or a `BetaMessageParamRole`. `BetaMessageParam.Content` can be `null`, because `Content` is optional on `BetaSystemMessageParam`.
271271
272272 ```csharp
273273 // Before
from line 324
324324 <Tab title="Java">
325325 In v2, `MessageParam` was a single class with a `role` and a `content`. In v3 it is a union of the three classes, and `MessageParam.Content` is now the top-level `MessageContentParam`. `BetaMessageParam` changes the same way, and its classes have the same names with a `Beta` prefix. The JSON that the SDK sends is the same, except that a beta system message now has `role` before `content`.
326326
327 `addUserMessage`, `addAssistantMessage`, and `addSystemMessage` with a `String`, `addMessage` with a `Message` or a `MessageParam`, and `Message.toParam()` don't change.
327 `addUserMessage`, `addAssistantMessage`, and `addSystemMessage` with a `String`, `addMessage` with a `Message` or a `MessageParam`, and `Message.toParam()` don’t change.
328328
329329 `MessageParam.builder()` and `toBuilder()` are gone. Call the factory method for the role, which is `ofUser`, `ofAssistant`, or `ofSystem`:
330330
from line 347
347347 | `addAssistantMessageOfBlockParams` | `addAssistantMessageOfBetaContentBlockParams` | `addAssistantMessageOfContentBlockParams` |
348348 | `addSystemMessageOfBlockParams` | `addSystemMessageOfBetaContentBlockParams` | `addSystemMessageOfBlocks` |
349349
350 On the content class, `BlockParams` (`BetaContentBlockParams` in beta) becomes `ContentBlockParams` in `ofBlockParams`, `blockParams()`, `isBlockParams()`, `asBlockParams()`, and the visitor's `visitBlockParams`.
350 On the content class, `BlockParams` (`BetaContentBlockParams` in beta) becomes `ContentBlockParams` in `ofBlockParams`, `blockParams()`, `isBlockParams()`, `asBlockParams()`, and the visitor’s `visitBlockParams`.
351351
352352 A system message has its own content class, `SystemMessageParam.Content`, which `addSystemMessage` now takes. Its blocks are `TextBlockParam`, not `ContentBlockParam`. In beta they are `BetaSystemMessageParam.Content.SystemMessageContentBlock`, which has `ofText`, `ofToolAddition`, and `ofToolRemoval`.
353353
from line 359
359359 builder.addSystemMessageOfBlocks(List.of(textBlock));
360360 ```
361361
362 When you read a message, `role()` doesn't change. `content()` returns a `MessageContentParam`, or an `Optional<BetaMessageContentParam>` in beta, where a system message can have no content. `_role()`, `_content()`, and `_additionalProperties()` are now on the class for the role, which `asUser()`, `asAssistant()`, or `asSystem()` returns.
362 When you read a message, `role()` doesn’t change. `content()` returns a `MessageContentParam`, or an `Optional<BetaMessageContentParam>` in beta, where a system message can have no content. `_role()`, `_content()`, and `_additionalProperties()` are now on the class for the role, which `asUser()`, `asAssistant()`, or `asSystem()` returns.
363363 </Tab>
364364
365365 <Tab title="PHP">
366 The classes are in `Anthropic\Messages`, and the beta classes in `Anthropic\Beta\Messages`. Messages you write as arrays don't change, and still need their `'role'`.
366 The classes are in `Anthropic\Messages`, and the beta classes in `Anthropic\Beta\Messages`. Messages you write as arrays don’t change, and still need their `'role'`.
367367
368 `MessageParam::with()` and `BetaMessageParam::with()` remain, and return the class that goes with `role`. A call with named arguments needs no change. `role` is now the first parameter, so in a positional call swap `content` and `role`, or call `with()` on the role's class, which takes no `role`. For arguments in the old order PHP throws `UnhandledMatchError`, which PHPStan does not warn about, or a `TypeError` if `content` is a list or `role` is an enum case.
368 `MessageParam::with()` and `BetaMessageParam::with()` remain, and return the class that goes with `role`. A call with named arguments needs no change. `role` is now the first parameter, so in a positional call swap `content` and `role`, or call `with()` on the role’s class, which takes no `role`. For arguments in the old order PHP throws `UnhandledMatchError`, which PHPStan does not warn about, or a `TypeError` if `content` is a list or `role` is an enum case.
369369
370370 ```php
371371 // Before
from line 385
385385 function send(UserMessageParam|AssistantMessageParam|SystemMessageParam $message): void {}
386386 ```
387387
388 * `MessageParam::fromArray()` and `(new MessageParam())->withContent()` throw "Call to undefined method". Call them on the role's class, which sets `role` itself.
389 * `withRole()` takes only the string now: PHP throws a `TypeError` for a `Role` case. To give a message another role, build that role's class.
388 * `MessageParam::fromArray()` and `(new MessageParam())->withContent()` throw “Call to undefined method”. Call them on the role’s class, which sets `role` itself.
389 * `withRole()` takes only the string now: PHP throws a `TypeError` for a `Role` case. To give a message another role, build that role’s class.
390390 * `clearAt` and `outputConfig` exist only on `BetaSystemMessageParam`, and `BetaMessageParam::with()` drops them for the other two roles. The `BetaMessageParam\ClearAt` enum is now `BetaSystemMessageParam\ClearAt`.
391391 * `MessageParam\Content` is now `MessageContentParam`, and `BetaMessageParam\Content` is now `BetaMessageContentParam`. Their PHPStan types take the new name too: `ContentShape` is now `MessageContentParamShape`.
392392 * An array whose `'role'` is a `Role` case still works, but PHPStan now reports it. Write the string.
from line 393
393393 </Tab>
394394
395395 <Tab title="Ruby">
396 Messages you write as hashes don't change.
396 Messages you write as hashes don’t change.
397397
398398 In v2, `Anthropic::MessageParam` is a union of `Anthropic::UserMessageParam`, `Anthropic::AssistantMessageParam`, and `Anthropic::SystemMessageParam`. In the same way, `Anthropic::Beta::BetaMessageParam` is a union of its three classes.
399399
400400 `Anthropic::MessageParam.new(role: :user, content: "Hello, Claude")` still works, and returns an instance of the class for that role. You can also build that class yourself, with no `role:`, as `Anthropic::UserMessageParam.new(content: "Hello, Claude")`. Four things change for code that builds or inspects these instances:
401401
402 * `MessageParam.new` takes keywords only, so pass a hash as `MessageParam.new(**hash)`. It raises an `ArgumentError` when `role:` is missing or isn't one of the three roles.
402 * `MessageParam.new` takes keywords only, so pass a hash as `MessageParam.new(**hash)`. It raises an `ArgumentError` when `role:` is missing or isn’t one of the three roles.
403403 * `message.is_a?(Anthropic::MessageParam)` is `false`. A `when Anthropic::MessageParam` branch and `messages.grep(Anthropic::MessageParam)` still match.
404404 * An instance built with `.new` has no `:role` in its `to_h`, and `message[:role]` is `nil`. `message.role` still returns the role.
405405 * Only `BetaSystemMessageParam` has `clear_at` and `output_config`. On a user or assistant message they returned `nil`, and now raise a `NoMethodError`.
from line 471
471471 if (message.Type.GetString() == "agent.message") { }
472472 ```
473473
474 To tell the variants of a union apart, keep using its `TryPick*`, `Switch`, and `Match` methods, which don't change.
474 To tell the variants of a union apart, keep using its `TryPick*`, `Switch`, and `Match` methods, which don’t change.
475475 </Tab>
476476
477477 <Tab title="Go">
from line 518
518518 * `BetaManagedAgentsMultiagentRosterEntryParamsOfBetaManagedAgentsAgents`
519519 * `BetaManagedAgentsMultiagentRosterEntryParamsOfBetaManagedAgentsAdvisors`
520520
521 These four took no other argument, so they are gone. Set the union's field to a pointer to the empty struct, such as `BetaManagedAgentsEventParamsUnion{OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{}}`:
521 These four took no other argument, so they are gone. Set the union’s field to a pointer to the empty struct, such as `BetaManagedAgentsEventParamsUnion{OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{}}`:
522522
523523 * `BetaManagedAgentsEventParamsOfUserInterrupt`
524524 * `BetaManagedAgentsMultiagentRosterEntryParamsOfBetaManagedAgentsMultiagentSelfs`
from line 527
527527 </Tab>
528528
529529 <Tab title="Java">
530 An example of such an enum is the nested class `BetaManagedAgentsTextBlock.Type`, with its one member `TEXT`. Nearly all of them are the `type` of a class under `com.anthropic.models.beta`: `agents`, `deploymentruns`, `deployments`, `dreams`, `environments`, `memorystores`, `sessions`, `userprofiles`, and `vaults`. In v3 these fields are a `JsonValue` that the builder fills in, as `TextBlockParam`'s already was. An optional field, such as the `type` of `BetaManagedAgentsBashToolConfigParams`, keeps its enum.
530 An example of such an enum is the nested class `BetaManagedAgentsTextBlock.Type`, with its one member `TEXT`. Nearly all of them are the `type` of a class under `com.anthropic.models.beta`: `agents`, `deploymentruns`, `deployments`, `dreams`, `environments`, `memorystores`, `sessions`, `userprofiles`, and `vaults`. In v3 these fields are a `JsonValue` that the builder fills in, as `TextBlockParam`’s already was. An optional field, such as the `type` of `BetaManagedAgentsBashToolConfigParams`, keeps its enum.
531531
532532 When you build an object, delete the call:
533533
from line 556
556556 if (message._type().equals(JsonValue.from("agent.message"))) {}
557557 ```
558558
559 To tell the variants of a union apart, keep using its `is*()` and `as*()` methods and its visitor, which don't change.
559 To tell the variants of a union apart, keep using its `is*()` and `as*()` methods and its visitor, which don’t change.
560560 </Tab>
561561
562562 <Tab title="PHP">
563563 An example of such an enum case is `Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock\Type::TEXT`. Nearly all of them are the `type` of a Managed Agents class under `Anthropic\Beta`. These properties are now a string with that one value as their default, as the `type` of `Anthropic\Messages\TextBlockParam` already was. An optional property, such as the `type` of `Anthropic\Beta\Environments\BetaPackages`, keeps its enum.
564564
565 `with()` no longer takes `type`. Remove it: PHP throws "Unknown named parameter" for a leftover `type:`, and in a positional call the arguments after it move up by one.
565 `with()` no longer takes `type`. Remove it: PHP throws “Unknown named parameter” for a leftover `type:`, and in a positional call the arguments after it move up by one.
566566
567567 ```php
568568 // Before
from line 583
583583 ```
584584
585585 * `withType()` and the `type` argument of `$client->beta->sessions->resources->add()` take only the string now. In `add()` it defaults to `'file'`, so you can leave it out.
586 * Requests you write as arrays don't change, and still need their `'type'`.
586 * Requests you write as arrays don’t change, and still need their `'type'`.
587587 </Tab>
588588
589589 <Tab title="Ruby">
from line 599
599599 event.type == :"agent.message"
600600 ```
601601
602 Requests you write as hashes don't change, and still need their `type`:
602 Requests you write as hashes don’t change, and still need their `type`:
603603
604604 ```ruby
605605 client.beta.sessions.events.send_(
from line 620
620620
621621<Tabs exclude="shell, python, csharp, go, java, php">
622622 <Tab title="TypeScript">
623 The `compactionControl` option and the `@anthropic-ai/sdk/lib/tools/CompactionControl` module are gone. The option stopped working in v0.129.0, where passing it threw an `AnthropicError`. It isn't in the tool runner's parameters type, so TypeScript reports it as an unknown property.
623 The `compactionControl` option and the `@anthropic-ai/sdk/lib/tools/CompactionControl` module are gone. The option stopped working in v0.129.0, where passing it threw an `AnthropicError`. It isn’t in the tool runner’s parameters type, so TypeScript reports it as an unknown property.
624624
625625 To keep compacting automatically at a token threshold:
626626
from line 674
674674
675675### Text Completions is removed
676676
677The legacy Text Completions API is removed from the TypeScript, Go, Java, and Ruby SDKs and from the CLI. Newer models and features don't support it. Use the [Messages API](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) instead.
677The legacy Text Completions API is removed from the TypeScript, Go, Java, and Ruby SDKs and from the CLI. Newer models and features don’t support it. Use the [Messages API](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) instead.
678678
679679### `betas` is removed from the non-beta Models methods
680680
from line 864
864864
865865<Tabs exclude="shell:cURL">
866866 <Tab title="CLI">
867 If `--base-url` or `ANTHROPIC_BASE_URL` points `ant` at a proxy or gateway that reads `X-Api-Key`, change it to read the key from `Authorization`. `ant` has no flag or environment variable for adding a header of your own, so it can't send `X-Api-Key`.
867 If `--base-url` or `ANTHROPIC_BASE_URL` points `ant` at a proxy or gateway that reads `X-Api-Key`, change it to read the key from `Authorization`. `ant` has no flag or environment variable for adding a header of your own, so it can’t send `X-Api-Key`.
868868 </Tab>
869869
870870 <Tab title="Python">
from line 956
956956 $client = new Client(apiKey: $apiKey, requestOptions: ['middleware' => [$sendXApiKey]]);
957957 ```
958958
959 Do not use the client's `extraHeaders` for this. A request that has `extraHeaders` of its own replaces the client's, and every endpoint under `$client->beta` that needs a beta header has them.
959 Do not use the client’s `extraHeaders` for this. A request that has `extraHeaders` of its own replaces the client’s, and every endpoint under `$client->beta` that needs a beta header has them.
960960 </Tab>
961961
962962 <Tab title="Ruby">
from line 971
971971 client = Anthropic::Client.new(api_key: api_key, middleware: send_x_api_key)
972972 ```
973973
974 The first line leaves a request alone when it doesn't carry the key, such as one that was redirected to another origin.
974 The first line leaves a request alone when it doesn’t carry the key, such as one that was redirected to another origin.
975975
976976 `Anthropic::AWSClient` takes no `middleware:` argument. Pass the middleware with each request instead, as `request_options: {middleware: send_x_api_key}`.
977977 </Tab>
from line 981
981981
982982This affects the Python, TypeScript, Go, PHP, and Ruby SDKs.
983983
984If you removed `X-Api-Key` so that a request sent only an authentication token, delete that code instead of changing it, because removing `Authorization` would remove the token. [A client holds one credential](https://platform.claude.com/docs/en/api/migration-2026-10-21#sdk-one-credential), so make sure that it's the token: with none passed in code and both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` set, the client uses the key.
984If you removed `X-Api-Key` so that a request sent only an authentication token, delete that code instead of changing it, because removing `Authorization` would remove the token. [A client holds one credential](https://platform.claude.com/docs/en/api/migration-2026-10-21#sdk-one-credential), so make sure that it’s the token: with none passed in code and both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` set, the client uses the key.
985985
986986<Tabs exclude="shell, csharp, java">
987987 <Tab title="Python">
988988 Code that passes `extra_headers={"X-Api-Key": omit}` to keep the key off a request no longer does that. Pass `extra_headers={"Authorization": omit}` instead.
989989
990 `extra_headers={"X-Api-Key": other_key}` no longer replaces the client's key: both keys go out. To send another key with one request, pass `extra_headers={"Authorization": f"Bearer {other_key}"}`.
990 `extra_headers={"X-Api-Key": other_key}` no longer replaces the client’s key: both keys go out. To send another key with one request, pass `extra_headers={"Authorization": f"Bearer {other_key}"}`.
991991 </Tab>
992992
993993 <Tab title="TypeScript">
994 `headers: { "X-Api-Key": otherKey }` in a request's options no longer replaces the client's key: both keys go out. To send another key with one request, pass ``headers: { Authorization: `Bearer $\{otherKey}` }``.
994 `headers: { "X-Api-Key": otherKey }` in a request’s options no longer replaces the client’s key: both keys go out. To send another key with one request, pass ``headers: { Authorization: `Bearer $\{otherKey}` }``.
995995 </Tab>
996996
997997 <Tab title="Go">
from line 1005
10051005 client.Messages.New(ctx, params, option.WithHeaderDel("Authorization"))
10061006 ```
10071007
1008 `option.WithHeader("X-Api-Key", otherKey)` no longer replaces the client's key: both keys go out. To send another key with one request, pass `option.WithAPIKey(otherKey)`.
1008 `option.WithHeader("X-Api-Key", otherKey)` no longer replaces the client’s key: both keys go out. To send another key with one request, pass `option.WithAPIKey(otherKey)`.
10091009 </Tab>
10101010
10111011 <Tab title="PHP">
10121012 `'extraHeaders' => ['X-Api-Key' => null]` no longer keeps the key off a request. To send a request with no credential, set `Authorization` to `null` instead, or remove it in a middleware if you did this for the whole client.
10131013
1014 `'extraHeaders' => ['X-Api-Key' => $otherKey]` no longer replaces the client's key: both keys go out. To send another key with one request, set `Authorization` to `"Bearer {$otherKey}"`.
1014 `'extraHeaders' => ['X-Api-Key' => $otherKey]` no longer replaces the client’s key: both keys go out. To send another key with one request, set `Authorization` to `"Bearer {$otherKey}"`.
10151015 </Tab>
10161016
10171017 <Tab title="Ruby">
from line 1027
10271027 <Tab title="Python">
10281028 v1 sent both an API key and an authentication token when the client had both, each in its own header. The credential is one of `api_key`, `auth_token`, `credentials`, `config`, or `profile`.
10291029
1030 * **Pass at most one.** Two or more in the constructor, or in one `copy()` or `with_options()` call, raise `ValueError`. `None` doesn't count. v1 raised `TypeError`, and only for two of `credentials`, `config`, and `profile`.
1030 * **Pass at most one.** Two or more in the constructor, or in one `copy()` or `with_options()` call, raise `ValueError`. `None` doesn’t count. v1 raised `TypeError`, and only for two of `credentials`, `config`, and `profile`.
10311031 * **The one you pass is the only one.** As in v1, the client does not look for another in the environment or on disk.
10321032 * **With none passed, only the first of these that is set is used:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, the profile that `ANTHROPIC_PROFILE` names, the workload identity federation variables, the active profile on disk (the one the `active_config` file names, otherwise `default`). When both of the first two are set, the client logs a warning once. v1 used the active profile ahead of the federation variables when `ANTHROPIC_CONFIG_DIR` was set or an `active_config` file existed; set `ANTHROPIC_PROFILE` to keep using it.
1033 * **`copy()` and `with_options()` replace.** A credential you pass replaces the client's own, whichever kind either one is. A base URL and headers that came from the old credential's config or profile go with it, and the new credential's are used. A base URL you passed stays.
1033 * **`copy()` and `with_options()` replace.** A credential you pass replaces the client’s own, whichever kind either one is. A base URL and headers that came from the old credential’s config or profile go with it, and the new credential’s are used. A base URL you passed stays.
10341034
10351035 ```python
10361036 client = Anthropic(api_key=key, auth_token=token)
from line 1049
10491049 <Tab title="TypeScript">
10501050 In v0 a client that had both an API key and an authentication token sent both, each in its own header. The credential is one of `apiKey`, `authToken`, `credentials`, `config`, or `profile`.
10511051
1052 * **Pass at most one.** Two or more in `new Anthropic()`, or in one `withOptions()` call, throw a `TypeError`. `null` and `undefined` don't count.
1052 * **Pass at most one.** Two or more in `new Anthropic()`, or in one `withOptions()` call, throw a `TypeError`. `null` and `undefined` don’t count.
10531053 * **The one you pass is the only one.** The client does not look for another in the environment or on disk.
10541054 * **With none passed, the first of these is used:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, the profile that `ANTHROPIC_PROFILE` names, the workload identity federation variables, the active profile on disk (the one the `active_config` file names, otherwise `default`). When both of the first two are set, the client logs a warning once. In v0 the active profile came before the federation variables. An `ANTHROPIC_PROFILE` that names a missing profile was skipped in v0, and now the first request throws an `AnthropicError`. `loadConfig()` with no argument follows the same order and throws the same error.
10551055 * **`withOptions` replaces.** A credential you pass replaces the one of the client you copy, so `client.withOptions({ authToken })` no longer needs `apiKey: null` next to it.
1056 * **A platform client takes only its platform's credentials.** The AWS, Bedrock, and Foundry clients and `AnthropicVertex` throw an `AnthropicError` for a credential option they don't authenticate with, as `AnthropicGoogleCloud` already did. On the AWS and Bedrock clients `apiKey: null` now clears the key, so the client signs with AWS credentials. `AnthropicBedrock` keeps its key in `client.apiKey`, where v0 kept it in `client.authToken`.
1056 * **A platform client takes only its platform’s credentials.** The AWS, Bedrock, and Foundry clients and `AnthropicVertex` throw an `AnthropicError` for a credential option they don’t authenticate with, as `AnthropicGoogleCloud` already did. On the AWS and Bedrock clients `apiKey: null` now clears the key, so the client signs with AWS credentials. `AnthropicBedrock` keeps its key in `client.apiKey`, where v0 kept it in `client.authToken`.
10571057
10581058 ```typescript
10591059 // Before: sent the API key and ignored `credentials`
from line 1063
10631063 const client = new Anthropic({ credentials });
10641064 ```
10651065
1066 The doc comments on `ClientOptions` in [`client.ts`](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/src/client.ts) say how each option behaves, and the doc comment on each of those platform clients' constructors lists the options it rejects.
1066 The doc comments on `ClientOptions` in [`client.ts`](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/src/client.ts) say how each option behaves, and the doc comment on each of those platform clients’ constructors lists the options it rejects.
10671067
10681068 If you relied on sending both, for example a token for a gateway and a key for the API behind it, set `X-Api-Key` in `defaultHeaders` and keep `authToken`. Have the gateway remove its `Authorization` header before it forwards the request. The Claude API expects one credential for each request.
10691069 </Tab>
from line 1071
10711071 <Tab title="C#">
10721072 In v12 a client that had both an API key and an authentication token sent both, each in its own header. The credential is one of `ApiKey`, `AuthToken`, or `ClientOptions.Credentials`.
10731073
1074 * **Passing two is an error.** Setting a second one throws an `ArgumentException`. `null` doesn't count, so to swap the credential in `ClientOptions` that you built yourself, set the old one to `null` first.
1075 * **Passing one uses it and ignores the environment.** No credential environment variable or profile on disk is read. In the same way, `AnthropicCredentials.Resolve(profile: "name")` now returns that profile's credentials when `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set.
1076 * **Passing none uses the first match in the environment:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, the workload identity federation variables, then the active profile on disk. An empty `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` counts as unset. When both of the first two are set, a warning goes to stderr once. The client looks when it is first used, so an `ANTHROPIC_PROFILE` that doesn't exist now throws from the first request instead of the constructor.
1077 * **`WithOptions` replaces.** A credential you pass in the modifier replaces the client's, whichever kind that was, so `AuthToken = token` no longer needs `ApiKey = null` next to it. With none passed, the copy keeps the client's credential, `BaseUrl`, and `ExtraHeaders`, even when the modifier returns new `ClientOptions`.
1074 * **Passing two is an error.** Setting a second one throws an `ArgumentException`. `null` doesn’t count, so to swap the credential in `ClientOptions` that you built yourself, set the old one to `null` first.
1075 * **Passing one uses it and ignores the environment.** No credential environment variable or profile on disk is read. In the same way, `AnthropicCredentials.Resolve(profile: "name")` now returns that profile’s credentials when `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set.
1076 * **Passing none uses the first match in the environment:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, the workload identity federation variables, then the active profile on disk. An empty `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` counts as unset. When both of the first two are set, a warning goes to stderr once. The client looks when it is first used, so an `ANTHROPIC_PROFILE` that doesn’t exist now throws from the first request instead of the constructor.
1077 * **`WithOptions` replaces.** A credential you pass in the modifier replaces the client’s, whichever kind that was, so `AuthToken = token` no longer needs `ApiKey = null` next to it. With none passed, the copy keeps the client’s credential, `BaseUrl`, and `ExtraHeaders`, even when the modifier returns new `ClientOptions`.
10781078
10791079 ```csharp
10801080 // Before: sent both, each in its own header
from line 1094
10941094 <Tab title="Go">
10951095 In v1 a client that had both an API key and an authentication token sent both, each in its own header. The credential comes from `option.WithAPIKey`, `option.WithAuthToken`, `option.WithConfig`, `option.WithProfile`, or `option.WithFederationTokenProvider`.
10961096
1097 * **Two of them passed to `anthropic.NewClient` are an error.** `NewClient` returns no error, so every request the client makes fails with it. `NewClient` already applies `anthropic.DefaultClientOptions()`, so don't pass its result as well: the credential it finds in the environment then counts as one you passed.
1097 * **Two of them passed to `anthropic.NewClient` are an error.** `NewClient` returns no error, so every request the client makes fails with it. `NewClient` already applies `anthropic.DefaultClientOptions()`, so don’t pass its result as well: the credential it finds in the environment then counts as one you passed.
10981098 * **One of them is used as it is.** No credential is read from the environment or from a profile on disk. In v1 `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` won over the last three.
10991099 * **With none of them, the first of these that is set is used,** in the same order as v1: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, the workload identity federation variables, the active profile on disk. When both of the first two are set, the SDK logs a warning once.
1100 * **One of them on a request replaces the client's credential for that request,** so `option.WithAuthToken` no longer needs `option.WithHeaderDel("X-Api-Key")` next to it.
1100 * **One of them on a request replaces the client’s credential for that request,** so `option.WithAuthToken` no longer needs `option.WithHeaderDel("X-Api-Key")` next to it.
11011101
11021102 ```go
11031103 // Before: the API key was sent and a warning was logged
from line 1157
11571157 Earlier versions sent both an API key and an authentication token when the client had both, each in its own header. `Anthropic\Client` takes one credential, from `apiKey`, `authToken`, or `credentials`:
11581158
11591159 * Passing more than one of them throws `\InvalidArgumentException`. Only `null` counts as not passed. An empty string counts as passed, so `new Client(apiKey: '')` sends no credential.
1160 * Passing one uses it, and no credential is read from the environment or from a profile on disk. With `ANTHROPIC_API_KEY` set, `new Client(authToken: $token)` sends the token. Earlier versions sent the environment's key with it.
1161 * Passing none uses the first of these that is set: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, the profile named by `ANTHROPIC_PROFILE`, the workload identity federation variables, the active profile on disk (the one the `active_config` file names, otherwise `default`). Earlier versions used the active profile before the federation variables, and used no profile when there was no `active_config` file. They also skipped an `ANTHROPIC_PROFILE` that named a missing or invalid profile. For that, `new Client()` now throws `Anthropic\Core\Exceptions\AnthropicException`. With both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` set, the client sends the key and its `authToken` property is empty. The first client built that way also writes a warning with `error_log()`, once for each process, which on a web server means once for each request: `Anthropic: ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are both set. Using ANTHROPIC_API_KEY.` To stop it, unset the variable you don't use.
1160 * Passing one uses it, and no credential is read from the environment or from a profile on disk. With `ANTHROPIC_API_KEY` set, `new Client(authToken: $token)` sends the token. Earlier versions sent the environment’s key with it.
1161 * Passing none uses the first of these that is set: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, the profile named by `ANTHROPIC_PROFILE`, the workload identity federation variables, the active profile on disk (the one the `active_config` file names, otherwise `default`). Earlier versions used the active profile before the federation variables, and used no profile when there was no `active_config` file. They also skipped an `ANTHROPIC_PROFILE` that named a missing or invalid profile. For that, `new Client()` now throws `Anthropic\Core\Exceptions\AnthropicException`. With both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` set, the client sends the key and its `authToken` property is empty. The first client built that way also writes a warning with `error_log()`, once for each process, which on a web server means once for each request: `Anthropic: ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are both set. Using ANTHROPIC_API_KEY.` To stop it, unset the variable you don’t use.
11621162
11631163 ```php
11641164 // Before: sent the key as X-Api-Key and the token as Authorization.
from line 1176
11761176 * **One passed:** The client uses it, and does not fall back to a credential from the environment or from a profile on disk.
11771177 * **None passed:** The client uses the first of these it finds: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, the workload identity federation variables, the active profile on disk. The order has not changed, but with both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` set, `client.auth_token` is now `nil`. An empty `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` now counts as unset, where earlier versions let an empty `ANTHROPIC_API_KEY` hide the token and everything after it.
11781178
1179 When no credential is passed and both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` are set, the SDK also warns once for each process, on standard error through `Kernel#warn`: `Anthropic: ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are both set. Using ANTHROPIC_API_KEY.` Unset the variable you don't use to silence it.
1179 When no credential is passed and both `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` are set, the SDK also warns once for each process, on standard error through `Kernel#warn`: `Anthropic: ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are both set. Using ANTHROPIC_API_KEY.` Unset the variable you don’t use to silence it.
11801180
11811181 ```ruby
11821182 # Before: sent the key and ignored the token.
from line 1220
12201220 </Tab>
12211221
12221222 <Tab title="C#">
1223 **A union's `Type` is an enum.** In v12, the `Type` property of a union class, such as `ContentBlock.Type` or `RawMessageStreamEvent.Type`, was a `JsonElement`. In v13 it is an `ApiEnum<string, T>`, where `T` is a new enum with one member for each variant, such as `ContentBlockType`. A few unions are told apart by `Name` or `Command` instead, such as `BetaMemoryTool20250818Command.Command`, and those properties change in the same way. The `Type` of a variant class, such as `TextBlock.Type`, is still a `JsonElement`.
1223 **A union’s `Type` is an enum.** In v12, the `Type` property of a union class, such as `ContentBlock.Type` or `RawMessageStreamEvent.Type`, was a `JsonElement`. In v13 it is an `ApiEnum<string, T>`, where `T` is a new enum with one member for each variant, such as `ContentBlockType`. A few unions are told apart by `Name` or `Command` instead, such as `BetaMemoryTool20250818Command.Command`, and those properties change in the same way. The `Type` of a variant class, such as `TextBlock.Type`, is still a `JsonElement`.
12241224
12251225 ```csharp
12261226 // Before
from line 1312
13121312 </Tab>
13131313
13141314 <Tab title="PHP">
1315 **Enum cases you pass are read back as their values.** `with()` and setters such as `withAllowedCallers()` still take an enum case or its value. Earlier versions kept a case as you passed it. v1 keeps the case's value, which is what the property types already declare. Objects that the SDK builds from a response don't change, and the JSON that the SDK sends is the same. If you read a property back and compare it with a case, the comparison is now false and nothing throws, so compare it with the case's value:
1315 **Enum cases you pass are read back as their values.** `with()` and setters such as `withAllowedCallers()` still take an enum case or its value. Earlier versions kept a case as you passed it. v1 keeps the case’s value, which is what the property types already declare. Objects that the SDK builds from a response don’t change, and the JSON that the SDK sends is the same. If you read a property back and compare it with a case, the comparison is now false and nothing throws, so compare it with the case’s value:
13161316
13171317 ```php
13181318 $tool = CodeExecutionTool20250825::with(allowedCallers: [AllowedCaller::DIRECT]);
No line in this hunk matches that.