---
title: Flujo de eventos de la sesión
url: https://platform.claude.com/docs/es/managed-agents/events-and-streaming
description: Envía eventos, transmite respuestas e interrumpe o redirige tu sesión a mitad de la ejecución.
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

La comunicación con Claude Managed Agents se basa en eventos. Abres un flujo para seguir una sesión, envías eventos para dirigirla y le respondes cuando se pausa en espera de una entrada.

## Tipos de eventos

Los eventos fluyen en dos direcciones: tú envías eventos al agente y la sesión te devuelve eventos.

* **Eventos que envías:** los eventos `user.*` inician una sesión y la dirigen a medida que avanza. Los eventos [`system.message`](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#send-system-messages) añaden contexto a nivel de sistema.
* **Eventos que recibes:** los eventos de sesión, los eventos de span y los eventos del agente informan sobre el estado de la sesión y el progreso del agente.

Estas cadenas de tipo de evento siguen una convención de nomenclatura `{domain}.{action}`. Consulta [Tipos de eventos](https://platform.claude.com/docs/es/managed-agents/reference#event-types) en la referencia para ver el catálogo completo.

Los [tipos de eventos de webhook](https://platform.claude.com/docs/es/managed-agents/webhooks#supported-event-types) son independientes, y algunos de sus nombres difieren de los del flujo. Por ejemplo, los webhooks usan `session.status_idled` en lugar de `session.status_idle`.

## Transmitir eventos

Transmite eventos desde la sesión mediante "streaming" (streaming) para recibir actualizaciones en tiempo real mientras el agente trabaja. Un flujo entrega solo los eventos emitidos después de abrirse, así que abre el flujo antes de enviar eventos para evitar una condición de carrera.

<CodeGroup>
  ```bash cURL
  # Abre el stream antes de enviar el primer evento: solo se entregan los eventos posteriores.
  curl --fail-with-body -sS -N \
    "https://api.anthropic.com/v1/sessions/$SESSION_ID/events/stream?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "accept: text/event-stream"
  ```

  ```bash CLI
  # Abre el stream antes de enviar el primer evento: solo se entregan los eventos posteriores.
  ant beta:sessions:events stream --session-id "$SESSION_ID"
  ```

  ```python Python
  # Abre primero el stream y luego envía el mensaje del usuario
  with client.beta.sessions.events.stream(session.id) as stream:
      client.beta.sessions.events.send(
          session.id,
          events=[
              {
                  "type": "user.message",
                  "content": [{"type": "text", "text": "Summarize the repo README"}],
              },
          ],
      )

      for event in stream:
          match event.type:
              case "agent.message":
                  for block in event.content:
                      if block.type == "text":
                          print(block.text, end="")
              case "session.status_idle":
                  break
              case "session.error":
                  error_message = event.error.message if event.error else "unknown"
                  print(f"\n[Error: {error_message}]")
                  break
  ```

  ```typescript TypeScript
  // Abre primero el stream y luego envía el mensaje del usuario
  const stream = await client.beta.sessions.events.stream(session.id);
  await client.beta.sessions.events.send(session.id, {
    events: [
      {
        type: "user.message",
        content: [{ type: "text", text: "Summarize the repo README" }]
      }
    ]
  });

  events: for await (const event of stream) {
    switch (event.type) {
      case "agent.message":
        for (const block of event.content) {
          if (block.type === "text") {
            process.stdout.write(block.text);
          }
        }
        break;
      case "session.status_idle":
        break events;
      case "session.error":
        console.log(`\n[Error: ${event.error?.message ?? "unknown"}]`);
        break events;
    }
  }
  ```

  ```csharp C#
  // Abre primero el stream y luego envía el mensaje del usuario
  using var stream = await client.Beta.Sessions.Events.WithRawResponse.StreamStreaming(session.ID);
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = BetaManagedAgentsUserMessageEventParamsType.UserMessage,
              Content =
              [
                  new BetaManagedAgentsTextBlock
                  {
                      Type = BetaManagedAgentsTextBlockType.Text,
                      Text = "Summarize the repo README",
                  },
              ],
          },
      ],
  });

  await foreach (var streamEvent in stream.Enumerate())
  {
      if (streamEvent.Value is BetaManagedAgentsAgentMessageEvent message)
      {
          foreach (var block in message.Content)
          {
              if (block.Value is BetaManagedAgentsTextBlock textBlock)
              {
                  Console.Write(textBlock.Text);
              }
          }
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent)
      {
          break;
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionErrorEvent error)
      {
          Console.WriteLine($"\n[Error: {error.Error?.Message ?? "unknown"}]");
          break;
      }
  }
  ```

  ```go Go
  	// Abre primero el stream y luego envía el mensaje del usuario
  	stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
  	defer stream.Close()

  	if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  		Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  			OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
  				Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  				Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  					OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  						Type: anthropic.BetaManagedAgentsTextBlockTypeText,
  						Text: "Summarize the repo README",
  					},
  				}},
  			},
  		}},
  	}); err != nil {
  		panic(err)
  	}

  events:
  	for stream.Next() {
  		switch event := stream.Current().AsAny().(type) {
  		case anthropic.BetaManagedAgentsAgentMessageEvent:
  			// lista de tipo concreto: BetaManagedAgentsTextBlock
  			for _, block := range event.Content {
  				fmt.Print(block.Text)
  			}
  		case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
  			break events
  		case anthropic.BetaManagedAgentsSessionErrorEvent:
  			fmt.Printf("\n[Error: %s]\n", cmp.Or(event.Error.Message, "unknown"))
  			break events
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  ```

  ```java Java
  // Abre primero el stream y luego envía el mensaje del usuario
  try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserMessageEventParams.builder()
                  .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
                  .addTextContent("Summarize the repo README")
                  .build())
              .build()
      );

      Iterable<BetaManagedAgentsStreamSessionEvents> events = stream.stream()::iterator;
      events:
      for (var event : events) {
          switch (event.type().value()) {
              case AGENT_MESSAGE -> event.asAgentMessage().content().forEach(block -> block.text().ifPresent(textBlock -> IO.print(textBlock.text())));
              case SESSION_STATUS_IDLE -> {
                  break events;
              }
              case SESSION_ERROR -> {
                  // El campo `message` abarca todas las variantes de error; léelo del JSON sin procesar.
                  var errorMessage =
                      event.asSessionError().error()._json().orElse(null) instanceof JsonObject json
                          ? json.values().get("message").asStringOrThrow()
                          : "unknown";
                  IO.println("\n[Error: " + errorMessage + "]");
                  break events;
              }
          }
      }
  }
  ```

  ```php PHP
  use Anthropic\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsSessionErrorEvent;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock;

  // Abre primero el stream y luego envía el mensaje del usuario
  $stream = $client->beta->sessions->events->streamStream($session->id);
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          [
              'type' => 'user.message',
              'content' => [['type' => 'text', 'text' => 'Summarize the repo README']],
          ],
      ],
  );

  foreach ($stream as $event) {
      match (true) {
          $event instanceof ManagedAgentsAgentMessageEvent => array_walk(
              $event->content,
              static fn ($block) => $block instanceof ManagedAgentsTextBlock ? print($block->text) : null,
          ),
          $event instanceof ManagedAgentsSessionErrorEvent => printf("\n[Error: %s]", $event->error?->message ?? 'unknown'),
          default => null,
      };
      if ($event->type === 'session.status_idle' || $event->type === 'session.error') {
          break;
      }
  }
  $stream->close();
  ```

  ```ruby Ruby
  # Abre primero el stream y luego envía el mensaje del usuario
  stream = client.beta.sessions.events.stream_events(session.id)

  client.beta.sessions.events.send_(
    session.id,
    events: [{
      type: "user.message",
      content: [{type: "text", text: "Summarize the repo README"}]
    }]
  )

  stream.each do |event|
    case event
    when Anthropic::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
      event.content.each { print it.text }
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      break
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionErrorEvent
      puts "\n[Error: #{event.error&.message || "unknown"}]"
      break
    else
      # ignora otros tipos de eventos
    end
  end
  ```
</CodeGroup>

La sesión informa de los errores mediante el evento `session.error`. Consulta [Tipos de eventos](https://platform.claude.com/docs/es/managed-agents/reference#event-types) para ver sus campos.

El texto de respuesta del agente llega como eventos `agent.message` almacenados en búfer, cada uno emitido solo después de que finaliza la solicitud al modelo que lo produjo. Para mostrar el texto mientras el modelo aún lo está generando, consulta [Previsualizar respuestas con deltas de eventos](https://platform.claude.com/docs/es/managed-agents/event-deltas).

### Reconectar sin perder eventos

Para reconectarte a una sesión existente sin perder eventos, combina un nuevo flujo con el historial de eventos:

1. Abre un nuevo flujo.
2. [Lista el historial completo de eventos](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#list-past-events) para inicializar un conjunto de IDs de eventos ya vistos.
3. Sigue el flujo en vivo, omitiendo cualquier evento que ya haya devuelto la lista del historial.

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </CodeGroupItem>

  ```python Python
  with client.beta.sessions.events.stream(session.id) as stream:
      # El stream está abierto y almacenando en búfer. Lista el historial antes de seguir en vivo.
      history = client.beta.sessions.events.list(session.id)
      seen_event_ids = {past_event.id for past_event in history}

      # Sigue los eventos en vivo, omitiendo lo que ya se haya visto
      for event in stream:
          if event.type == "event_start" or event.type == "event_delta":
              # Las vistas previas de deltas no están habilitadas en esta conexión.
              continue
          if event.id in seen_event_ids:
              continue
          seen_event_ids.add(event.id)
          match event.type:
              case "agent.message":
                  for block in event.content:
                      if block.type == "text":
                          print(block.text, end="")
              case "session.status_idle":
                  break
  ```

  ```typescript TypeScript
  const seenEventIds = new Set<string>();
  const stream = await client.beta.sessions.events.stream(session.id);

  // El stream está abierto y almacenando en búfer. Lista el historial antes de seguir en vivo.
  for await (const event of client.beta.sessions.events.list(session.id)) {
    seenEventIds.add(event.id);
  }

  // Sigue los eventos en vivo, omitiendo lo ya visto
  tail: for await (const event of stream) {
    // Los eventos de vista previa (event_start/event_delta) no llevan id de nivel superior
    if (event.type === "event_start" || event.type === "event_delta") {
      continue;
    }
    if (seenEventIds.has(event.id)) {
      continue;
    }
    seenEventIds.add(event.id);
    switch (event.type) {
      case "agent.message":
        for (const block of event.content) {
          if (block.type === "text") {
            process.stdout.write(block.text);
          }
        }
        break;
      case "session.status_idle":
        break tail;
    }
  }
  ```

  ```csharp C#
  using var stream = await client.Beta.Sessions.Events.WithRawResponse.StreamStreaming(session.ID);

  // El stream está abierto y almacenando en búfer. Lista el historial antes de seguir en vivo.
  HashSet<string> seenEventIds = [];
  var history = await client.Beta.Sessions.Events.List(session.ID);
  await foreach (var pastEvent in history.Paginate())
  {
      seenEventIds.Add(pastEvent.ID);
  }

  // Sigue los eventos en vivo, omitiendo los ya vistos
  await foreach (var streamEvent in stream.Enumerate())
  {
      if (!seenEventIds.Add(streamEvent.ID))
      {
          continue;
      }
      if (streamEvent.Value is BetaManagedAgentsAgentMessageEvent message)
      {
          foreach (var block in message.Content)
          {
              if (block.Value is BetaManagedAgentsTextBlock textBlock)
              {
                  Console.Write(textBlock.Text);
              }
          }
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent)
      {
          break;
      }
  }
  ```

  ```go Go
  	stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
  	defer stream.Close()

  	// El stream está abierto y almacenando en búfer. Lista el historial antes de seguir en vivo.
  	seenEventIDs := map[string]struct{}{}
  	history := client.Beta.Sessions.Events.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionEventListParams{})
  	for history.Next() {
  		seenEventIDs[history.Current().ID] = struct{}{}
  	}
  	if err := history.Err(); err != nil {
  		panic(err)
  	}

  	// Sigue los eventos en vivo, omitiendo lo que ya se haya visto
  tail:
  	for stream.Next() {
  		event := stream.Current()
  		if _, seen := seenEventIDs[event.ID]; seen {
  			continue
  		}
  		seenEventIDs[event.ID] = struct{}{}
  		switch event := event.AsAny().(type) {
  		case anthropic.BetaManagedAgentsAgentMessageEvent:
  			// lista de tipo concreto: BetaManagedAgentsTextBlock
  			for _, block := range event.Content {
  				fmt.Print(block.Text)
  			}
  		case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
  			break tail
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  ```

  ```java Java
  try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
      // El stream está abierto y almacenando en búfer. Lista el historial antes de seguir en vivo.
      // Cada variante de evento lleva `id`; léelo del JSON sin procesar para deduplicar entre variantes.
      var seenEventIds = new HashSet<String>();
      for (var pastEvent : client.beta().sessions().events().list(session.id()).autoPager()) {
          if (pastEvent._json().orElseThrow() instanceof JsonObject json) {
              seenEventIds.add(json.values().get("id").asStringOrThrow());
          }
      }

      // Sigue los eventos en vivo; Set.add devuelve false para IDs ya vistos, omitiendo la repetición.
      stream.stream()
          .filter(event -> event._json().orElseThrow() instanceof JsonObject json
              && seenEventIds.add(json.values().get("id").asStringOrThrow()))
          .takeWhile(event -> !event.isSessionStatusIdle())
          .filter(BetaManagedAgentsStreamSessionEvents::isAgentMessage)
          .forEach(event -> event.asAgentMessage().content()
              .forEach(block -> block.text().ifPresent(textBlock -> IO.print(textBlock.text()))));
  }
  ```

  ```php PHP
  use Anthropic\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock;

  $stream = $client->beta->sessions->events->streamStream($session->id);

  // El stream está abierto y almacenando en búfer. Lista el historial antes de seguir los eventos en vivo.
  $seenEventIds = [];
  foreach ($client->beta->sessions->events->list($session->id)->pagingEachItem() as $event) {
      $seenEventIds[$event->id] = true;
  }

  // Sigue los eventos en vivo, omitiendo los que ya se vieron
  foreach ($stream as $event) {
      if (isset($seenEventIds[$event->id])) {
          continue;
      }
      $seenEventIds[$event->id] = true;
      match (true) {
          $event instanceof ManagedAgentsAgentMessageEvent => array_walk(
              $event->content,
              static fn ($block) => $block instanceof ManagedAgentsTextBlock ? print($block->text) : null,
          ),
          default => null,
      };
      if ($event->type === 'session.status_idle') {
          break;
      }
  }
  $stream->close();
  ```

  ```ruby Ruby
  stream = client.beta.sessions.events.stream_events(session.id)

  # El stream está abierto y almacenando en búfer. Lista el historial antes de seguir en vivo.
  seen_event_ids = Set.new
  client.beta.sessions.events.list(session.id).auto_paging_each { seen_event_ids << it.id }

  # Sigue los eventos en vivo, omitiendo lo ya visto — Set#add? devuelve nil para duplicados
  stream.each do |event|
    next unless seen_event_ids.add?(event.id)
    case event
    when Anthropic::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
      event.content.each { print it.text }
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      break
    else
      # ignora otros tipos de eventos
    end
  end
  ```
</CodeGroup>

## Enviar eventos

Envía un evento `user.message` para iniciar o continuar el trabajo del agente:

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{
      "events": [
        {
          "type": "user.message",
          "content": [
            {"type": "text", "text": "Analyze the performance of the sort function in utils.py"}
          ]
        }
      ]
    }'
  ```

  ```bash CLI
  ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
  events:
    - type: user.message
      content:
        - type: text
          text: Analyze the performance of the sort function in utils.py
  YAML
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[
          {
              "type": "user.message",
              "content": [
                  {
                      "type": "text",
                      "text": "Analyze the performance of the sort function in utils.py",
                  },
              ],
          },
      ],
  )
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [
      {
        type: "user.message",
        content: [
          {
            type: "text",
            text: "Analyze the performance of the sort function in utils.py"
          }
        ]
      }
    ]
  });
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = BetaManagedAgentsUserMessageEventParamsType.UserMessage,
              Content =
              [
                  new BetaManagedAgentsTextBlock
                  {
                      Type = BetaManagedAgentsTextBlockType.Text,
                      Text = "Analyze the performance of the sort function in utils.py",
                  },
              ],
          },
      ],
  });
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
  			Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  			Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  				OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  					Type: anthropic.BetaManagedAgentsTextBlockTypeText,
  					Text: "Analyze the performance of the sort function in utils.py",
  				},
  			}},
  		},
  	}},
  }); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserMessageEventParams.builder()
              .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
              .addTextContent("Analyze the performance of the sort function in utils.py")
              .build())
          .build());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          [
              'type' => 'user.message',
              'content' => [
                  [
                      'type' => 'text',
                      'text' => 'Analyze the performance of the sort function in utils.py',
                  ],
              ],
          ],
      ],
  );
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [
      {
        type: "user.message",
        content: [
          {
            type: "text",
            text: "Analyze the performance of the sort function in utils.py"
          }
        ]
      }
    ]
  )
  ```
</CodeGroup>

Cada evento del historial de la sesión incluye una marca de tiempo `processed_at` (java, php: `processedAt`; csharp, go: `ProcessedAt`), que se establece cuando el evento termina de procesarse. En los eventos que envías, `processed_at` (java, php: `processedAt`; csharp, go: `ProcessedAt`) es `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `time.Time{}`) mientras el evento sigue en cola detrás de eventos anteriores. Las excepciones son `user.define_outcome`, `user.custom_tool_result` y `user.tool_result`, que se procesan al recibirse y se devuelven con `processed_at` (java, php: `processedAt`; csharp, go: `ProcessedAt`) ya completado.

