---
title: Migración a la versión de API 2026-10-21
url: https://platform.claude.com/docs/es/api/migration-2026-10-21
description: "Actualiza una integración con la API de Claude de la versión de API 2023-06-01 a la 2026-10-21: la versión del SDK que debes instalar y los cambios de código que debes hacer."
---

Esta guía enumera los cambios de código necesarios para pasar de la versión de API `2023-06-01` a la `2026-10-21`. Los [pasos de actualización](https://platform.claude.com/docs/es/api/migration-2026-10-21#migration-checklist) indican qué hacer, en orden.

## Pasos de actualización

<Steps>
  <Step title="Cambia a 2026-10-21">
    Si usas un SDK, tus solicitudes usan la versión de API que estaba vigente cuando se publicó la versión principal de tu SDK. Las versiones que se muestran para tu lenguaje son las primeras que usan `2026-10-21`, así que actualiza a ellas. Si llamas a la API sin un SDK, establece el encabezado `anthropic-version` en `2026-10-21`.

    <Tabs>
      <Tab title="cURL">
        Establece el encabezado `anthropic-version` en `2026-10-21`:

        ```bash
        curl https://api.anthropic.com/v1/messages \
          -H "x-api-key: $ANTHROPIC_API_KEY" \
          -H "anthropic-version: 2026-10-21" \
          -H "content-type: application/json" \
          -d '{
            "model": "claude-opus-5-5",
            "max_tokens": 1024,
            "messages": [{"role": "user", "content": "Hello, Claude"}]
          }'
        ```
      </Tab>

      <Tab title="CLI">
        Actualiza la CLI `ant` a la versión 2.0.0. El comando de Homebrew y las descargas de las versiones publicadas siguen siendo los mismos.

        Go exige que la ruta de un módulo en la versión principal 2 o posterior termine en esa versión, por lo que la ruta del módulo de la CLI ahora es `github.com/anthropics/anthropic-cli/v2`. Si instalas con `go install`, agrega `/v2` a la ruta:

        ```bash
        # Antes
        go install 'github.com/anthropics/anthropic-cli/cmd/ant@latest'

        # Después
        go install 'github.com/anthropics/anthropic-cli/v2/cmd/ant@latest'
        ```

        La ruta anterior sigue instalando la última versión v1, sin ningún error ni advertencia.
      </Tab>

      <Tab title="Python">
        Actualiza el paquete `anthropic` a la versión 2.0.0.
      </Tab>

      <Tab title="TypeScript">
        Actualiza el paquete `@anthropic-ai/sdk` a la versión 1.0.0.

        La v1 de `@anthropic-ai/bedrock-sdk`, `@anthropic-ai/vertex-sdk`, `@anthropic-ai/foundry-sdk`, `@anthropic-ai/aws-sdk` y `@anthropic-ai/google-cloud-sdk` necesita la v1 de `@anthropic-ai/sdk`, y sus versiones anteriores necesitan una versión inferior a la 1. Si usas alguno de ellos, pásalo a la v1 en la misma actualización. De lo contrario, tu gestor de paquetes instala una segunda copia, más antigua, de `@anthropic-ai/sdk` para él.
      </Tab>

      <Tab title="C#">
        Actualiza el paquete `Anthropic` a la versión 13.0.0.
      </Tab>

      <Tab title="Go">
        Actualiza a la versión 2.0.0 del SDK de Go, cuya ruta de módulo es `github.com/anthropics/anthropic-sdk-go/v2`. Una nueva versión principal de un módulo de Go tiene una nueva ruta de importación, así que cambia cada importación de `github.com/anthropics/anthropic-sdk-go`, y de sus paquetes como `github.com/anthropics/anthropic-sdk-go/option`, para que empiece con `github.com/anthropics/anthropic-sdk-go/v2`. Luego ejecuta `go get github.com/anthropics/anthropic-sdk-go/v2`.
      </Tab>

      <Tab title="Java">
        Actualiza el artefacto `com.anthropic:anthropic-java` a la versión 3.0.0.
      </Tab>

      <Tab title="PHP">
        Ejecuta `composer require "anthropic-ai/sdk"` para actualizar el paquete a la versión 1.0.0. `composer update` por sí solo se mantiene dentro de tu restricción de versión actual.
      </Tab>

      <Tab title="Ruby">
        Actualiza la gema `anthropic` a la versión 2.0.0.
      </Tab>
    </Tabs>

    <Warning>
      En Amazon Bedrock, `2026-10-21` solo está disponible para los modelos Claude 4.7 y posteriores. Si llamas a un modelo más antiguo en Bedrock, de los que aparecen en [Claude en Amazon Bedrock (Opus 4.6 y anteriores)](https://platform.claude.com/docs/es/build-with-claude/claude-on-amazon-bedrock-legacy), quédate en la versión del SDK que usas hoy o, si estableces la versión de API tú mismo, sigue enviando la versión que envías hoy. Para pasar esas llamadas a `2026-10-21`, primero cámbialas a un modelo Claude 4.7 o posterior.
    </Warning>

    Publica la actualización junto con tus correcciones del siguiente paso.
  </Step>

  <Step title="Actualiza tu código para los cambios incompatibles">
    Revisa los [cambios incompatibles](https://platform.claude.com/docs/es/api/migration-2026-10-21#breaking-changes) y actualiza tu código para cada uno que te afecte. Haz también los [cambios de código que necesita la nueva versión del SDK o de la CLI](https://platform.claude.com/docs/es/api/migration-2026-10-21#sdk-changes).
  </Step>

  <Step title="Prueba con 2026-10-21">
    Prueba tu integración con `2026-10-21` antes de publicarla.
  </Step>
</Steps>

## Cambios incompatibles

### Messages API

Estos cambios se aplican a las solicitudes a la Messages API (`POST /v1/messages`), al [conteo de tokens](https://platform.claude.com/docs/es/build-with-claude/token-counting) y a cada solicitud de un [Message Batch](https://platform.claude.com/docs/es/build-with-claude/batch-processing) creado con `2026-10-21`.

#### Los bloques de documento en mensajes del asistente devuelven un error

Te afecta si envías bloques `document` directamente en mensajes `assistant`.

En `2026-10-21`, una solicitud que coloca un bloque `document` directamente en un mensaje `assistant` devuelve un error 400. Envía los documentos en mensajes `user` en su lugar. Un documento anidado dentro de otro bloque, como un resultado de la herramienta de obtención web que Claude devolvió, se sigue aceptando.

#### Las citas en mensajes del usuario devuelven un error

Te afecta si envías `citations` en bloques `text` dentro de mensajes `user`, incluso dentro de bloques `search_result`.

En `2026-10-21`, una solicitud que envía un bloque `text` con un arreglo `citations` no vacío en un mensaje `user` devuelve un error 400. Elimina `citations` de esos bloques. Las citas en bloques `text` de mensajes `assistant`, como las respuestas anteriores de Claude que reenvías, se siguen aceptando, al igual que la configuración `"citations": {"enabled": true}` en documentos y resultados de búsqueda.

#### Se eliminan los parámetros de muestreo

Te afecta si envías `temperature`, `top_p` o `top_k` en solicitudes de Messages o de Message Batch.

En `2026-10-21`, una solicitud que envía `temperature`, `top_p` o `top_k` devuelve un error 400, incluso con el valor predeterminado del parámetro.

Elimina los tres parámetros de tus solicitudes.

A partir de las últimas versiones principales, el SDK y la CLI ya no tienen los tres parámetros.

Sin estos parámetros, una solicitud muestrea con sus valores predeterminados, incluida una `temperature` de 1.0, igual que una solicitud `2023-06-01` que los omite. Los modelos que ya aceptan solo los valores predeterminados, como Claude 4.7 y los modelos posteriores, muestrean como antes. En los modelos que aceptan otros valores, como Claude Sonnet 4.6, una solicitud que reducía `temperature` para obtener una salida más consistente pierde ese efecto. Ninguna otra configuración lo reemplaza. Si una solicitud depende de un valor no predeterminado, deja de enviar el parámetro o mantén esa solicitud en `2023-06-01`.

### Admin API y Claude Enterprise Analytics API

Te afecta si tu código lee los campos de respuesta de la Admin API o de la Claude Enterprise Analytics API que aparecen en las siguientes tablas.

En `2026-10-21`, las respuestas de la Admin API y de la [Claude Enterprise Analytics API](https://platform.claude.com/docs/es/manage-claude/analytics-api) ya no incluyen los nombres antiguos de los campos renombrados. Usa los nombres nuevos en su lugar.

Admin API:

| Endpoints                                                                                                                                                                                                                                                                                                            | Nombre antiguo | Nombre nuevo                                                                                                                                                    |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Grupos RBAC](https://platform.claude.com/docs/es/api/beta/organization/rbac_groups/list): crear, listar, obtener y actualizar                                                                                                                                                                                       | `roles`        | `role_ids`                                                                                                                                                      |
| [Miembros de grupos RBAC](https://platform.claude.com/docs/es/api/beta/organization/rbac_groups/members/list): listar y agregar, y la respuesta a [eliminar un miembro](https://platform.claude.com/docs/es/api/beta/organization/rbac_groups/members/remove)                                                        | `group_id`     | `rbac_group_id`                                                                                                                                                 |
| [Roles RBAC](https://platform.claude.com/docs/es/api/beta/organization/rbac_roles/list): listar y obtener                                                                                                                                                                                                            | `name`         | `display_name`                                                                                                                                                  |
| Límites de velocidad de la [organización](https://platform.claude.com/docs/es/api/organization/rate_limits/list) y del [espacio de trabajo](https://platform.claude.com/docs/es/api/organization/workspaces/rate_limits/list): listar, y la respuesta a establecer los límites de velocidad de un espacio de trabajo | `group_type`   | `group.type`                                                                                                                                                    |
| [Claves de API](https://platform.claude.com/docs/es/api/organization/api_keys/list): listar, obtener y actualizar                                                                                                                                                                                                    | `workspace_id` | `scope` (valores diferentes; consulta [`workspace_id` en las claves de API](https://platform.claude.com/docs/es/api/migration-2026-10-21#api-key-workspace-id)) |

Claude Enterprise Analytics API:

| Endpoints                                                                                                                                                                                                                         | Nombre antiguo | Nombre nuevo          |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | --------------------- |
| Informes por usuario de [uso](https://platform.claude.com/docs/es/api/beta/organization/analytics/user_usage_report/list) y de [costo](https://platform.claude.com/docs/es/api/beta/organization/analytics/user_cost_report/list) | `actor.email`  | `actor.email_address` |
| [Resúmenes de actividad](https://platform.claude.com/docs/es/api/beta/organization/analytics/summaries/list)                                                                                                                      | `summaries`    | `data`                |

El parámetro de consulta `group_type` en los endpoints de listado de límites de velocidad y el filtro `workspace_id` en el endpoint de listado de claves de API no cambian.

#### `workspace_id` en las claves de API

Te afecta si tu código lee `workspace_id` en las claves de API.

En `2023-06-01`, el `workspace_id` de una clave de API es el ID del espacio de trabajo al que está vinculada la clave, o `null` para una clave vinculada al Default Workspace y para una clave que no está vinculada a un solo espacio de trabajo (una clave de todos los espacios de trabajo).

En `2026-10-21`, se elimina `workspace_id`. Lee `scope` en su lugar: para una clave vinculada a un espacio de trabajo, incluido el Default Workspace, `scope.workspace_id` es el ID real de ese espacio de trabajo, y una clave de todos los espacios de trabajo tiene un `scope` de `{"type": "organization"}`, sin `workspace_id`.

Si tu código trataba un `workspace_id` `null` como el Default Workspace, compara `scope.workspace_id` con el ID de tu Default Workspace en su lugar. Para encontrar ese ID, llama a [List Workspaces](https://platform.claude.com/docs/es/api/organization/workspaces/list) con `include_default=true` y toma el espacio de trabajo llamado `"Default"`.

## Cambios en el SDK y la CLI

Las versiones anteriores del SDK y de la CLI enviaban `anthropic-version: 2023-06-01` con cada solicitud. Las versiones indicadas en [Cambia a 2026-10-21](https://platform.claude.com/docs/es/api/migration-2026-10-21#upgrade) envían `2026-10-21`, incluidos sus clientes de Amazon Bedrock y Google Cloud. Los cambios de [Cambios incompatibles](https://platform.claude.com/docs/es/api/migration-2026-10-21#breaking-changes) también se aplican a ellas: los parámetros eliminados y los nombres de campo antiguos también desaparecen de los tipos del SDK y de las opciones y la salida de la CLI.

### `MessageParam` es una unión con un tipo por rol

En versiones anteriores, `MessageParam` era un solo tipo para todos los roles. Ahora cada rol tiene su propio tipo: `UserMessageParam`, `AssistantMessageParam` y `SystemMessageParam`. `BetaMessageParam` se divide de la misma manera, en `BetaUserMessageParam`, `BetaAssistantMessageParam` y `BetaSystemMessageParam`. Las solicitudes que envía el SDK tienen los mismos campos y valores que antes.

<Tabs exclude="shell">
  <Tab title="Python">
    Un mensaje escrito como un literal de diccionario, como `{"role": "user", "content": "Hello"}`, no necesita cambios.

    En v1, `anthropic.types.MessageParam` era un solo `TypedDict`. En v2 es una unión de los tres tipos, y `anthropic.types.beta.BetaMessageParam` es una unión de sus tres tipos. Ambos nombres siguen funcionando en anotaciones, como `list[MessageParam]`.

    Llamar a cualquiera de los dos nombres ahora genera un `TypeError`, y un verificador de tipos lo señala. Escribe el mensaje como un diccionario:

    ```python
    # Antes
    message = MessageParam(role="user", content="Hello")

    # Después
    message: MessageParam = {"role": "user", "content": "Hello"}
    ```

    Crear una subclase de cualquiera de los dos nombres también genera un `TypeError`. Crea una subclase del tipo del rol, como `UserMessageParam`.

    Los cambios restantes solo afectan a la verificación de tipos. Un diccionario cuyo `role` es una variable que puede contener más de un rol no coincide con ningún tipo de la unión, así que construye el diccionario para un rol a la vez:

    ```python
    # Antes
    def to_message(role: Literal["user", "assistant"], text: str) -> MessageParam:
        return {"role": role, "content": text}


    # Después
    def to_message(role: Literal["user", "assistant"], text: str) -> MessageParam:
        if role == "user":
            return {"role": "user", "content": text}
        return {"role": "assistant", "content": text}
    ```

    Dos más se aplican solo a `BetaMessageParam`:

    * `clear_at` y `output_config` son claves de `BetaSystemMessageParam`, por lo que un mensaje de usuario o de asistente no puede tenerlas.
    * `content` es opcional en `BetaSystemMessageParam`, por lo que pyright señala `message["content"]`. Léelo con `message.get("content")`, o comprueba primero que `message["role"]` no sea `"system"`.
  </Tab>

  <Tab title="TypeScript">
    `MessageParam` era una sola interfaz. Ahora es `UserMessageParam | AssistantMessageParam | SystemMessageParam`, y `BetaMessageParam` es la misma unión de sus tres tipos. Solo cambiaron los tipos. Un literal de mensaje que nombra su rol, como `{ role: "user", content: "Hello, Claude" }`, pasa la verificación de tipos como antes, así que la mayor parte del código no necesita cambios.

    El tipo de sistema difiere de los otros dos. Su `content` es una cadena o bloques de texto, además de bloques `tool_addition` y `tool_removal` en `beta`, por lo que un bloque de imagen ahí ya no compila. En `beta`, solo él tiene `clear_at` y `output_config`, y su `content` es opcional.

    El código que maneja un mensaje de cualquier rol tiene que comprobar `role` antes de reemplazar o agregar bloques de contenido, y antes de leer `clear_at` u `output_config`:

    ```typescript
    // Antes
    const copy: Anthropic.MessageParam = { ...message, content: blocks };

    // Después
    const copy: Anthropic.MessageParam =
      message.role === "system" ? message : { ...message, content: blocks };
    ```

    El `content` de un `BetaMessageParam` puede ser `undefined`:

    ```typescript
    // Antes
    const types =
      typeof message.content === "string" ? [] : message.content.map((block) => block.type);

    // Después
    const types = Array.isArray(message.content) ? message.content.map((block) => block.type) : [];
    ```

    El tipo `BetaMessageParam["content"]` también incluye `undefined`. Para el contenido de un mensaje de usuario o de asistente, usa `BetaMessageContentParam`.

    Una interfaz no puede extender una unión, y una clase no puede implementarla. Usa un alias de tipo, o nombra el tipo de un rol en su lugar.

    ```typescript
    // Antes
    interface StoredMessage extends Anthropic.MessageParam {
      id: string;
    }

    // Después
    type StoredMessage = Anthropic.MessageParam & { id: string };
    ```
  </Tab>

  <Tab title="C#">
    En v12, `MessageParam` y `BetaMessageParam` eran clases cuyos `Role` y `Content` establecías. En v13 cada una es una unión de las tres clases de sus roles, y los enums `Role` de `Anthropic.Models.Messages` y `Anthropic.Models.Beta.Messages` desaparecen.

    Cuando construyas un mensaje, nombra la clase de su rol y elimina `Role`. Cada clase se convierte implícitamente a la unión:

    ```csharp
    // Antes
    Messages = [new() { Role = Role.User, Content = "Hello, Claude" }],

    // Después
    Messages = [new UserMessageParam { Content = "Hello, Claude" }],
    ```

    `new UserMessageParam("Hello, Claude")` también funciona.

    Las clases que contienen `Content` también cambian:

    * `MessageParamContent` ahora es `MessageContentParam`, y `BetaMessageParamContent` ahora es `BetaMessageContentParam`.
    * Un mensaje de sistema tiene su propia clase de contenido. Un `SystemMessageParamContent` contiene una cadena o una lista de `TextBlockParam`, y un `BetaSystemMessageParamContent` contiene una cadena o una lista de `SystemMessageContentBlock`. Un `List<ContentBlockParam>` o un `List<BetaContentBlockParam>` ya no se convierte a ella.
    * `ClearAt` y `OutputConfig` están solo en `BetaSystemMessageParam`.

    Cuando lees un mensaje, la unión sigue teniendo `Role` y `Content`, pero no puedes establecerlos, así que una expresión `with` va en la clase del rol. `Role` ahora contiene un `MessageParamRole` o un `BetaMessageParamRole`. `BetaMessageParam.Content` puede ser `null`, porque `Content` es opcional en `BetaSystemMessageParam`.

    ```csharp
    // Antes
    if (message.Role.Value() == Role.Assistant) { }

    // Después
    if (message.Role.Value() == MessageParamRole.Assistant) { }
    ```

    `TryPickUser`, `TryPickAssistant`, `TryPickSystem`, `Switch` y `Match` te dan la clase del rol.
  </Tab>

  <Tab title="Go">
    En v1, `MessageParam` era un solo struct con un campo `Role` y un campo `Content`. En v2 es una unión: `OfUser` contiene un `*UserMessageParam`, `OfAssistant` un `*AssistantMessageParam` y `OfSystem` un `*SystemMessageParam`. `BetaMessageParam` cambia de la misma manera. El SDK envía el mismo JSON que antes, salvo por el orden de las claves en un mensaje de sistema beta.

    `NewUserMessage`, `NewAssistantMessage`, `NewBetaUserMessage` y los métodos `ToParam` de `Message` y `BetaMessage` funcionan como antes, así que el código que construye sus mensajes con ellos no necesita cambios.

    Un literal de struct nombra el tipo de su rol. Omite `Role`, porque cada tipo serializa el suyo:

    ```go
    // Antes
    anthropic.MessageParam{
    	Role:    anthropic.MessageParamRoleAssistant,
    	Content: blocks,
    }

    // Después
    anthropic.MessageParam{OfAssistant: &anthropic.AssistantMessageParam{
    	Content: blocks,
    }}
    ```

    El código que lee un mensaje comprueba qué campo está establecido:

    ```go
    // Antes
    if message.Role == anthropic.MessageParamRoleUser {
    	blocks = message.Content
    }

    // Después
    if message.OfUser != nil {
    	blocks = message.OfUser.Content
    }
    ```

    * Los tipos `MessageParamRole` y `BetaMessageParamRole` y sus constantes desaparecen. `message.GetRole()` devuelve un puntero al rol como `string`, o `nil` cuando no hay ningún campo establecido.
    * `ClearAt` y `OutputConfig` se movieron de `BetaMessageParam` a `BetaSystemMessageParam`. Los nombres del tipo `BetaMessageParamClearAt` y de sus constantes ahora empiezan con `BetaSystemMessageParamClearAt`.
    * `SystemMessageParam.Content` es un `[]TextBlockParam`. `BetaSystemMessageParam.Content` es un `[]BetaSystemMessageParamContentUnion`, que contiene bloques `text`, `tool_addition` y `tool_removal`. `NewBetaSystemMessage` recibe bloques de ese tipo, así que `anthropic.NewBetaTextBlock("Be brief.")` se convierte en `anthropic.BetaSystemMessageParamContentUnion{OfText: &anthropic.BetaTextBlockParam{Text: "Be brief."}}`.
    * Una copia de un `MessageParam` apunta al mismo mensaje que el original. Establecer `duplicate.OfUser.Content` cambia ambos, mientras que establecer `duplicate.Content` en v1 cambiaba solo la copia.
    * `mcp.ToMessage` devuelve un `*mcp.UnsupportedValueError` para un rol distinto de `user` o `assistant`. v1 copiaba cualquier rol en el mensaje.
  </Tab>

  <Tab title="Java">
    En v2, `MessageParam` era una sola clase con un `role` y un `content`. En v3 es una unión de las tres clases, y `MessageParam.Content` ahora es el `MessageContentParam` de nivel superior. `BetaMessageParam` cambia de la misma manera, y sus clases tienen los mismos nombres con un prefijo `Beta`. El JSON que envía el SDK es el mismo, excepto que un mensaje de sistema beta ahora tiene `role` antes de `content`.

    `addUserMessage`, `addAssistantMessage` y `addSystemMessage` con un `String`, `addMessage` con un `Message` o un `MessageParam`, y `Message.toParam()` no cambian.

    `MessageParam.builder()` y `toBuilder()` desaparecen. Llama al método de fábrica del rol, que es `ofUser`, `ofAssistant` u `ofSystem`:

    ```java
    // Antes
    MessageParam message = MessageParam.builder()
        .role(MessageParam.Role.USER)
        .content("Hello, Claude")
        .build();

    // Después
    MessageParam message = MessageParam.ofUser("Hello, Claude");
    ```

    Los métodos que reciben una lista de bloques de contenido tienen nombres nuevos en los builders de `MessageCreateParams`, `StructuredMessageCreateParams`, `MessageCountTokensParams` y `BatchCreateParams.Request.Params`:

    | v2                                 | v2 beta                                       | v3                                        |
    | ---------------------------------- | --------------------------------------------- | ----------------------------------------- |
    | `addUserMessageOfBlockParams`      | `addUserMessageOfBetaContentBlockParams`      | `addUserMessageOfContentBlockParams`      |
    | `addAssistantMessageOfBlockParams` | `addAssistantMessageOfBetaContentBlockParams` | `addAssistantMessageOfContentBlockParams` |
    | `addSystemMessageOfBlockParams`    | `addSystemMessageOfBetaContentBlockParams`    | `addSystemMessageOfBlocks`                |

    En la clase de contenido, `BlockParams` (`BetaContentBlockParams` en beta) se convierte en `ContentBlockParams` en `ofBlockParams`, `blockParams()`, `isBlockParams()`, `asBlockParams()` y en el `visitBlockParams` del visitor.

    Un mensaje de sistema tiene su propia clase de contenido, `SystemMessageParam.Content`, que ahora recibe `addSystemMessage`. Sus bloques son `TextBlockParam`, no `ContentBlockParam`. En beta son `BetaSystemMessageParam.Content.SystemMessageContentBlock`, que tiene `ofText`, `ofToolAddition` y `ofToolRemoval`.

    ```java
    // Antes
    builder.addSystemMessageOfBlockParams(List.of(ContentBlockParam.ofText(textBlock)));

    // Después
    builder.addSystemMessageOfBlocks(List.of(textBlock));
    ```

    Cuando lees un mensaje, `role()` no cambia. `content()` devuelve un `MessageContentParam`, o un `Optional<BetaMessageContentParam>` en beta, donde un mensaje de sistema puede no tener contenido. `_role()`, `_content()` y `_additionalProperties()` ahora están en la clase del rol, que devuelven `asUser()`, `asAssistant()` o `asSystem()`.
  </Tab>

  <Tab title="PHP">
    Las clases están en `Anthropic\Messages`, y las clases beta en `Anthropic\Beta\Messages`. Los mensajes que escribes como arreglos no cambian, y siguen necesitando su `'role'`.

    `MessageParam::with()` y `BetaMessageParam::with()` se mantienen, y devuelven la clase que corresponde a `role`. Una llamada con argumentos con nombre no necesita cambios. `role` ahora es el primer parámetro, así que en una llamada posicional intercambia `content` y `role`, o llama a `with()` en la clase del rol, que no recibe `role`. Con argumentos en el orden anterior, PHP lanza `UnhandledMatchError`, sobre lo cual PHPStan no advierte, o un `TypeError` si `content` es una lista o `role` es un caso de enum.

    ```php
    // Antes
    MessageParam::with('Hello, Claude', 'user');

    // Después
    UserMessageParam::with('Hello, Claude');
    ```

    Ningún objeto es ya un `MessageParam`: `instanceof MessageParam` es falso, y PHP lanza un `TypeError` donde un tipo de parámetro o de retorno lo nombra. Nombra las tres clases en su lugar:

    ```php
    // Antes
    function send(MessageParam $message): void {}

    // Después
    function send(UserMessageParam|AssistantMessageParam|SystemMessageParam $message): void {}
    ```

    * `MessageParam::fromArray()` y `(new MessageParam)->withContent()` lanzan "Call to undefined method". Llámalos en la clase del rol, que establece `role` por sí misma.
    * `withRole()` ahora solo recibe la cadena: PHP lanza un `TypeError` para un caso de `Role`. Para darle otro rol a un mensaje, construye la clase de ese rol.
    * `clearAt` y `outputConfig` existen solo en `BetaSystemMessageParam`, y `BetaMessageParam::with()` los descarta para los otros dos roles. El enum `BetaMessageParam\ClearAt` ahora es `BetaSystemMessageParam\ClearAt`.
    * `MessageParam\Content` ahora es `MessageContentParam`, y `BetaMessageParam\Content` ahora es `BetaMessageContentParam`. Sus tipos de PHPStan también adoptan el nuevo nombre: `ContentShape` ahora es `MessageContentParamShape`.
    * Un arreglo cuyo `'role'` es un caso de `Role` sigue funcionando, pero PHPStan ahora lo señala. Escribe la cadena.
  </Tab>

  <Tab title="Ruby">
    Los mensajes que escribes como hashes no cambian.

    En v2, `Anthropic::MessageParam` es una unión de `Anthropic::UserMessageParam`, `Anthropic::AssistantMessageParam` y `Anthropic::SystemMessageParam`. De la misma manera, `Anthropic::Beta::BetaMessageParam` es una unión de sus tres clases.

    `Anthropic::MessageParam.new(role: :user, content: "Hello, Claude")` sigue funcionando, y devuelve una instancia de la clase de ese rol. También puedes construir esa clase tú mismo, sin `role:`, como `Anthropic::UserMessageParam.new(content: "Hello, Claude")`. Cuatro cosas cambian para el código que construye o inspecciona estas instancias:

    * `MessageParam.new` solo recibe argumentos de palabra clave, así que pasa un hash como `MessageParam.new(**hash)`. Genera un `ArgumentError` cuando falta `role:` o no es uno de los tres roles.
    * `message.is_a?(Anthropic::MessageParam)` es `false`. Una rama `when Anthropic::MessageParam` y `messages.grep(Anthropic::MessageParam)` siguen coincidiendo.
    * Una instancia construida con `.new` no tiene `:role` en su `to_h`, y `message[:role]` es `nil`. `message.role` sigue devolviendo el rol.
    * Solo `BetaSystemMessageParam` tiene `clear_at` y `output_config`. En un mensaje de usuario o de asistente devolvían `nil`, y ahora generan un `NoMethodError`.

    ```ruby
    # Antes
    typed = messages.select { _1.is_a?(Anthropic::MessageParam) }
    roles = typed.map { _1[:role] }

    # Después
    typed = messages.grep(Anthropic::MessageParam)
    roles = typed.map(&:role)
    ```

    Los módulos de enum `Role` se mantienen. Estas constantes se movieron. Los nombres que empiezan con `Beta` están bajo `Anthropic::Beta`, y los demás bajo `Anthropic`:

    | Eliminado                   | Usa en su lugar                                                                           |
    | --------------------------- | ----------------------------------------------------------------------------------------- |
    | `MessageParam::Content`     | `MessageContentParam`, o `SystemMessageParam::Content` para un mensaje de sistema         |
    | `BetaMessageParam::Content` | `BetaMessageContentParam`, o `BetaSystemMessageParam::Content` para un mensaje de sistema |
    | `BetaMessageParam::ClearAt` | `BetaSystemMessageParam::ClearAt`                                                         |

    Si verificas tipos con Sorbet, `MessageParam::OrHash` desaparece, y `Anthropic::MessageParam` como tipo ya no acepta una instancia. Usa `Anthropic::MessageParam::Variants` para las tres clases, cada una de las cuales tiene su propio `OrHash`. En RBS, un valor con tipo `Anthropic::MessageParam` no tiene `role` ni `content`. Usa el alias `Anthropic::Models::message_param`, que era la forma de hash de un mensaje y ahora es la unión de las tres clases. Los nombres beta son `BetaMessageParam::Variants` y `Anthropic::Models::Beta::beta_message_param`.
  </Tab>
</Tabs>

### Los enums de un solo valor son constantes

Esto afecta a los SDK de C#, Go, Java, PHP y Ruby y a una opción de la CLI. En versiones anteriores, un campo obligatorio que solo puede contener un valor tenía su propio tipo enum con un único miembro. Estos campos ahora son constantes, y los tipos enum desaparecen. El JSON que el SDK envía y lee es el mismo.

<Tabs exclude="shell:cURL, python, typescript">
  <Tab title="CLI">
    `--type` es opcional en `ant beta:sessions:resources add`. `file` es el único valor que `--type` puede tomar ahí, así que v2 lo envía por ti. Los comandos que todavía pasan `--type file` siguen funcionando, así que no hay nada que cambiar.
  </Tab>

  <Tab title="C#">
    En v12, una propiedad `required` de este tipo tenía un enum con un único miembro, como `BetaManagedAgentsTextBlockType.Text`. Casi todas son la propiedad `Type` de una clase bajo `Anthropic.Models.Beta`: `Agents`, `DeploymentRuns`, `Deployments`, `Dreams`, `Environments`, `MemoryStores`, `Sessions`, `UserProfiles` y `Vaults`. En v13 estas propiedades son un `JsonElement` que el constructor rellena, como ya lo era `TextBlockParam.Type`.

    Una propiedad opcional, como `BetaManagedAgentsGlobToolConfigParams.Type`, conserva su enum. Dos de esos enums tienen un nombre nuevo:

    | v12                                         | v13                                       |
    | ------------------------------------------- | ----------------------------------------- |
    | `BetaManagedAgentsBashToolConfigParamsType` | `Anthropic.Models.Beta.Agents.Type`       |
    | `BetaPackagesType`                          | `Anthropic.Models.Beta.Environments.Type` |

    Cuando construyas un objeto, elimina la propiedad:

    ```csharp
    // Antes
    new BetaManagedAgentsTextBlock
    {
        Type = BetaManagedAgentsTextBlockType.Text,
        Text = "Hello Claude!",
    };

    // Después
    new BetaManagedAgentsTextBlock { Text = "Hello Claude!" };
    ```

    Una clase que se queda con una sola propiedad obligatoria también obtiene un constructor que la recibe, así que `new BetaManagedAgentsTextBlock("Hello Claude!")` también funciona. Una clase que se queda sin ninguna se construye con `new BetaManagedAgentsAlwaysAllowPolicy()`.

    Cuando leas una, compara la cadena:

    ```csharp
    // Antes
    if (message.Type.Value() == BetaManagedAgentsAgentMessageEventType.AgentMessage) { }

    // Después
    if (message.Type.GetString() == "agent.message") { }
    ```

    Para distinguir las variantes de una unión, sigue usando sus métodos `TryPick*`, `Switch` y `Match`, que no cambian.
  </Tab>

  <Tab title="Go">
    Un ejemplo de este tipo enum y su constante es `BetaManagedAgentsTextBlockType` y `BetaManagedAgentsTextBlockTypeText`. Casi todos son el campo `Type` de un struct bajo `client.Beta`: Agents, Deployments, Deployment Runs, Dreams, Environments, Memory Stores, Sessions, User Profiles y Vaults. En v2 estos campos usan un tipo del paquete `shared/constant`, como ya lo hacía `TextBlockParam.Type`. Un campo opcional, como `BetaManagedAgentsBashToolConfigParams.Type`, conserva su enum.

    En una solicitud, elimina el campo. Se serializa con su único valor cuando lo omites:

    ```go
    // Antes
    anthropic.BetaManagedAgentsTextBlockParam{
    	Type: anthropic.BetaManagedAgentsTextBlockTypeText,
    	Text: "Hello Claude!",
    }

    // Después
    anthropic.BetaManagedAgentsTextBlockParam{
    	Text: "Hello Claude!",
    }
    ```

    Un struct cuyo único campo era ese `Type` ahora tiene un constructor, como `NewBetaManagedAgentsAlwaysAllowPolicyParam()`. Como miembro de una unión, un puntero al struct vacío también funciona: `OfAlwaysAllow: &anthropic.BetaManagedAgentsAlwaysAllowPolicyParam{}`.

    En una respuesta, compara con la cadena:

    ```go
    // Antes
    switch event.Type {
    case string(anthropic.BetaManagedAgentsAgentMessageEventTypeAgentMessage):
    }

    // Después
    switch event.Type {
    case "agent.message":
    }
    ```

    Una variable, campo o parámetro tuyo que se declaró con uno de los tipos eliminados adopta el nuevo tipo del campo, como `constant.AgentMessage`. `constant.ValueOf[constant.AgentMessage]()` devuelve su valor.

    Los constructores de unión cambian con los campos. Estos cinco pierden su argumento `type_`:

    * `BetaManagedAgentsEventParamsOfUserToolConfirmation`
    * `BetaManagedAgentsEventParamsOfUserDefineOutcome`
    * `BetaManagedAgentsDeploymentInitialEventParamsOfUserDefineOutcome`
    * `BetaManagedAgentsMultiagentRosterEntryParamsOfBetaManagedAgentsAgents`
    * `BetaManagedAgentsMultiagentRosterEntryParamsOfBetaManagedAgentsAdvisors`

    Estos cuatro no recibían ningún otro argumento, así que desaparecen. Establece el campo de la unión en un puntero al struct vacío, como `BetaManagedAgentsEventParamsUnion{OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{}}`:

    * `BetaManagedAgentsEventParamsOfUserInterrupt`
    * `BetaManagedAgentsMultiagentRosterEntryParamsOfBetaManagedAgentsMultiagentSelfs`
    * `BetaManagedAgentsCredentialNetworkingParamsOfUnrestricted`
    * `BetaOutputBehaviorParamOfCreateNew`
  </Tab>

  <Tab title="Java">
    Un ejemplo de este enum es la clase anidada `BetaManagedAgentsTextBlock.Type`, con su único miembro `TEXT`. Casi todos son el `type` de una clase bajo `com.anthropic.models.beta`: `agents`, `deploymentruns`, `deployments`, `dreams`, `environments`, `memorystores`, `sessions`, `userprofiles` y `vaults`. En v3 estos campos son un `JsonValue` que el builder rellena, como ya lo era el de `TextBlockParam`. Un campo opcional, como el `type` de `BetaManagedAgentsBashToolConfigParams`, conserva su enum.

    Cuando construyas un objeto, elimina la llamada:

    ```java
    // Antes
    BetaManagedAgentsTextBlock block = BetaManagedAgentsTextBlock.builder()
        .type(BetaManagedAgentsTextBlock.Type.TEXT)
        .text("Hello Claude!")
        .build();

    // Después
    BetaManagedAgentsTextBlock block = BetaManagedAgentsTextBlock.builder()
        .text("Hello Claude!")
        .build();
    ```

    Una clase que se queda con un solo campo obligatorio también obtiene un método de fábrica que lo recibe, así que `BetaManagedAgentsTextBlock.of("Hello Claude!")` también funciona. Una clase que se queda sin ninguno pierde `of(type)`, y sus uniones pierden la sobrecarga que recibía el enum, como `ofAlwaysAllow(type)`. Constrúyela con `BetaManagedAgentsAlwaysAllowPolicy.builder().build()`.

    Cuando leas uno, `type()` desaparece y `_type()` devuelve el `com.anthropic.core.JsonValue`:

    ```java
    // Antes
    if (message.type().equals(BetaManagedAgentsAgentMessageEvent.Type.AGENT_MESSAGE)) {}

    // Después
    if (message._type().equals(JsonValue.from("agent.message"))) {}
    ```

    Para distinguir las variantes de una unión, sigue usando sus métodos `is*()` y `as*()` y su visitor, que no cambian.
  </Tab>

  <Tab title="PHP">
    Un ejemplo de este caso de enum es `Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock\Type::TEXT`. Casi todos son el `type` de una clase de Managed Agents bajo `Anthropic\Beta`. Estas propiedades ahora son una cadena con ese único valor como predeterminado, como ya lo era el `type` de `Anthropic\Messages\TextBlockParam`. Una propiedad opcional, como el `type` de `Anthropic\Beta\Environments\BetaPackages`, conserva su enum.

    `with()` ya no recibe `type`. Elimínalo: PHP lanza "Unknown named parameter" para un `type:` sobrante, y en una llamada posicional los argumentos posteriores se desplazan una posición.

    ```php
    // Antes
    ManagedAgentsTextBlock::with(text: 'Hello Claude!', type: Type::TEXT);

    // Después
    ManagedAgentsTextBlock::with(text: 'Hello Claude!');
    ```

    Donde comparabas con un caso de enum, compara con la cadena:

    ```php
    // Antes
    $event->type === Type::AGENT_MESSAGE->value;

    // Después
    $event->type === 'agent.message';
    ```

    * `withType()` y el argumento `type` de `$client->beta->sessions->resources->add()` ahora solo reciben la cadena. En `add()` su valor predeterminado es `'file'`, así que puedes omitirlo.
    * Las solicitudes que escribes como arreglos no cambian, y siguen necesitando su `'type'`.
  </Tab>

  <Tab title="Ruby">
    Un ejemplo de esta constante de enum es `Anthropic::Beta::Sessions::BetaManagedAgentsTextBlock::Type::TEXT`. Casi todas son el `type` de una clase de Managed Agents bajo `Anthropic::Beta`. En v2 estos campos se declaran como una constante, como ya lo era el `type` de `Anthropic::TextBlockParam`. Un campo opcional, como el `type` de `Anthropic::Beta::BetaManagedAgentsGlobToolConfigParams`, conserva su enum.

    Reemplaza una constante eliminada por su símbolo:

    ```ruby
    # Antes
    event.type == Anthropic::Beta::Sessions::BetaManagedAgentsAgentMessageEvent::Type::AGENT_MESSAGE

    # Después
    event.type == :"agent.message"
    ```

    Las solicitudes que escribes como hashes no cambian, y siguen necesitando su `type`:

    ```ruby
    client.beta.sessions.events.send_(
      session.id,
      events: [{type: "user.message", content: [{type: "text", text: "Hello Claude!"}]}]
    )
    ```

    Cuando construyes una instancia de modelo, `type:` ahora tiene un valor predeterminado, así que puedes omitirlo: `Anthropic::Beta::Sessions::BetaManagedAgentsTextBlock.new(text: "Hello Claude!")`. Lo mismo ocurre con la palabra clave `type:` de `client.beta.sessions.resources.add`.

    Si verificas tipos con Sorbet, los alias `Type::OrSymbol` y `Type::TaggedSymbol` de estos módulos desaparecen con ellos, y los campos son un `Symbol`. `OrSymbol` también permitía un `String`, así que `.new(type: "text")` ya no pasa la verificación de tipos. Quita la palabra clave o pasa `:text`. En RBS, el alias `type_` de cada clase desaparece, y el campo es el literal, como `:text`.
  </Tab>
</Tabs>

### La compactación del lado del cliente se eliminó del tool runner

Esto afecta a los SDK de TypeScript y Ruby. La "client-side compaction" (compactación del lado del cliente) en el "tool runner" (ejecutor de herramientas) se eliminó en favor de la compactación del lado del servidor, que resume la conversación dentro de la API en lugar de hacerlo con una solicitud adicional desde el cliente.

<Tabs exclude="shell, python, csharp, go, java, php">
  <Tab title="TypeScript">
    La opción `compactionControl` y el módulo `@anthropic-ai/sdk/lib/tools/CompactionControl` ya no existen. La opción dejó de funcionar en la v0.129.0, donde pasarla lanzaba un `AnthropicError`. No está en el tipo de parámetros, así que TypeScript la reporta como una propiedad desconocida.

    Para seguir compactando automáticamente al alcanzar un umbral de tokens:

    ```typescript
    // Antes
    const runner = client.beta.messages.toolRunner({
      // ...
      compactionControl: { enabled: true, contextTokenThreshold: 100_000 }
    });

    // Después
    const runner = client.beta.messages.toolRunner({
      // ...
      betas: ["compact-2026-01-12"],
      context_management: {
        edits: [{ type: "compact_20260112", trigger: { type: "input_tokens", value: 100_000 } }]
      }
    });
    ```

    Para decidir tú mismo cuándo se compacta la conversación, llama a [`runner.compactBeforeNextTurn()`](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/helpers.md#compaction).
  </Tab>

  <Tab title="Ruby">
    La opción `compaction_control:` ya no existe. Dejó de funcionar en la v1.74.0, donde pasarla generaba un `ArgumentError`.

    Para seguir compactando automáticamente al alcanzar un umbral de tokens:

    ```ruby
    # Antes
    runner = client.beta.messages.tool_runner(
      # ...
      compaction_control: {enabled: true, context_token_threshold: 100_000}
    )

    # Después
    runner = client.beta.messages.tool_runner(
      # ...
      betas: ["compact-2026-01-12"],
      context_management: {
        edits: [{type: :compact_20260112, trigger: {type: :input_tokens, value: 100_000}}]
      }
    )
    ```

    Para decidir tú mismo cuándo se compacta la conversación, llama a [`#compact_before_next_turn`](https://github.com/anthropics/anthropic-sdk-ruby/blob/main/helpers.md#compact_before_next_turn---compact-the-conversation) en el runner.
  </Tab>
</Tabs>

El umbral de activación debe ser de al menos 50,000 tokens. Consulta [compactación](https://platform.claude.com/docs/es/build-with-claude/compaction-threshold) para ver las demás opciones (`pause_after_compaction`, `instructions` personalizadas).

### Text Completions se eliminó

La API heredada de Text Completions se eliminó de los SDK de TypeScript, Go, Java y Ruby, y de la CLI. Los modelos y las funciones más recientes no la admiten. Usa la [API de Messages](https://platform.claude.com/docs/es/api/messages) en su lugar.

<Tabs exclude="shell:cURL, python, csharp, php">
  <Tab title="CLI">
    El comando `ant completions create` ya no existe.

    ```bash
    # Antes
    ant completions create \
      --model "$MODEL" \
      --max-tokens-to-sample 1024 \
      --prompt $'\n\nHuman: Hello, Claude\n\nAssistant:'

    # Después
    ant messages create \
      --model "$MODEL" \
      --max-tokens 1024 \
      --message '{role: user, content: "Hello, Claude"}'
    ```

    El texto de la respuesta pasa del campo `completion` a `content.0.text`, así que un script que pasaba `--transform completion` ahora pasa `--transform content.0.text`.

    `ant messages create` también acepta `--stream`, pero sus eventos tienen otra forma: el texto llega en `delta.text` de los eventos `content_block_delta`, así que `--transform content.0.text` no se aplica a un stream.
  </Tab>

  <Tab title="TypeScript">
    `client.completions` ya no existe, junto con la clase de recurso `Completions` y los tipos `Completion`, `CompletionCreateParams`, `CompletionCreateParamsNonStreaming` y `CompletionCreateParamsStreaming`. `AnthropicBedrock` tampoco tiene ya una propiedad `completions`. Las constantes `HUMAN_PROMPT` y `AI_PROMPT`, que solo usaban los prompts de Text Completions, también desaparecieron, tanto como exportaciones como propiedades estáticas del cliente.

    ```typescript
    // Antes
    const completion = await client.completions.create({
      model,
      max_tokens_to_sample: 1024,
      prompt: "\n\nHuman: Hello, Claude\n\nAssistant:"
    });
    console.log(completion.completion);

    // Después
    const message = await client.messages.create({
      model,
      max_tokens: 1024,
      messages: [{ role: "user", content: "Hello, Claude" }]
    });
    console.log(message.content);
    ```

    `client.completions.create({ ..., stream: true })` se convierte en `client.messages.create({ ..., stream: true })` o `client.messages.stream({ ... })`.
  </Tab>

  <Tab title="Go">
    `client.Completions` ya no existe, junto con `CompletionService`, `NewCompletionService`, `CompletionNewParams` y `Completion`. `aws.Client` tampoco tiene ya un campo `Completions`. `bedrock.DefaultEndpoints` ya no incluye `/v1/complete`.

    ```go
    // Antes
    completion, err := client.Completions.New(ctx, anthropic.CompletionNewParams{
    	Model:             model,
    	MaxTokensToSample: 1024,
    	Prompt:            "\n\nHuman: Hello, Claude\n\nAssistant:",
    })
    fmt.Println(completion.Completion)

    // Después
    message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
    	Model:     model,
    	MaxTokens: 1024,
    	Messages: []anthropic.MessageParam{
    		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
    	},
    })
    fmt.Println(message.Content[0].Text)
    ```

    `client.Completions.NewStreaming` se convierte en `client.Messages.NewStreaming`.
  </Tab>

  <Tab title="Java">
    `client.completions()` ya no existe en `AnthropicClient` ni en `AnthropicClientAsync`, junto con `CompletionService`, `CompletionServiceAsync` y el paquete `com.anthropic.models.completions` (`CompletionCreateParams` y `Completion`).

    ```java
    // Antes
    CompletionCreateParams params = CompletionCreateParams.builder()
        .model(model)
        .maxTokensToSample(1024)
        .prompt("\n\nHuman: Hello, Claude\n\nAssistant:")
        .build();

    System.out.println(client.completions().create(params).completion());

    // Después
    MessageCreateParams params = MessageCreateParams.builder()
        .model(model)
        .maxTokens(1024)
        .addUserMessage("Hello, Claude")
        .build();

    client.messages().create(params).content().stream()
        .flatMap(contentBlock -> contentBlock.text().stream())
        .forEach(textBlock -> System.out.println(textBlock.text()));
    ```

    `client.completions().createStreaming` se convierte en `client.messages().createStreaming`.
  </Tab>

  <Tab title="Ruby">
    `client.completions` ya no existe, junto con `Anthropic::Resources::Completions`, `Anthropic::CompletionCreateParams` y `Anthropic::Completion`. `Anthropic::BedrockClient` tampoco tiene ya un lector `completions`. En `Anthropic::BedrockMantleClient` y `Anthropic::GoogleCloudClient`, donde `completions` generaba `NotImplementedError`, el método ya no existe.

    ```ruby
    # Antes
    completion = client.completions.create(
      model: model,
      max_tokens_to_sample: 1024,
      prompt: "\n\nHuman: Hello, Claude\n\nAssistant:"
    )
    puts(completion.completion)

    # Después
    message = client.messages.create(
      model: model,
      max_tokens: 1024,
      messages: [{role: "user", content: "Hello, Claude"}]
    )
    puts(message.content)
    ```

    `client.completions.create_streaming` se convierte en `client.messages.stream`.
  </Tab>
</Tabs>

### `betas` se eliminó de los métodos de Models que no son beta

Los métodos de Models que no son beta ya no aceptan `betas` (csharp, go: `Betas`; java: `.addBeta()`), y los comandos `ant models` ya no aceptan `--beta`. Para enviar encabezados beta, llama a los mismos métodos en `client.beta.models` (csharp, go: `client.Beta.Models`; java: `client.beta().models()`; php: `$client->beta->models`), o a `ant beta:models` en la CLI. En el SDK, esos métodos devuelven `BetaModelInfo` (ruby: `Anthropic::Beta::BetaModelInfo`) donde los que no son beta devuelven `ModelInfo` (ruby: `Anthropic::ModelInfo`).

<Tabs exclude="shell:cURL">
  <Tab title="CLI">
    ```bash
    # Antes
    ant models list --beta "$BETA"

    # Después
    ant beta:models list --beta "$BETA"
    ```
  </Tab>

  <Tab title="Python">
    `client.models.retrieve()` y `client.models.list()` ya no aceptan `betas` (pasarlo produce un `TypeError`, y un verificador de tipos lo señala), y `anthropic.types.ModelListParams` ya no tiene una clave `betas`.

    ```python
    # Antes
    page = client.models.list(betas=[beta])

    # Después
    page = client.beta.models.list(betas=[beta])
    ```
  </Tab>

  <Tab title="TypeScript">
    `Anthropic.ModelRetrieveParams` y `Anthropic.ModelListParams` ya no tienen una propiedad `betas`.

    ```typescript
    // Antes
    const page = await client.models.list({ betas: [beta] });

    // Después
    const page = await client.beta.models.list({ betas: [beta] });
    ```
  </Tab>

  <Tab title="C#">
    `ModelRetrieveParams` y `ModelListParams` en `Anthropic.Models.Models` ya no tienen una propiedad `Betas`. Los métodos de `client.Beta.Models` aceptan las clases con los mismos nombres en `Anthropic.Models.Beta.Models`.

    ```csharp
    // Antes
    using Anthropic.Models.Models;

    var page = await client.Models.List(new ModelListParams { Betas = [beta] });

    // Después
    using Anthropic.Models.Beta.Models;

    var page = await client.Beta.Models.List(new ModelListParams { Betas = [beta] });
    ```
  </Tab>

  <Tab title="Go">
    `ModelGetParams` y `ModelListParams` ya no tienen un campo `Betas`.

    ```go
    // Antes
    page, err := client.Models.List(ctx, anthropic.ModelListParams{
    	Betas: []anthropic.AnthropicBeta{beta},
    })

    // Después
    page, err := client.Beta.Models.List(ctx, anthropic.BetaModelListParams{
    	Betas: []anthropic.AnthropicBeta{beta},
    })
    ```
  </Tab>

  <Tab title="Java">
    `ModelRetrieveParams` y `ModelListParams` en `com.anthropic.models.models` ya no tienen `betas()`, y sus builders ya no tienen `betas` ni `addBeta`. Los métodos de `client.beta().models()` aceptan las clases con los mismos nombres en `com.anthropic.models.beta.models`.

    ```java
    // Antes
    import com.anthropic.models.models.ModelListPage;
    import com.anthropic.models.models.ModelListParams;

    ModelListPage page = client.models().list(ModelListParams.builder().addBeta(beta).build());

    // Después
    import com.anthropic.models.beta.models.ModelListPage;
    import com.anthropic.models.beta.models.ModelListParams;

    ModelListPage page = client.beta().models().list(ModelListParams.builder().addBeta(beta).build());
    ```
  </Tab>

  <Tab title="PHP">
    `$client->models->retrieve()` y `$client->models->list()` ya no aceptan `betas` (pasarlo lanza `Error: Unknown named parameter $betas`), y `Anthropic\Models\ModelRetrieveParams` y `Anthropic\Models\ModelListParams` ya no tienen una propiedad `betas`.

    ```php
    // Antes
    $page = $client->models->list(betas: [$beta]);

    // Después
    $page = $client->beta->models->list(betas: [$beta]);
    ```

    Si pasas argumentos por posición, `workspaceID` y `requestOptions` suben cada uno una posición en ambos métodos.
  </Tab>

  <Tab title="Ruby">
    `client.models.retrieve` y `client.models.list` ya no aceptan `betas:`, y `Anthropic::ModelRetrieveParams` y `Anthropic::ModelListParams` ya no tienen un campo `betas`.

    ```ruby
    # Antes
    page = client.models.list(betas: [beta])

    # Después
    page = client.beta.models.list(betas: [beta])
    ```

    Pasar `betas:` a `client.models` no genera un error, pero ya no establece el encabezado `anthropic-beta`.
  </Tab>
</Tabs>

### Las claves de API se envían como `Authorization: Bearer`

El SDK y la CLI envían tu clave de API como `Authorization: Bearer <key>` en lugar de en el encabezado `X-Api-Key`, así que cambia cualquier proxy o "gateway" (puerta de enlace) que lea o establezca `X-Api-Key` para que use `Authorization`, o [envía tú mismo la clave en `X-Api-Key`](https://platform.claude.com/docs/es/api/migration-2026-10-21#sdk-proxy-x-api-key). La Claude API acepta una clave en cualquiera de los dos encabezados, así que un programa que se comunica directamente con la API no necesita cambios.

```text wrap
# Before
X-Api-Key: <key>

# After
Authorization: Bearer <key>
```

<Tabs>
  <Tab title="CLI">
    Esto se aplica a una clave en `ANTHROPIC_API_KEY` y a una que se pasa por tubería a `--api-key-stdin`.

    Con `--debug`, el registro de solicitudes ahora muestra `Authorization: Bearer <REDACTED>` donde antes mostraba `X-Api-Key: <REDACTED>`.
  </Tab>

  <Tab title="Python">
    `Authorization` es el encabezado que `auth_token` ya usa.

    Esto se aplica al argumento `api_key` y a la variable de entorno `ANTHROPIC_API_KEY` de `Anthropic` y `AsyncAnthropic`, y a `api_key` y `ANTHROPIC_AWS_API_KEY` de `AnthropicAWS`. `AnthropicFoundry` sigue enviando su clave como `x-api-key`, y `AnthropicBedrockMantle` ya enviaba un token bearer.
  </Tab>

  <Tab title="TypeScript">
    `Authorization` es el encabezado que la opción `authToken` ya usa.

    Esto se aplica a la opción `apiKey`, a la variable de entorno `ANTHROPIC_API_KEY`, y a `apiKey` y `ANTHROPIC_AWS_API_KEY` en `@anthropic-ai/aws-sdk`. `apiKey` en `@anthropic-ai/foundry-sdk` sigue enviándose como `X-Api-Key`, y `apiKey` en `@anthropic-ai/bedrock-sdk` ya era un token bearer.

    Un middleware o un `fetch` personalizado que lea la clave del encabezado de solicitud `x-api-key` debe leer `authorization` en su lugar, donde la clave va después de `Bearer `.
  </Tab>

  <Tab title="C#">
    `Authorization` es el encabezado que `AuthToken` ya usa.

    Esto se aplica a `ApiKey` en `AnthropicClient` y `ClientOptions`, y a la variable de entorno `ANTHROPIC_API_KEY`. En la versión de `Anthropic.Aws` que depende de la v13, también se aplica a `ApiKey` en `AwsClientOptions` y a `ANTHROPIC_AWS_API_KEY`. `AnthropicFoundryApiKeyCredentials` sigue enviando su clave como `x-api-key`, y `ApiKey` en `MantleAwsClientOptions` ya era un token bearer.
  </Tab>

  <Tab title="Go">
    Esto se aplica a `option.WithAPIKey`, a la variable de entorno `ANTHROPIC_API_KEY` y a `APIKey` en `aws.ClientConfig` (o `ANTHROPIC_AWS_API_KEY`). `APIKey` en `foundry.ClientConfig` (o `ANTHROPIC_FOUNDRY_API_KEY`) sigue enviándose como `X-Api-Key`, y `APIKey` en `bedrock.MantleClientConfig` ya era un token bearer.
  </Tab>

  <Tab title="Java">
    `Authorization` es el encabezado que `authToken` ya usa.

    Esto se aplica a `apiKey` en los builders de `AnthropicOkHttpClient`, `AnthropicOkHttpClientAsync` y `AnthropicBackend`, a la variable de entorno `ANTHROPIC_API_KEY` y a la propiedad del sistema `anthropic.apiKey`. `apiKey` en el builder de `FoundryBackend` sigue enviándose como `x-api-key`. `apiKey` en los builders de `AwsBackend`, `BedrockBackend` y `BedrockMantleBackend` ya era un token bearer.

    Un interceptor ahora ve la clave en `Authorization` en lugar de en `X-Api-Key`.
  </Tab>

  <Tab title="PHP">
    `Authorization` es el encabezado que `authToken` ya usa.

    Esto se aplica al argumento `apiKey` y a la variable de entorno `ANTHROPIC_API_KEY` de `Anthropic\Client`, y a `apiKey` y `ANTHROPIC_AWS_API_KEY` de `Anthropic\Aws\Client`. `Anthropic\Foundry\Client` sigue enviando su clave como `x-api-key`, y `Anthropic\Bedrock\MantleClient` ya enviaba un token bearer.
  </Tab>

  <Tab title="Ruby">
    `Authorization` es el encabezado que `auth_token:` ya usa.

    Esto se aplica al argumento `api_key:` y a la variable de entorno `ANTHROPIC_API_KEY` de `Anthropic::Client`, y a `api_key:` y `ANTHROPIC_AWS_API_KEY` de `Anthropic::AWSClient`. `Anthropic::BedrockMantleClient` ya enviaba un token bearer.

    El SDK descarta `Authorization` cuando sigue una redirección a otro origen, lo que incluye una redirección de `http` a `https` en el mismo host. Ahora la clave también se descarta en ese caso.
  </Tab>
</Tabs>

#### Si un proxy o gateway lee `X-Api-Key`

<Tabs exclude="shell:cURL">
  <Tab title="CLI">
    Si `--base-url` o `ANTHROPIC_BASE_URL` dirige `ant` a un proxy o gateway que lee `X-Api-Key`, cámbialo para que lea la clave de `Authorization`. `ant` no tiene ninguna opción ni variable de entorno para agregar un encabezado propio, así que no puede enviar `X-Api-Key`.
  </Tab>

  <Tab title="Python">
    Vuelve a mover la clave con una función `auth` en el cliente HTTP. Se ejecuta en cada solicitud que envía el cliente, incluidos los reintentos:

    ```python
    import httpx2
    from anthropic import Anthropic, DefaultHttpxClient


    def send_x_api_key(request: httpx2.Request) -> httpx2.Request:
        if request.headers.get("Authorization") == f"Bearer {api_key}":
            del request.headers["Authorization"]
            request.headers["X-Api-Key"] = api_key
        return request


    client = Anthropic(api_key=api_key, http_client=DefaultHttpxClient(auth=send_x_api_key))
    ```

    `AsyncAnthropic` acepta la misma función, en `http_client=DefaultAsyncHttpxClient(auth=send_x_api_key)`.
  </Tab>

  <Tab title="TypeScript">
    Establece el encabezado tú mismo. `Authorization: null` evita que la clave también se envíe ahí:

    ```typescript
    const client = new Anthropic({
      apiKey,
      defaultHeaders: { "X-Api-Key": apiKey, Authorization: null }
    });
    ```

    Si el proxy agrega la credencial y tu cliente no tiene ninguna, la v0 te permitía indicarlo con `"X-Api-Key": null` o con `Authorization: null` en `defaultHeaders`. La v1 solo acepta `Authorization: null`.

    El poller y el worker de trabajo del entorno reciben su propia clave, así que excluyen los encabezados predeterminados `X-Api-Key` y `Authorization` de sus propias solicitudes.
  </Tab>

  <Tab title="C#">
    Establece el encabezado tú mismo con `ExtraHeaders`. Establecer `ApiKey` y `AuthToken` en `null` evita que también se envíe una credencial encontrada en `ANTHROPIC_API_KEY` o `ANTHROPIC_AUTH_TOKEN`:

    ```csharp
    AnthropicClient client = new(
        new ClientOptions
        {
            ApiKey = null,
            AuthToken = null,
            ExtraHeaders = new Dictionary<string, string> { ["X-Api-Key"] = apiKey },
        }
    );
    ```
  </Tab>

  <Tab title="Go">
    Establece el encabezado tú mismo. `option.WithHeaderDel` evita que también se envíe una clave encontrada en `ANTHROPIC_API_KEY`:

    ```go
    client := anthropic.NewClient(
    	option.WithHeader("X-Api-Key", apiKey),
    	option.WithHeaderDel("Authorization"),
    )
    ```

    `environments.WorkPoller` y `environments.EnvironmentWorker` reciben su propia clave, así que excluyen un `X-Api-Key` establecido manualmente de sus propias solicitudes.
  </Tab>

  <Tab title="Java">
    Establece el encabezado tú mismo en lugar de llamar a `apiKey`:

    ```java
    AnthropicClient client = AnthropicOkHttpClient.builder()
        .putHeader("X-Api-Key", apiKey)
        .build();
    ```
  </Tab>

  <Tab title="PHP">
    Vuelve a mover la clave con un middleware:

    ```php
    use Anthropic\Client;
    use Psr\Http\Message\RequestInterface;
    use Psr\Http\Message\ResponseInterface;

    $sendXApiKey = fn (RequestInterface $request, callable $next): ResponseInterface => $next(
        $request->withoutHeader('Authorization')->withHeader('X-Api-Key', $apiKey)
    );

    $client = new Client(apiKey: $apiKey, requestOptions: ['middleware' => [$sendXApiKey]]);
    ```

    No uses los `extraHeaders` del cliente para esto. Una solicitud que tiene sus propios `extraHeaders` reemplaza los del cliente, y todos los endpoints bajo `$client->beta` que necesitan un encabezado beta los tienen.
  </Tab>

  <Tab title="Ruby">
    Vuelve a mover la clave con un middleware:

    ```ruby
    send_x_api_key = lambda do |req, nxt|
      next nxt.call(req) unless req.headers["authorization"] == "Bearer #{api_key}"
      nxt.call(req.with(headers: {**req.headers.except("authorization"), "x-api-key" => api_key}))
    end

    client = Anthropic::Client.new(api_key: api_key, middleware: send_x_api_key)
    ```

    La primera línea deja intacta una solicitud cuando no lleva la clave, como una que se redirigió a otro origen.

    `Anthropic::AWSClient` no acepta un argumento `middleware:`. En su lugar, pasa el middleware con cada solicitud, como `request_options: {middleware: send_x_api_key}`.
  </Tab>
</Tabs>

#### Si estableciste o eliminaste `X-Api-Key` en una solicitud

Esto afecta a los SDK de Python, TypeScript, Go, PHP y Ruby.

Si eliminaste `X-Api-Key` para que una solicitud enviara solo un token de autenticación, borra ese código en lugar de cambiarlo, porque eliminar `Authorization` eliminaría el token. [Un cliente contiene una sola credencial](https://platform.claude.com/docs/es/api/migration-2026-10-21#sdk-one-credential), así que asegúrate de que sea el token: si no se pasa ninguna en el código y están establecidas tanto `ANTHROPIC_API_KEY` como `ANTHROPIC_AUTH_TOKEN`, el cliente usa la clave.

<Tabs exclude="shell, csharp, java">
  <Tab title="Python">
    El código que pasa `extra_headers={"X-Api-Key": omit}` para mantener la clave fuera de una solicitud ya no lo consigue. Pasa `extra_headers={"Authorization": omit}` en su lugar.

    `extra_headers={"X-Api-Key": other_key}` ya no reemplaza la clave del cliente: se envían ambas claves. Para enviar otra clave con una solicitud, pasa `extra_headers={"Authorization": f"Bearer {other_key}"}`.
  </Tab>

  <Tab title="TypeScript">
    `headers: { "X-Api-Key": otherKey }` en las opciones de una solicitud ya no reemplaza la clave del cliente: se envían ambas claves. Para enviar otra clave con una solicitud, pasa ``headers: { Authorization: `Bearer $\{otherKey}` }``.
  </Tab>

  <Tab title="Go">
    `option.WithHeaderDel("X-Api-Key")` ya no mantiene la clave fuera de una solicitud. Elimina `Authorization` en su lugar:

    ```go
    // Antes
    client.Messages.New(ctx, params, option.WithHeaderDel("X-Api-Key"))

    // Después
    client.Messages.New(ctx, params, option.WithHeaderDel("Authorization"))
    ```

    `option.WithHeader("X-Api-Key", otherKey)` ya no reemplaza la clave del cliente: se envían ambas claves. Para enviar otra clave con una solicitud, pasa `option.WithAPIKey(otherKey)`.
  </Tab>

  <Tab title="PHP">
    `'extraHeaders' => ['X-Api-Key' => null]` ya no mantiene la clave fuera de una solicitud. Para enviar una solicitud sin credencial, establece `Authorization` en `null` en su lugar, o elimínalo en un middleware si hacías esto para todo el cliente.

    `'extraHeaders' => ['X-Api-Key' => $otherKey]` ya no reemplaza la clave del cliente: se envían ambas claves. Para enviar otra clave con una solicitud, establece `Authorization` en `"Bearer {$otherKey}"`.
  </Tab>

  <Tab title="Ruby">
    `extra_headers: {"x-api-key" => nil}` ya no mantiene la clave fuera de una solicitud. Establece `"authorization"` en `nil` en su lugar. Para enviar otra clave con una solicitud, establece `"authorization"` en `"Bearer #{other_key}"`, no `"x-api-key"`.
  </Tab>
</Tabs>

### Un cliente contiene una sola credencial

Una clave de API y un token de autenticación ahora comparten un encabezado, así que un cliente del SDK contiene una sola credencial. Si no se pasa ninguna en el código, sigue el orden de [precedencia de credenciales](https://platform.claude.com/docs/es/manage-claude/wif-reference#credential-precedence).

<Tabs exclude="shell">
  <Tab title="Python">
    La v1 enviaba tanto una clave de API como un token de autenticación cuando el cliente tenía ambos, cada uno en su propio encabezado. La credencial es una de `api_key`, `auth_token`, `credentials`, `config` o `profile`.

    * **Pasa como máximo una.** Dos o más en el constructor, o en una sola llamada a `copy()` o `with_options()`, generan `ValueError`. `None` no cuenta. La v1 generaba `TypeError`, y solo para dos de `credentials`, `config` y `profile`.
    * **La que pasas es la única.** Como en la v1, el cliente no busca otra en el entorno ni en el disco.
    * **Si no se pasa ninguna, solo se usa la primera de estas que esté establecida:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, el perfil que nombra `ANTHROPIC_PROFILE`, las variables de "workload identity federation" (federación de identidades de carga de trabajo), el perfil activo en el disco (el que nombra el archivo `active_config`; de lo contrario, `default`). Cuando las dos primeras están establecidas, el cliente registra una advertencia una vez. La v1 usaba el perfil activo antes que las variables de federación cuando `ANTHROPIC_CONFIG_DIR` estaba establecida o existía un archivo `active_config`; establece `ANTHROPIC_PROFILE` para seguir usándolo.
    * **`copy()` y `with_options()` reemplazan.** Una credencial que pasas reemplaza la del propio cliente, sea del tipo que sea cualquiera de las dos. Una URL base y encabezados que provenían de la configuración o el perfil de la credencial anterior se van con ella, y se usan los de la nueva credencial. Una URL base que pasaste se mantiene.

    ```python
    client = Anthropic(api_key=key, auth_token=token)

    # Antes: enviaba `X-Api-Key: <key>` y `Authorization: Bearer <token>`
    # Después:  genera `ValueError`
    ```

    Los clientes de plataforma mantienen sus propias reglas. `AnthropicAWS` acepta `auth_token` junto a `api_key` sin error, y envía la clave.

    Los docstrings de `Anthropic.__init__` y `Anthropic.copy` en [`_client.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_client.py) tienen los detalles.

    Si dependías de enviar ambas, por ejemplo un token para un gateway y una clave para la API detrás de él, pasa el token como `auth_token` y establece `X-Api-Key` en `default_headers`. Haz que el gateway elimine su encabezado `Authorization` antes de reenviar la solicitud. La Claude API espera una sola credencial por solicitud.
  </Tab>

  <Tab title="TypeScript">
    En la v0, un cliente que tenía tanto una clave de API como un token de autenticación enviaba ambos, cada uno en su propio encabezado. La credencial es una de `apiKey`, `authToken`, `credentials`, `config` o `profile`.

    * **Pasa como máximo una.** Dos o más en `new Anthropic()`, o en una sola llamada a `withOptions()`, lanzan un `TypeError`. `null` y `undefined` no cuentan.
    * **La que pasas es la única.** El cliente no busca otra en el entorno ni en el disco.
    * **Si no se pasa ninguna, se usa la primera de estas:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, el perfil que nombra `ANTHROPIC_PROFILE`, las variables de federación de identidades de carga de trabajo, el perfil activo en el disco (el que nombra el archivo `active_config`; de lo contrario, `default`). Cuando las dos primeras están establecidas, el cliente registra una advertencia una vez. En la v0, el perfil activo iba antes que las variables de federación. Un `ANTHROPIC_PROFILE` que nombra un perfil inexistente se omitía en la v0, y ahora la primera solicitud lanza un `AnthropicError`. `loadConfig()` sin argumentos sigue el mismo orden y lanza el mismo error.
    * **`withOptions` reemplaza.** Una credencial que pasas reemplaza la del cliente que copias, así que `client.withOptions({ authToken })` ya no necesita `apiKey: null` junto a ella.
    * **Un cliente de plataforma solo acepta las credenciales de su plataforma.** Los clientes de AWS, Bedrock y Foundry y `AnthropicVertex` lanzan un `AnthropicError` para una opción de credencial con la que no se autentican, como ya hacía `AnthropicGoogleCloud`. En los clientes de AWS y Bedrock, `apiKey: null` ahora borra la clave, así que el cliente firma con credenciales de AWS. `AnthropicBedrock` guarda su clave en `client.apiKey`, donde la v0 la guardaba en `client.authToken`.

    ```typescript
    // Antes: enviaba la clave de API e ignoraba `credentials`
    const client = new Anthropic({ apiKey, credentials });

    // Después: la línea anterior lanza un TypeError
    const client = new Anthropic({ credentials });
    ```

    Los comentarios de documentación de [`ClientOptions`](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/src/client.ts) indican cómo se comporta cada opción, y el comentario de documentación del constructor de cada uno de esos clientes de plataforma enumera las opciones que rechaza.

    Si dependías de enviar ambas, por ejemplo un token para un gateway y una clave para la API detrás de él, establece `X-Api-Key` en `defaultHeaders` y mantén `authToken`. Haz que el gateway elimine su encabezado `Authorization` antes de reenviar la solicitud. La Claude API espera una sola credencial por solicitud.
  </Tab>

  <Tab title="C#">
    En la v12, un cliente que tenía tanto una clave de API como un token de autenticación enviaba ambos, cada uno en su propio encabezado. La credencial es una de `ApiKey`, `AuthToken` o `ClientOptions.Credentials`.

    * **Pasar dos es un error.** Establecer una segunda lanza una `ArgumentException`. `null` no cuenta, así que para cambiar la credencial en unas `ClientOptions` que construiste tú mismo, establece primero la anterior en `null`.
    * **Pasar una la usa e ignora el entorno.** No se lee ninguna variable de entorno de credenciales ni ningún perfil en el disco. Del mismo modo, `AnthropicCredentials.Resolve(profile: "name")` ahora devuelve las credenciales de ese perfil cuando `ANTHROPIC_API_KEY` o `ANTHROPIC_AUTH_TOKEN` está establecida.
    * **No pasar ninguna usa la primera coincidencia en el entorno:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, las variables de federación de identidades de carga de trabajo y, luego, el perfil activo en el disco. Un `ANTHROPIC_API_KEY` o `ANTHROPIC_AUTH_TOKEN` vacío cuenta como no establecido. Cuando las dos primeras están establecidas, se envía una advertencia a stderr una vez. El cliente busca cuando se usa por primera vez, así que un `ANTHROPIC_PROFILE` que no existe ahora lanza una excepción desde la primera solicitud en lugar de desde el constructor.
    * **`WithOptions` reemplaza.** Una credencial que pasas en el modificador reemplaza la del cliente, sea del tipo que sea, así que `AuthToken = token` ya no necesita `ApiKey = null` junto a ella. Si no se pasa ninguna, la copia mantiene la credencial, `BaseUrl` y `ExtraHeaders` del cliente, incluso cuando el modificador devuelve nuevas `ClientOptions`.

    ```csharp
    // Antes: se enviaban ambos, cada uno en su propio encabezado
    AnthropicClient client = new() { ApiKey = apiKey, AuthToken = token };

    // Después: la línea anterior lanza una ArgumentException
    AnthropicClient client = new() { AuthToken = token };
    ```

    La documentación XML de esas tres propiedades de [`ClientOptions`](https://github.com/anthropics/anthropic-sdk-csharp/blob/main/src/Anthropic/Core/ClientOptions.cs) tiene los detalles.

    Los clientes de plataformas en la nube mantienen las credenciales con las que se construyeron. Los clientes de Bedrock, Bedrock Mantle, Foundry, Google Cloud y AWS y `AnthropicVertexClient` deciden cómo autenticarse cuando los construyes, y la mayoría usa el encabezado `Authorization` para ello. Las versiones de estos paquetes que dependen de la v13 ignoran un `ApiKey` o `AuthToken` que establezcas mediante `WithOptions`. Para usar otra clave con `AnthropicAwsClient`, construye un nuevo cliente con `ApiKey` en `AwsClientOptions`.

    Si dependías de enviar ambas, por ejemplo un token para un gateway y una clave para la API detrás de él, establece `X-Api-Key` con `ExtraHeaders` y mantén `AuthToken`. Haz que el gateway elimine su encabezado `Authorization` antes de reenviar la solicitud. La Claude API espera una sola credencial por solicitud.
  </Tab>

  <Tab title="Go">
    En la v1, un cliente que tenía tanto una clave de API como un token de autenticación enviaba ambos, cada uno en su propio encabezado. La credencial proviene de `option.WithAPIKey`, `option.WithAuthToken`, `option.WithConfig`, `option.WithProfile` o `option.WithFederationTokenProvider`.

    * **Pasar dos de ellas a `anthropic.NewClient` es un error.** `NewClient` no devuelve ningún error, así que todas las solicitudes que hace el cliente fallan con él. `NewClient` ya aplica `anthropic.DefaultClientOptions()`, así que no pases también su resultado: la credencial que encuentra en el entorno contaría entonces como una que pasaste.
    * **Una de ellas se usa tal cual.** No se lee ninguna credencial del entorno ni de un perfil en el disco. En la v1, `ANTHROPIC_API_KEY` y `ANTHROPIC_AUTH_TOKEN` tenían prioridad sobre las tres últimas.
    * **Sin ninguna de ellas, se usa la primera de estas que esté establecida,** en el mismo orden que en la v1: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, las variables de federación de identidades de carga de trabajo, el perfil activo en el disco. Cuando las dos primeras están establecidas, el SDK registra una advertencia una vez.
    * **Una de ellas en una solicitud reemplaza la credencial del cliente para esa solicitud,** así que `option.WithAuthToken` ya no necesita `option.WithHeaderDel("X-Api-Key")` junto a ella.

    ```go
    // Antes: se enviaba la clave de API y se registraba una advertencia
    client := anthropic.NewClient(
    	option.WithAPIKey(apiKey),
    	option.WithProfile("work"),
    )

    // Después: todas las solicitudes del cliente anterior fallan, así que pasa la que quieras
    client := anthropic.NewClient(option.WithAPIKey(apiKey))
    ```

    La advertencia que registraba la v1 ya no existe, así que `option.WithConfigQuiet`, que la suprimía, ahora hace lo mismo que `option.WithConfig` y está obsoleta.

    Los comentarios de documentación de `anthropic.NewClient` y `anthropic.DefaultClientOptions` en [`client.go`](https://github.com/anthropics/anthropic-sdk-go/blob/main/client.go) tienen los detalles.

    Si dependías de enviar ambas, por ejemplo un token para un gateway y una clave para la API detrás de él, establece `X-Api-Key` con `option.WithHeader` y mantén `option.WithAuthToken`. Haz que el gateway elimine su encabezado `Authorization` antes de reenviar la solicitud. La Claude API espera una sola credencial por solicitud.

    `ANTHROPIC_CUSTOM_HEADERS` y `ANTHROPIC_WEBHOOK_SIGNING_KEY` ahora se aplican a todos los clientes que construye `anthropic.NewClient`. En la v1 solo se leían cuando ni el entorno ni un perfil en el disco contenían una credencial.
  </Tab>

  <Tab title="Java">
    En la v2, un cliente que tenía tanto una clave de API como un token de autenticación enviaba ambos, cada uno en su propio encabezado. La credencial es una de `apiKey`, `authToken` o `configurationProvider` en los builders de `AnthropicOkHttpClient`, `AnthropicOkHttpClientAsync` y `AnthropicBackend`.

    * **Establece como máximo una.** Con dos en un mismo builder, `build()` lanza una `IllegalStateException` que las nombra. `null` o un `Optional` vacío no cuentan.
    * **La que estableces es la única.** `fromEnv()`, llamado antes o después, no toma ninguna credencial del entorno ni de un perfil en el disco. En la v2, una clave o un token que encontraba `fromEnv()` podía enviarse también, o en su lugar.
    * **Si no se establece ninguna, `fromEnv()` usa la primera de estas que encuentre:** `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, las variables de federación de identidades de carga de trabajo, el perfil activo en el disco. Cuando las dos primeras están establecidas, se envía una advertencia a `System.err` una vez. Las propiedades del sistema `anthropic.apiKey` y `anthropic.authToken` cuentan como el entorno. Una clave o un token vacíos, en una variable o en una propiedad del sistema, cuentan como no establecidos. En la v2, `fromEnv()` usaba el valor vacío como clave o token.

    ```java
    // Antes: se enviaban ambos. En v3, build() lanza una IllegalStateException
    AnthropicClient client = AnthropicOkHttpClient.builder()
        .apiKey(apiKey)
        .authToken(authToken)
        .build();

    // Después
    AnthropicClient client = AnthropicOkHttpClient.builder()
        .apiKey(apiKey)
        .build();
    ```

    La documentación de `fromEnv()` en esos builders tiene los detalles.

    Si dependías de enviar ambas, por ejemplo un token para un gateway y una clave para la API detrás de él, establece ambos encabezados tú mismo:

    ```java
    AnthropicClient client = AnthropicOkHttpClient.builder()
        .putHeader("Authorization", "Bearer " + gatewayToken)
        .putHeader("X-Api-Key", apiKey)
        .build();
    ```

    Haz que el gateway elimine su encabezado `Authorization` antes de reenviar la solicitud. La Claude API espera una sola credencial por solicitud.
  </Tab>

  <Tab title="PHP">
    Las versiones anteriores enviaban tanto una clave de API como un token de autenticación cuando el cliente tenía ambos, cada uno en su propio encabezado. `Anthropic\Client` acepta una sola credencial, de `apiKey`, `authToken` o `credentials`:

    * Pasar más de una de ellas lanza `\InvalidArgumentException`. Solo `null` cuenta como no pasada. Una cadena vacía cuenta como pasada, así que `new Client(apiKey: '')` no envía ninguna credencial.
    * Pasar una la usa, y no se lee ninguna credencial del entorno ni de un perfil en el disco. Con `ANTHROPIC_API_KEY` establecida, `new Client(authToken: $token)` envía el token. Las versiones anteriores enviaban también la clave del entorno.
    * No pasar ninguna usa la primera de estas que esté establecida: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, el perfil nombrado por `ANTHROPIC_PROFILE`, las variables de federación de identidades de carga de trabajo, el perfil activo en el disco (el que nombra el archivo `active_config`; de lo contrario, `default`). Las versiones anteriores usaban el perfil activo antes que las variables de federación, y no usaban ningún perfil cuando no había un archivo `active_config`. También omitían un `ANTHROPIC_PROFILE` que nombraba un perfil inexistente o no válido. En ese caso, `new Client()` ahora lanza `Anthropic\Core\Exceptions\AnthropicException`. Con `ANTHROPIC_API_KEY` y `ANTHROPIC_AUTH_TOKEN` establecidas, el cliente envía la clave y su propiedad `authToken` queda vacía. El primer cliente construido de esa manera también escribe una advertencia con `error_log()`, una vez por proceso, lo que en un servidor web significa una vez por solicitud: `Anthropic: ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are both set. Using ANTHROPIC_API_KEY.` Para detenerla, elimina la variable que no usas.

    ```php
    // Antes: enviaba la clave como X-Api-Key y el token como Authorization.
    // Después: lanza "Pass at most one of `apiKey`, `authToken`, or `credentials`. Received `apiKey`, `authToken`."
    $client = new Client(apiKey: $key, authToken: $token);
    ```

    Si dependías de enviar ambas, por ejemplo un token para un gateway y una clave para la API detrás de él, pasa el token como `authToken` y agrega `X-Api-Key` en un middleware como el de [Si un proxy o gateway lee `X-Api-Key`](https://platform.claude.com/docs/es/api/migration-2026-10-21#sdk-proxy-x-api-key), sin la llamada a `withoutHeader`. Haz que el gateway elimine su encabezado `Authorization` antes de reenviar la solicitud. La Claude API espera una sola credencial por solicitud.
  </Tab>

  <Tab title="Ruby">
    Los argumentos de credenciales de `Anthropic::Client.new` son `api_key:`, `auth_token:`, `credentials:` y `config:`. Un argumento `nil` cuenta como no pasado. Los clientes de plataforma mantienen sus propias reglas.

    * **Dos o más pasados:** `Anthropic::Client.new` genera `ArgumentError`. Las versiones anteriores solo lo generaban para `credentials:` con `config:`, y en los demás casos enviaban la clave si había una y, luego, el token.
    * **Uno pasado:** El cliente lo usa y no recurre a una credencial del entorno ni de un perfil en el disco.
    * **Ninguno pasado:** El cliente usa la primera de estas que encuentre: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_PROFILE`, las variables de federación de identidades de carga de trabajo, el perfil activo en el disco. El orden no ha cambiado, pero con `ANTHROPIC_API_KEY` y `ANTHROPIC_AUTH_TOKEN` establecidas, `client.auth_token` ahora es `nil`. Un `ANTHROPIC_API_KEY` o `ANTHROPIC_AUTH_TOKEN` vacío ahora cuenta como no establecido, mientras que las versiones anteriores permitían que un `ANTHROPIC_API_KEY` vacío ocultara el token y todo lo que venía después.

    Cuando no se pasa ninguna credencial y están establecidas tanto `ANTHROPIC_API_KEY` como `ANTHROPIC_AUTH_TOKEN`, el SDK también emite una advertencia una vez por proceso, en la salida de error estándar mediante `Kernel#warn`: `Anthropic: ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are both set. Using ANTHROPIC_API_KEY.` Elimina la variable que no usas para silenciarla.

    ```ruby
    # Antes: enviaba la clave e ignoraba el token.
    # Después: ArgumentError, "Pass at most one of `api_key:`, `auth_token:`, `credentials:`, or `config:`. Received `api_key:`, `auth_token:`."
    Anthropic::Client.new(api_key: api_key, auth_token: auth_token)

    # Pasa solo el que quieras enviar.
    Anthropic::Client.new(api_key: api_key)
    ```
  </Tab>
</Tabs>

### Cambios en un SDK o en la CLI

Cada uno de estos cambios afecta a un SDK o a la CLI.

<Tabs exclude="shell:cURL, python, typescript">
  <Tab title="CLI">
    **Se eliminaron `--api-key` y `--auth-token`.** `ant` ya no acepta una clave de API ni un token de autenticación como valor de un flag, porque un valor en la línea de comandos es visible en el historial del shell y en los listados de procesos. Ambos flags han mostrado una advertencia de obsolescencia desde la v1.27.0. Un comando que todavía pase uno de ellos se detiene con `flag provided but not defined`.

    | Eliminado              | Usa en su lugar                                           |
    | ---------------------- | --------------------------------------------------------- |
    | `--api-key <key>`      | `ANTHROPIC_API_KEY`, `--api-key-stdin` o `ant auth login` |
    | `--auth-token <token>` | `ANTHROPIC_AUTH_TOKEN` o `--auth-token-stdin`             |

    ```bash
    # Antes
    ant --api-key "$KEY" models list

    # Después, desde el entorno
    export ANTHROPIC_API_KEY="$KEY"
    ant models list

    # Después, desde un gestor de secretos
    op read op://vault/anthropic/api-key | ant --api-key-stdin models list
    ```

    `--api-key-stdin` y `--auth-token-stdin` leen toda la entrada estándar, por lo que un comando que use uno de ellos toma el cuerpo de su solicitud de los flags.

    `ant auth status`, la indicación de la primera ejecución de `ant apply` y la nota sobre múltiples fuentes de autenticación ahora llaman a estas credenciales `ANTHROPIC_API_KEY` y `ANTHROPIC_AUTH_TOKEN`, donde antes mostraban `--api-key / ANTHROPIC_API_KEY` y `--auth-token / ANTHROPIC_AUTH_TOKEN`.
  </Tab>

  <Tab title="C#">
    **El `Type` de una unión es un enum.** En la v12, la propiedad `Type` de una clase de unión, como `ContentBlock.Type` o `RawMessageStreamEvent.Type`, era un `JsonElement`. En la v13 es un `ApiEnum<string, T>`, donde `T` es un nuevo enum con un miembro para cada variante, como `ContentBlockType`. Algunas uniones se distinguen por `Name` o `Command` en su lugar, como `BetaMemoryTool20250818Command.Command`, y esas propiedades cambian de la misma manera. El `Type` de una clase de variante, como `TextBlock.Type`, sigue siendo un `JsonElement`.

    ```csharp
    // Antes
    if (block.Type.GetString() == "tool_use") { }

    // Después
    if (block.Type.Value() == ContentBlockType.ToolUse) { }
    ```

    `block.Type.Raw()` devuelve la cadena, incluida una para la que esta versión del SDK no tiene un miembro de enum.

    **`IChatClient` ignora las opciones de muestreo.** Los adaptadores de `IChatClient` (`AsIChatClient()`) ahora ignoran `ChatOptions.Temperature`, `ChatOptions.TopK` y `ChatOptions.TopP`. Consulta [Se eliminaron los parámetros de muestreo](https://platform.claude.com/docs/es/api/migration-2026-10-21#sampling-parameters).
  </Tab>

  <Tab title="Go">
    **Se eliminaron `vertex.DefaultVersion` y `bedrock.DefaultVersion`.**
  </Tab>

  <Tab title="Java">
    **Getters que ahora devuelven un `Optional`.** `apiKey()` y `authToken()` en `AnthropicBackend` ahora devuelven un `Optional<String>`, como ya lo hacía `apiKey()` en `AwsBackend` y `BedrockMantleBackend`. En la v2 cada uno devolvía un `String`, que era `null` cuando el backend no tenía una credencial de ese tipo.

    ```java
    // Antes
    String apiKey = backend.apiKey();
    if (apiKey != null) {}

    // Después
    Optional<String> apiKey = backend.apiKey();
    if (apiKey.isPresent()) {}
    ```

    Estos getters ahora también devuelven un `Optional`. En la v2 cada uno devolvía el valor en sí, que era `null` cuando no había ninguno:

    * `apiKey()` y `awsCredentialsProvider()` en `BedrockBackend`
    * `filename()` y `mimeType()` en `McpResourceFile`
    * `detail()` en `CredentialSource`
    * `body()` en `HttpRequest`
    * `resource()` en `FoundryBackend`, que era `getResource()`
    * `responseValidation()`, `timeout()`, `followRedirects()`, `fallbackState()` y `openTelemetry()` en `RequestOptions`, que eran `getResponseValidation()`, `getTimeout()`, `getFollowRedirects()`, `getFallbackState()` y `getOpenTelemetry()`

    ```java
    // Antes
    String filename = file.filename();
    if (filename != null) {}
    String resource = backend.getResource();

    // Después
    Optional<String> filename = file.filename();
    if (filename.isPresent()) {}
    Optional<String> resource = backend.resource();
    ```

    Se eliminó `getBaseUrl()` en `FoundryBackend`. Llama a `baseUrl()`, que devuelve la URL a la que van las solicitudes. Esa es la URL base que configuraste, o la que se construye a partir del recurso, y termina en `/anthropic`.

    También se eliminó `getBaseUrl()` en `AnthropicBackend`. Llama a `baseUrl()`, que devuelve el mismo valor.

    **`HttpRequest` requiere una URL base.** Si construyes un `HttpRequest` tú mismo, como en una prueba de un interceptor, configura su URL base. Sin ella, `build()` ahora lanza una `IllegalStateException`. En la v2 devolvía una solicitud cuyo `baseUrl()` era `null`. Las solicitudes que construye el SDK ya tenían una.

    ```java
    // Antes
    HttpRequest request = HttpRequest.builder()
        .method(HttpMethod.GET)
        .build();

    // Después
    HttpRequest request = HttpRequest.builder()
        .method(HttpMethod.GET)
        .baseUrl("https://api.anthropic.com")
        .build();
    ```

    `baseUrl()` sigue devolviendo un `String`, que ahora nunca es `null`. En el builder, se eliminó `baseUrl(Optional<String>)` y `baseUrl(String)` ya no acepta `null`.

    **Los interceptores ven `anthropic-version`.** El cliente ahora agrega el encabezado `anthropic-version` predeterminado antes de que se ejecuten los interceptores. En la v2, el backend lo agregaba después de ellos. Si tu interceptor configura su propia versión, llama a `replaceHeaders`, porque `putHeader` ahora agrega un segundo valor:

    ```java
    // Antes
    request.toBuilder().putHeader("anthropic-version", version).build();

    // Después
    request.toBuilder().replaceHeaders("anthropic-version", version).build();
    ```
  </Tab>

  <Tab title="PHP">
    **Los casos de enum que pasas se leen de vuelta como sus valores.** `with()` y los setters como `withAllowedCallers()` siguen aceptando un caso de enum o su valor. Las versiones anteriores conservaban un caso tal como lo pasabas. La v1 conserva el valor del caso, que es lo que ya declaran los tipos de las propiedades. Los objetos que el SDK construye a partir de una respuesta no cambian, y el JSON que envía el SDK es el mismo. Si lees una propiedad de vuelta y la comparas con un caso, la comparación ahora es falsa y no se lanza nada, así que compárala con el valor del caso:

    ```php
    $tool = CodeExecutionTool20250825::with(allowedCallers: [AllowedCaller::DIRECT]);

    // Antes
    in_array(AllowedCaller::DIRECT, $tool->allowedCallers, true);

    // Después
    in_array(AllowedCaller::DIRECT->value, $tool->allowedCallers, true);
    ```
  </Tab>

  <Tab title="Ruby">
    **Se eliminaron `Anthropic::VertexClient::DEFAULT_VERSION` y `Anthropic::BedrockClient::DEFAULT_VERSION`.**
  </Tab>
</Tabs>

### La licencia es Apache 2.0

A partir de estas versiones, el SDK y la CLI tienen licencia Apache License, Version 2.0. Las versiones anteriores siguen bajo la MIT License. No se necesitan cambios en el código. Si llevas un registro de las licencias de tus dependencias, actualiza sus entradas.

* **TypeScript:** Los paquetes de plataforma (`@anthropic-ai/bedrock-sdk`, `@anthropic-ai/vertex-sdk`, `@anthropic-ai/foundry-sdk`, `@anthropic-ai/aws-sdk` y `@anthropic-ai/google-cloud-sdk`) cambian de licencia en sus propias versiones 1.0.0.
* **C#:** Los demás paquetes (`Anthropic.Bedrock`, `Anthropic.Vertex`, `Anthropic.Foundry`, `Anthropic.Aws`, `Anthropic.GoogleCloud` y `Anthropic.Mcp`) cambian de licencia en sus propias versiones 1.0.0.
* **Java:** El cambio abarca todos los artefactos de `com.anthropic`.