## Responder cuando la sesión queda inactiva

Un evento `session.status_idle` significa que el agente se ha detenido y está esperando una entrada. Su `stop_reason.type` indica el motivo:

| `stop_reason.type` | Por qué se detuvo la sesión                                                                                                                       | Qué hacer                                                                                                                                                             |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `end_turn`         | El agente terminó su turno, o [lo interrumpiste](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#interrupt-the-agent).    | [Envía un `user.message`](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#resume-an-idle-session) cuando tengas más trabajo para el agente.   |
| `requires_action`  | Una o más llamadas a herramientas necesitan una respuesta tuya, como una llamada a una herramienta personalizada o una solicitud de confirmación. | [Responde a cada llamada a herramienta bloqueante](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#answer-tool-calls-that-pause-the-session). |
| `budget_reached`   | El costo de lista registrado de la sesión alcanzó su [presupuesto](https://platform.claude.com/docs/es/managed-agents/budgets).                   | [Cambia o elimina el presupuesto](https://platform.claude.com/docs/es/managed-agents/budgets#resume-a-session-at-its-budget).                                         |

Ningún evento que envíes reanuda una sesión pausada por su presupuesto. El trabajo que el presupuesto pausó se reanuda automáticamente cuando cambias el presupuesto a un valor superior al costo de lista consumido, o lo eliminas. Cuando el trabajo se reanuda, la sesión emite un evento `workflow_run.status_running` por cada [ejecución de flujo de trabajo que el presupuesto pausó](https://platform.claude.com/docs/es/managed-agents/workflow-runs#budgets-and-limits). Una ejecución que pausó una interrupción permanece pausada; consulta [Reanudar una sesión en su presupuesto](https://platform.claude.com/docs/es/managed-agents/budgets#resume-a-session-at-its-budget). Consulta [Cuando una sesión alcanza su presupuesto](https://platform.claude.com/docs/es/managed-agents/budgets#when-a-session-reaches-its-budget) para ver los eventos que marcan la pausa y los eventos que la sesión sigue aceptando.

## Responder a llamadas a herramientas que pausan la sesión

Una sesión se pausa cuando el agente invoca una [herramienta personalizada](https://platform.claude.com/docs/es/managed-agents/tools#custom-tools), y cuando una llamada a herramienta necesita tu confirmación según una [política de permisos](https://platform.claude.com/docs/es/managed-agents/permission-policies). Ambas pausas siguen la misma secuencia:

1. La sesión emite la llamada a herramienta como un evento `agent.custom_tool_use`, `agent.tool_use` o `agent.mcp_tool_use`.
2. La sesión se pausa con un evento `session.status_idle` cuyo `stop_reason.type` es `requires_action`. Los IDs de los eventos bloqueantes están en el arreglo `stop_reason.event_ids`.
3. Para cada ID de evento bloqueante, envía un evento [`user.custom_tool_result`](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#return-a-custom-tool-result) o [`user.tool_confirmation`](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#confirm-a-tool-call). Un resultado de herramienta personalizada no tiene que esperar al paso 2.
4. Una vez resueltos todos los eventos bloqueantes, la sesión vuelve a `running`.

En una sesión multiagente, los eventos bloqueantes de un subagente se publican también en el hilo principal. Consulta [Permisos de herramientas y herramientas personalizadas](https://platform.claude.com/docs/es/managed-agents/session-threads#tool-permissions-and-custom-tools).

### Devolver un resultado de herramienta personalizada

El evento `agent.custom_tool_use` contiene el nombre y la entrada de la herramienta. Ejecuta la herramienta en tu sistema. Luego envía un evento `user.custom_tool_result`, pasando el ID del evento en el parámetro `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`) junto con el contenido del resultado.

Puedes enviar el resultado en cuanto llegue el evento `agent.custom_tool_use`, sin esperar a `session.status_idle`. La sesión sigue emitiendo `session.status_idle` con un motivo de detención `requires_action` para la llamada, y tu cliente puede ignorarlo. Un segundo resultado para la misma llamada se acepta y no tiene efecto.

Cuando te [reconectas](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#reconnect-without-missing-events) a una sesión pausada, `stop_reason.event_ids` en el evento `session.status_idle` más reciente enumera las llamadas que debes responder.

El siguiente ejemplo responde a cada llamada a medida que llega:

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </CodeGroupItem>

  ```python Python
  with client.beta.sessions.events.stream(session.id) as stream:
      for event in stream:
          match event.type:
              case "agent.custom_tool_use":
                  # Ejecuta la herramienta
                  result = call_tool(event.name, event.input)

                  # Envía el resultado de vuelta
                  client.beta.sessions.events.send(
                      session.id,
                      events=[
                          {
                              "type": "user.custom_tool_result",
                              "custom_tool_use_id": event.id,
                              "content": [{"type": "text", "text": result}],
                          },
                      ],
                  )
              case "session.status_idle":
                  if event.stop_reason and event.stop_reason.type == "end_turn":
                      break
  ```

  ```typescript TypeScript
  const stream = await client.beta.sessions.events.stream(session.id);

  loop: for await (const event of stream) {
    switch (event.type) {
      case "agent.custom_tool_use": {
        // Ejecuta la herramienta
        const result = await callTool(event.name, event.input);

        // Envía el resultado de vuelta
        await client.beta.sessions.events.send(session.id, {
          events: [
            {
              type: "user.custom_tool_result",
              custom_tool_use_id: event.id,
              content: [{ type: "text", text: result }]
            }
          ]
        });
        break;
      }
      case "session.status_idle":
        if (event.stop_reason.type === "end_turn") {
          break loop;
        }
        break;
    }
  }
  ```

  ```csharp C#
  await foreach (var streamEvent in client.Beta.Sessions.Events.StreamStreaming(session.ID))
  {
      if (streamEvent.Value is BetaManagedAgentsAgentCustomToolUseEvent toolUse)
      {
          // Ejecuta la herramienta
          var result = await CallTool(toolUse.Name, toolUse.Input);

          // Envía el resultado de vuelta
          await client.Beta.Sessions.Events.Send(session.ID, new()
          {
              Events =
              [
                  new BetaManagedAgentsUserCustomToolResultEventParams
                  {
                      Type = BetaManagedAgentsUserCustomToolResultEventParamsType.UserCustomToolResult,
                      CustomToolUseID = toolUse.ID,
                      Content =
                      [
                          new BetaManagedAgentsTextBlock
                          {
                              Type = BetaManagedAgentsTextBlockType.Text,
                              Text = result,
                          },
                      ],
                  },
              ],
          });
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle
          && idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn)
      {
          break;
      }
  }
  ```

  ```go Go
  	stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
  	defer stream.Close()

  loop:
  	for stream.Next() {
  		switch event := stream.Current().AsAny().(type) {
  		case anthropic.BetaManagedAgentsAgentCustomToolUseEvent:
  			// Ejecuta la herramienta
  			result := callTool(event.Name, event.Input)
  			// Envía el resultado de vuelta
  			if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  				Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  					OfUserCustomToolResult: &anthropic.BetaManagedAgentsUserCustomToolResultEventParams{
  						Type:            anthropic.BetaManagedAgentsUserCustomToolResultEventParamsTypeUserCustomToolResult,
  						CustomToolUseID: event.ID,
  						Content: []anthropic.BetaManagedAgentsUserCustomToolResultEventParamsContentUnion{{
  							OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  								Type: anthropic.BetaManagedAgentsTextBlockTypeText,
  								Text: result,
  							},
  						}},
  					},
  				}},
  			}); err != nil {
  				panic(err)
  			}
  		case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
  			if _, ok := event.StopReason.AsAny().(anthropic.BetaManagedAgentsSessionEndTurn); ok {
  				break loop
  			}
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  ```

  ```java Java
  try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
      loop:
      for (var event : (Iterable<BetaManagedAgentsStreamSessionEvents>) stream.stream()::iterator) {
          switch (event.type().value()) {
              case AGENT_CUSTOM_TOOL_USE -> {
                  // Ejecuta la herramienta
                  var toolUse = event.asAgentCustomToolUse();
                  var result = callTool(toolUse.name(), toolUse.input());

                  // Envía el resultado de vuelta
                  client.beta().sessions().events().send(
                      session.id(),
                      EventSendParams.builder()
                          .addEvent(BetaManagedAgentsUserCustomToolResultEventParams.builder()
                              .type(BetaManagedAgentsUserCustomToolResultEventParams.Type.USER_CUSTOM_TOOL_RESULT)
                              .customToolUseId(toolUse.id())
                              .addTextContent(result)
                              .build())
                          .build());
              }
              case SESSION_STATUS_IDLE -> {
                  if (event.asSessionStatusIdle().stopReason().isEndTurn()) {
                      break loop;
                  }
              }
          }
      }
  }
  ```

  ```php PHP
  $stream = $client->beta->sessions->events->streamStream($session->id);

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsAgentCustomToolUseEvent:
              // Ejecuta la herramienta
              $result = callTool($event->name, $event->input);

              // Envía el resultado de vuelta
              $client->beta->sessions->events->send(
                  $session->id,
                  events: [
                      [
                          'type' => 'user.custom_tool_result',
                          'custom_tool_use_id' => $event->id,
                          'content' => [['type' => 'text', 'text' => $result]],
                      ],
                  ],
              );
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent:
              if ($event->stopReason instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionEndTurn) {
                  break 2;
              }
              break;
      }
  }
  ```

  ```ruby Ruby
  client.beta.sessions.events.stream_events(session.id).each do |event|
    case event
    when Anthropic::Beta::Sessions::BetaManagedAgentsAgentCustomToolUseEvent
      # Ejecuta la herramienta
      result = call_tool.call(event.name, event.input)
      # Envía el resultado de vuelta
      client.beta.sessions.events.send_(
        session.id,
        events: [
          {
            type: "user.custom_tool_result",
            custom_tool_use_id: event.id,
            content: [{type: "text", text: result}]
          }
        ]
      )
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      break if event.stop_reason.is_a?(Anthropic::Beta::Sessions::BetaManagedAgentsSessionEndTurn)
    end
  end
  ```
</CodeGroup>

Si el agente tiene [ejecuciones de flujo de trabajo](https://platform.claude.com/docs/es/managed-agents/workflow-runs) abiertas, una llamada a herramienta personalizada puede llegar mientras la sesión permanece en `running`. El evento de inactividad `requires_action` llega solo cuando ninguno de los [hilos de la sesión](https://platform.claude.com/docs/es/managed-agents/session-threads) está trabajando. No lo esperes: responde a cada llamada cuando llegue su evento `agent.custom_tool_use`. El ejemplo de [Seguir una ejecución](https://platform.claude.com/docs/es/managed-agents/workflow-runs#follow-a-run) muestra cómo hacerlo.

### Confirmar una llamada a herramienta

Envía un evento `user.tool_confirmation`, pasando el ID del evento en el parámetro `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`). Establece `result` (csharp, go: `Result`) en `"allow"` o `"deny"`. Consulta [Responder a solicitudes de confirmación](https://platform.claude.com/docs/es/managed-agents/permission-policies#respond-to-confirmation-requests) para saber qué llamadas esperan una confirmación y cómo explicar una denegación.

El siguiente ejemplo aprueba todas las llamadas pendientes:

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </CodeGroupItem>

  ```python Python
  with client.beta.sessions.events.stream(session.id) as stream:
      for event in stream:
          if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
              match stop_reason.type:
                  case "requires_action":
                      for event_id in stop_reason.event_ids:
                          # Aprueba la llamada a herramienta pendiente
                          client.beta.sessions.events.send(
                              session.id,
                              events=[
                                  {
                                      "type": "user.tool_confirmation",
                                      "tool_use_id": event_id,
                                      "result": "allow",
                                  },
                              ],
                          )
                  case "end_turn":
                      break
  ```

  ```typescript TypeScript
  const stream = await client.beta.sessions.events.stream(session.id);

  for await (const event of stream) {
    if (event.type !== "session.status_idle") {
      continue;
    }
    if (event.stop_reason.type === "end_turn") {
      break;
    }
    if (event.stop_reason.type !== "requires_action") {
      continue;
    }

    for (const eventId of event.stop_reason.event_ids) {
      // Aprueba la llamada a herramienta pendiente
      await client.beta.sessions.events.send(session.id, {
        events: [
          {
            type: "user.tool_confirmation",
            tool_use_id: eventId,
            result: "allow"
          }
        ]
      });
    }
  }
  ```

  ```csharp C#
  await foreach (var streamEvent in client.Beta.Sessions.Events.StreamStreaming(session.ID))
  {
      if (streamEvent.Value is not BetaManagedAgentsSessionStatusIdleEvent idle) continue;

      if (idle.StopReason?.Value is BetaManagedAgentsSessionRequiresAction requiresAction)
      {
          foreach (var eventId in requiresAction.EventIds)
          {
              // Aprueba la llamada a herramienta pendiente
              await client.Beta.Sessions.Events.Send(session.ID, new()
              {
                  Events =
                  [
                      new BetaManagedAgentsUserToolConfirmationEventParams
                      {
                          Type = BetaManagedAgentsUserToolConfirmationEventParamsType.UserToolConfirmation,
                          ToolUseID = eventId,
                          Result = BetaManagedAgentsUserToolConfirmationEventParamsResult.Allow,
                      },
                  ],
              });
          }
      }
      else if (idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn)
      {
          break;
      }
  }
  ```

  ```go Go
  	stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
  	defer stream.Close()

  loop:
  	for stream.Next() {
  		event, ok := stream.Current().AsAny().(anthropic.BetaManagedAgentsSessionStatusIdleEvent)
  		if !ok {
  			continue
  		}
  		switch stopReason := event.StopReason.AsAny().(type) {
  		case anthropic.BetaManagedAgentsSessionRequiresAction:
  			for _, eventID := range stopReason.EventIDs {
  				// Aprueba la llamada a herramienta pendiente
  				if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  					Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  						OfUserToolConfirmation: &anthropic.BetaManagedAgentsUserToolConfirmationEventParams{
  							Type:      anthropic.BetaManagedAgentsUserToolConfirmationEventParamsTypeUserToolConfirmation,
  							ToolUseID: eventID,
  							Result:    anthropic.BetaManagedAgentsUserToolConfirmationEventParamsResultAllow,
  						},
  					}},
  				}); err != nil {
  					panic(err)
  				}
  			}
  		case anthropic.BetaManagedAgentsSessionEndTurn:
  			break loop
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  ```

  ```java Java
  try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
      stream.stream()
          .filter(BetaManagedAgentsStreamSessionEvents::isSessionStatusIdle)
          .map(idleEvent -> idleEvent.asSessionStatusIdle().stopReason())
          .takeWhile(stopReason -> !stopReason.isEndTurn())
          .filter(stopReason -> stopReason.isRequiresAction())
          .flatMap(stopReason -> stopReason.asRequiresAction().eventIds().stream())
          // Aprueba cada llamada de herramienta pendiente
          .forEach(toolUseId -> client.beta().sessions().events().send(
              session.id(),
              EventSendParams.builder()
                  .addEvent(BetaManagedAgentsUserToolConfirmationEventParams.builder()
                      .type(BetaManagedAgentsUserToolConfirmationEventParams.Type.USER_TOOL_CONFIRMATION)
                      .toolUseId(toolUseId)
                      .result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW)
                      .build())
                  .build()));
  }
  ```

  ```php PHP
  $stream = $client->beta->sessions->events->streamStream($session->id);

  foreach ($stream as $event) {
      if ($event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent && $event->stopReason) {
          switch (true) {
              case $event->stopReason instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionRequiresAction:
                  foreach ($event->stopReason->eventIDs as $eventId) {
                      // Aprueba la llamada a herramienta pendiente
                      $client->beta->sessions->events->send(
                          $session->id,
                          events: [
                              [
                                  'type' => 'user.tool_confirmation',
                                  'tool_use_id' => $eventId,
                                  'result' => 'allow',
                              ],
                          ],
                      );
                  }
                  break;
              case $event->stopReason instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionEndTurn:
                  break 2;
          }
      }
  }
  ```

  ```ruby Ruby
  client.beta.sessions.events.stream_events(session.id).each do |event|
    case event
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      stop_reason = event.stop_reason
      case stop_reason
      when Anthropic::Beta::Sessions::BetaManagedAgentsSessionRequiresAction
        stop_reason.event_ids.each do |event_id|
          # Aprueba la llamada a herramienta pendiente
          client.beta.sessions.events.send_(
            session.id,
            events: [
              {type: "user.tool_confirmation", tool_use_id: event_id, result: "allow"}
            ]
          )
        end
      when Anthropic::Beta::Sessions::BetaManagedAgentsSessionEndTurn
        break
      end
    end
  end
  ```
</CodeGroup>

El ejemplo anterior aprueba cada llamada después de que la sesión queda inactiva. Si el agente tiene [ejecuciones de flujo de trabajo](https://platform.claude.com/docs/es/managed-agents/workflow-runs) abiertas, una llamada a herramienta puede esperar tu confirmación mientras la sesión permanece en `running`. No esperes al evento de inactividad: cuando llegue un evento `agent.tool_use` o `agent.mcp_tool_use` cuyo [`evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`)](https://platform.claude.com/docs/es/managed-agents/permission-policies#see-how-each-call-was-evaluated) sea `ask`, respóndelo. El ejemplo de [Seguir una ejecución](https://platform.claude.com/docs/es/managed-agents/workflow-runs#follow-a-run) responde a las llamadas a herramientas personalizadas de esta manera, y su introducción explica cómo añadir confirmaciones.

## Reanudar una sesión inactiva

Las sesiones persisten entre interacciones. Para reanudar una sesión, envíale un evento `user.message` como de costumbre:

<CodeGroup>
  ```bash cURL
  # En producción, pasa el ID almacenado de la sesión que quieres reanudar.
  curl --fail-with-body -sS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{
      "events": [
        {
          "type": "user.message",
          "content": [
            {"type": "text", "text": "Now run the tests against the changes you made earlier."}
          ]
        }
      ]
    }'
  ```

  ```bash CLI
  # En producción, pasa el ID almacenado de la sesión que quieres reanudar.
  ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
  events:
    - type: user.message
      content:
        - type: text
          text: Now run the tests against the changes you made earlier.
  YAML
  ```

  ```python Python
  # Reanuda una sesión creada previamente enviándole un nuevo evento user.message.
  # En producción, pasa el ID almacenado de la sesión que quieres reanudar.
  client.beta.sessions.events.send(
      session.id,
      events=[
          {
              "type": "user.message",
              "content": [
                  {
                      "type": "text",
                      "text": "Now run the tests against the changes you made earlier.",
                  },
              ],
          },
      ],
  )
  ```

  ```typescript TypeScript
  // Reanuda una sesión creada previamente enviándole un nuevo evento de usuario.
  // En producción, pasa el ID almacenado de la sesión que quieres reanudar.
  await client.beta.sessions.events.send(session.id, {
    events: [
      {
        type: "user.message",
        content: [
          {
            type: "text",
            text: "Now run the tests against the changes you made earlier."
          }
        ]
      }
    ]
  });
  ```

  ```csharp C#
  // Reanuda una sesión creada previamente por ID. En producción, pasa el
  // ID de sesión que guardaste cuando se creó la sesión.
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = BetaManagedAgentsUserMessageEventParamsType.UserMessage,
              Content =
              [
                  new BetaManagedAgentsTextBlock
                  {
                      Type = BetaManagedAgentsTextBlockType.Text,
                      Text = "Now run the tests against the changes you made earlier.",
                  },
              ],
          },
      ],
  });
  ```

  ```go Go
  // Reanuda una sesión creada previamente enviándole un nuevo evento user.message.
  // En producción, pasa el ID almacenado de la sesión a reanudar.
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
  			Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  			Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  				OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  					Type: anthropic.BetaManagedAgentsTextBlockTypeText,
  					Text: "Now run the tests against the changes you made earlier.",
  				},
  			}},
  		},
  	}},
  }); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  // Reanuda una sesión creada previamente por ID. En producción, pasa el
  // ID de sesión que guardaste cuando se creó la sesión.
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserMessageEventParams.builder()
              .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
              .addTextContent("Now run the tests against the changes you made earlier.")
              .build())
          .build());
  ```

  ```php PHP
  // Reanuda una sesión creada previamente enviándole un nuevo evento user.message.
  // En producción, pasa el ID de sesión que guardaste al crear la sesión.
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          [
              'type' => 'user.message',
              'content' => [
                  [
                      'type' => 'text',
                      'text' => 'Now run the tests against the changes you made earlier.',
                  ],
              ],
          ],
      ],
  );
  ```

  ```ruby Ruby
  # Reanudar una sesión es simplemente enviarle el siguiente evento. En producción,
  # pasa el ID de sesión que guardaste cuando se creó la sesión.
  client.beta.sessions.events.send_(
    session.id,
    events: [
      {
        type: "user.message",
        content: [
          {type: "text", text: "Now run the tests against the changes you made earlier."}
        ]
      }
    ]
  )
  ```
</CodeGroup>

El historial de la conversación se conserva a menos que la sesión se elimine explícitamente. Cuando una sesión queda inactiva, se crea un punto de control de su sandbox. El punto de control conserva el estado completo del sandbox, incluido el sistema de archivos, los paquetes instalados y cualquier archivo que haya creado el agente.

<Note>
  El estado del sandbox se conserva solo durante 30 días después de crear el sandbox, y la actividad no amplía este plazo. Después de 30 días, el estado del sandbox (archivos, herramientas instaladas, etc.) es irrecuperable, y una sesión reanudada comienza desde un sandbox nuevo. Si tu flujo de trabajo depende del contenido del sandbox, haz que el agente escriba los artefactos importantes en [salidas](https://platform.claude.com/docs/es/managed-agents/define-outcomes#retrieving-deliverables) antes de que termine el plazo.
</Note>

## Interrumpir al agente

Envía un evento `user.interrupt` para detener al agente a mitad de la ejecución y, a continuación, envía un evento `user.message` para redirigirlo:

<CodeGroup>
  ```bash cURL
  # El agente está analizando un archivo actualmente...
  # Interrumpe con una nueva dirección:
  curl --fail-with-body -sS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{
      "events": [
        {"type": "user.interrupt"},
        {
          "type": "user.message",
          "content": [
            {"type": "text", "text": "Instead, focus on fixing the bug in line 42."}
          ]
        }
      ]
    }'
  ```

  ```bash CLI
  # El agente está analizando un archivo...
  # Interrumpe con una nueva dirección:
  ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
  events:
    - type: user.interrupt
    - type: user.message
      content:
        - type: text
          text: Instead, focus on fixing the bug in line 42.
  YAML
  ```

  ```python Python
  # El agente está analizando un archivo...
  # Interrumpe con una nueva dirección:
  client.beta.sessions.events.send(
      session.id,
      events=[
          {"type": "user.interrupt"},
          {
              "type": "user.message",
              "content": [
                  {
                      "type": "text",
                      "text": "Instead, focus on fixing the bug in line 42.",
                  },
              ],
          },
      ],
  )
  ```

  ```typescript TypeScript
  // El agente está analizando un archivo...
  // Interrumpe con una nueva dirección:
  await client.beta.sessions.events.send(session.id, {
    events: [
      { type: "user.interrupt" },
      {
        type: "user.message",
        content: [
          {
            type: "text",
            text: "Instead, focus on fixing the bug in line 42."
          }
        ]
      }
    ]
  });
  ```

  ```csharp C#
  // El agente está analizando un archivo...
  // Interrumpe con una nueva dirección:
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
          },
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = BetaManagedAgentsUserMessageEventParamsType.UserMessage,
              Content =
              [
                  new BetaManagedAgentsTextBlock
                  {
                      Type = BetaManagedAgentsTextBlockType.Text,
                      Text = "Instead, focus on fixing the bug in line 42.",
                  },
              ],
          },
      ],
  });
  ```

  ```go Go
  // El agente está analizando un archivo...
  // Interrumpe con una nueva dirección:
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{
  		{
  			OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
  				Type: anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			},
  		},
  		{
  			OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
  				Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  				Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  					OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  						Type: anthropic.BetaManagedAgentsTextBlockTypeText,
  						Text: "Instead, focus on fixing the bug in line 42.",
  					},
  				}},
  			},
  		},
  	},
  }); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  // El agente está analizando un archivo actualmente...
  // Interrumpe con una nueva dirección:
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .build())
          .addEvent(BetaManagedAgentsUserMessageEventParams.builder()
              .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
              .addTextContent("Instead, focus on fixing the bug in line 42.")
              .build())
          .build());
  ```

  ```php PHP
  // El agente está analizando un archivo...
  // Interrumpe con una nueva indicación:
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          ['type' => 'user.interrupt'],
          [
              'type' => 'user.message',
              'content' => [
                  [
                      'type' => 'text',
                      'text' => 'Instead, focus on fixing the bug in line 42.',
                  ],
              ],
          ],
      ],
  );
  ```

  ```ruby Ruby
  # El agente está analizando un archivo...
  # Interrumpe con una nueva dirección:
  client.beta.sessions.events.send_(
    session.id,
    events: [
      {type: "user.interrupt"},
      {
        type: "user.message",
        content: [
          {type: "text", text: "Instead, focus on fixing the bug in line 42."}
        ]
      }
    ]
  )
  ```
</CodeGroup>

La llamada regresa en cuanto los eventos quedan en cola. Luego, la interrupción surte efecto en este orden:

1. El agente aplica la interrupción. Hasta entonces, el `processed_at` (java, php: `processedAt`; csharp, go: `ProcessedAt`) de la interrupción permanece en `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `time.Time{}`) y la sesión permanece en `running`. Una respuesta del modelo en curso se detiene de inmediato, pero la interrupción puede tardar más en aplicarse mientras se ejecutan llamadas a herramientas.
2. El evento `user.interrupt` aparece en el flujo, y el turno interrumpido termina con un evento `session.status_idle`.
3. El agente comienza su siguiente turno con el `user.message` que enviaste después de la interrupción.

El `stop_reason.type` del evento de inactividad es `end_turn`, el mismo valor que el de un turno que termina por sí solo. Si hay ejecuciones de flujo de trabajo abiertas, la interrupción no finaliza ninguna de ellas. Una ejecución en curso puede mantener la sesión en `running`, por lo que es posible que el evento `session.status_idle` del paso 2 no llegue. Consulta [Interrumpir una sesión con ejecuciones abiertas](https://platform.claude.com/docs/es/managed-agents/workflow-runs#interrupt-a-session-with-runs-open).

## Listar eventos anteriores

Recupera el historial completo de eventos de una sesión:

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json"
  ```

  ```bash CLI
  ant beta:sessions:events list --session-id "$SESSION_ID" --format jsonl
  ```

  ```python Python
  events = client.beta.sessions.events.list(session.id)
  for event in events.data:
      print(f"[{event.type}] {event.processed_at}")
  ```

  ```typescript TypeScript
  const events = await client.beta.sessions.events.list(session.id);
  for (const event of events.data) {
    console.log(`[${event.type}] ${event.processed_at}`);
  }
  ```

  ```csharp C#
  var events = await client.Beta.Sessions.Events.List(session.ID);
  foreach (var sessionEvent in events.Items)
  {
      Console.WriteLine($"[{sessionEvent.Json.GetProperty("type").GetString()}] {sessionEvent.ProcessedAt}");
  }
  ```

  ```go Go
  events, err := client.Beta.Sessions.Events.List(ctx, session.ID, anthropic.BetaSessionEventListParams{})
  if err != nil {
  	panic(err)
  }
  for _, event := range events.Data {
  	fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
  }
  ```

  ```java Java
  var events = client.beta().sessions().events().list(session.id());
  for (var event : events.data()) {
      var eventJson = event._json().orElseThrow().convert(JsonNode.class);
      var processedAt = eventJson.path("processed_at");
      IO.println("[" + eventJson.get("type").asText() + "] "
          + (processedAt.isTextual() ? processedAt.asText() : "null"));
  }
  ```

  ```php PHP
  $events = $client->beta->sessions->events->list($session->id);
  foreach ($events->data as $event) {
      $processedAt = ($event->processedAt ?? null)?->format(DATE_RFC3339) ?? 'null';
      echo "[{$event->type}] {$processedAt}\n";
  }
  ```

  ```ruby Ruby
  events = client.beta.sessions.events.list(session.id)
  events.data.each { puts "[#{it.type}] #{it.processed_at}" }
  ```
</CodeGroup>

Pasa un filtro `types` (curl: `types[]`; cli: `--type`; csharp, go: `Types`) para devolver solo tipos de eventos específicos:

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true&types[]=agent.tool_use&types[]=agent.tool_result" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01"
  ```

  ```bash CLI
  ant beta:sessions:events list --session-id "$SESSION_ID" \
    --type agent.tool_use --type agent.tool_result \
    --format jsonl
  ```

  ```python Python
  events = client.beta.sessions.events.list(
      session.id,
      types=["agent.tool_use", "agent.tool_result"],
  )
  for event in events.data:
      print(f"[{event.type}] {event.processed_at}")
  ```

  ```typescript TypeScript
  const events = await client.beta.sessions.events.list(session.id, {
    types: ["agent.tool_use", "agent.tool_result"]
  });
  for (const event of events.data) {
    console.log(`[${event.type}] ${event.processed_at}`);
  }
  ```

  ```csharp C#
  var events = await client.Beta.Sessions.Events.List(session.ID, new()
  {
      Types = ["agent.tool_use", "agent.tool_result"],
  });
  foreach (var sessionEvent in events.Items)
  {
      Console.WriteLine($"[{sessionEvent.Json.GetProperty("type").GetString()}] {sessionEvent.ProcessedAt}");
  }
  ```

  ```go Go
  events, err := client.Beta.Sessions.Events.List(ctx, session.ID, anthropic.BetaSessionEventListParams{
  	Types: []anthropic.BetaManagedAgentsSessionEventType{
  		anthropic.BetaManagedAgentsSessionEventTypeAgentToolUse,
  		anthropic.BetaManagedAgentsSessionEventTypeAgentToolResult,
  	},
  })
  if err != nil {
  	panic(err)
  }
  for _, event := range events.Data {
  	fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
  }
  ```

  ```java Java
  var events = client.beta().sessions().events().list(
      session.id(),
      EventListParams.builder()
          .addType(BetaManagedAgentsSessionEventType.AGENT_TOOL_USE)
          .addType(BetaManagedAgentsSessionEventType.AGENT_TOOL_RESULT)
          .build());
  for (var event : events.data()) {
      event.agentToolUse().ifPresent(toolUse ->
          IO.println("[" + toolUse.type() + "] " + toolUse.processedAt()));
      event.agentToolResult().ifPresent(toolResult ->
          IO.println("[" + toolResult.type() + "] " + toolResult.processedAt()));
  }
  ```

  ```php PHP
  $events = $client->beta->sessions->events->list(
      $session->id,
      types: ['agent.tool_use', 'agent.tool_result'],
  );
  foreach ($events->data as $event) {
      $processedAt = ($event->processedAt ?? null)?->format(DATE_RFC3339) ?? 'null';
      echo "[{$event->type}] {$processedAt}\n";
  }
  ```

  ```ruby Ruby
  events = client.beta.sessions.events.list(
    session.id,
    types: ["agent.tool_use", "agent.tool_result"]
  )
  events.data.each { puts "[#{it.type}] #{it.processed_at}" }
  ```
</CodeGroup>

## Enviar mensajes del sistema

En un [modelo compatible](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#supported-models), envía un evento `system.message` para dar al agente contexto privilegiado a nivel de sistema. El contexto se aplica al turno que lo acompaña y a todos los turnos posteriores. Úsalo cuando el agente necesite orientación actualizada a nivel de sistema a mitad de la sesión, por ejemplo, una persona diferente, restricciones revisadas o contexto obtenido en tiempo de ejecución.

El `content` (csharp, go: `Content`) del evento acepta de 1 a 1000 elementos de texto. El contenido se añade al contexto del sistema de la sesión como un turno `role: "system"`. No reemplaza la "system prompt" (indicación del sistema) de nivel superior, que establece el campo `system` (csharp, go: `System`; ruby: `system_`) en la definición del agente.

<CodeGroup>
  ```bash cURL
  curl --fail-with-body -sS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{
      "events": [
        {
          "type": "system.message",
          "content": [
            {"type": "text", "text": "The current timezone of the user is America/New_York."}
          ]
        }
      ]
    }'
  ```

  ```bash CLI
  ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
  events:
    - type: system.message
      content:
        - type: text
          text: "The user's current timezone is America/New_York."
  YAML
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[
          {
              "type": "system.message",
              "content": [
                  {
                      "type": "text",
                      "text": "The user's current timezone is America/New_York.",
                  },
              ],
          },
      ],
  )
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [
      {
        type: "system.message",
        content: [
          {
            type: "text",
            text: "The user's current timezone is America/New_York."
          }
        ]
      }
    ]
  });
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsSystemMessageEventParams
          {
              Type = BetaManagedAgentsSystemMessageEventParamsType.SystemMessage,
              Content =
              [
                  new BetaManagedAgentsSystemContentBlock
                  {
                      Type = BetaManagedAgentsSystemContentBlockType.Text,
                      Text = "The user's current timezone is America/New_York.",
                  },
              ],
          },
      ],
  });
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfSystemMessage: &anthropic.BetaManagedAgentsSystemMessageEventParams{
  			Type: anthropic.BetaManagedAgentsSystemMessageEventParamsTypeSystemMessage,
  			Content: []anthropic.BetaManagedAgentsSystemContentBlockParam{{
  				Type: anthropic.BetaManagedAgentsSystemContentBlockTypeText,
  				Text: "The user's current timezone is America/New_York.",
  			}},
  		},
  	}},
  }); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsSystemMessageEventParams.builder()
              .type(BetaManagedAgentsSystemMessageEventParams.Type.SYSTEM_MESSAGE)
              .addTextContent("The user's current timezone is America/New_York.")
              .build())
          .build());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          [
              'type' => 'system.message',
              'content' => [
                  [
                      'type' => 'text',
                      'text' => "The user's current timezone is America/New_York.",
                  ],
              ],
          ],
      ],
  );
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [
      {
        type: "system.message",
        content: [
          {type: "text", text: "The user's current timezone is America/New_York."}
        ]
      }
    ]
  )
  ```
</CodeGroup>

### Modelos compatibles

`system.message` es compatible con estos modelos:

* Claude Fable 5.1
* Claude Mythos 5.1
* Claude Fable 5
* Claude Mythos 5
* Claude Opus 5.5
* Claude Opus 5
* Claude Opus 4.8
* Claude Sonnet 5.5
* Claude Haiku 5.5

Si el modelo principal del agente no admite la inyección de mensajes del sistema a mitad de la conversación, el evento se rechaza con un error de validación `model_does_not_support_mid_conversation_system`. Los modelos de los subagentes no se verifican, porque `system.message` llega solo al hilo principal.

### Mensajes del sistema mientras hay una llamada a herramienta pendiente

Mientras la sesión está inactiva con un motivo de detención `requires_action`, un `system.message` debe ir después de un evento de resultado de herramienta en la misma solicitud. Si se envía solo o con un `user.message`, se rechaza hasta que se resuelvan los eventos de herramienta pendientes.

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Previsualizar respuestas con deltas de eventos" icon="text" href="https://platform.claude.com/docs/es/managed-agents/event-deltas">
    Muestra el texto de respuesta del agente mientras el modelo aún lo está generando.
  </Card>

  <Card title="Inspeccionar sesiones y hacer seguimiento del uso" icon="magnifying-glass" href="https://platform.claude.com/docs/es/managed-agents/session-observability">
    Inspecciona una sesión en la Claude Console y consulta su uso de tokens y su costo de lista.
  </Card>

  <Card title="Suscribirse a webhooks" icon="link" href="https://platform.claude.com/docs/es/managed-agents/webhooks">
    Recibe notificaciones cuando ocurran eventos importantes sin necesidad de sondeo.
  </Card>

  <Card title="Tipos de eventos" icon="book" href="https://platform.claude.com/docs/es/managed-agents/reference#event-types">
    Consulta todos los tipos de eventos que una sesión puede enviar o recibir.
  </Card>
</CardGroup>
