---
title: Threads de sessão
url: https://platform.claude.com/docs/pt-BR/managed-agents/session-threads
description: Liste, interrompa e arquive as threads de uma sessão multiagente, leia seus eventos e gerencie permissões de ferramentas entre elas.
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

Em uma [sessão multiagente](https://platform.claude.com/docs/pt-BR/managed-agents/multiagent-orchestration), cada agente trabalha em sua própria **session thread** (thread de sessão). Esta página aborda como listar, interromper e arquivar threads, os eventos que elas enviam e como as permissões de ferramentas funcionam entre elas. Uma [execução de workflow](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs) também cria threads de sessão.

## Thread principal e threads de sessão

O **fluxo de eventos no nível da sessão** (`/v1/sessions/{session_id}/events/stream`) é considerado a **primary thread** (thread principal), contendo uma visão condensada de toda a atividade em todas as threads. Você não vê a atividade completa dos [subagentes](https://platform.claude.com/docs/pt-BR/managed-agents/multiagent-orchestration#delegate-to-subagents), mas vê o início e o fim do trabalho deles, além de eventos bloqueantes, como solicitações de permissão de ferramentas.

As **threads de sessão** são onde você se aprofunda na atividade de um agente específico.

O `status` (csharp, go: `Status`) da sessão é uma agregação de toda a atividade dos agentes; se pelo menos uma thread estiver `running`, o status geral da sessão também será `running`. Uma [execução de workflow](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs) em andamento também pode manter a sessão `running`, mesmo enquanto nenhuma de suas threads estiver trabalhando. Quando nenhuma thread está trabalhando e uma thread aguarda seu cliente, a sessão fica `idle`; consulte [Saiba quando o trabalho está concluído](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs#know-when-the-work-is-done).

Um [orçamento de sessão](https://platform.claude.com/docs/pt-BR/managed-agents/budgets) é um limite único compartilhado entre todas as threads de uma sessão. À medida que o limite é atingido, as threads pausam de forma independente, e o custo de cada thread é calculado com base no próprio modelo servido da thread.

<Note>
  Uma sessão pode ter no máximo 25 threads filhas ao mesmo tempo. Threads ociosas contam até que você as arquive, e a thread principal não conta. O agente da thread principal pode chamar várias cópias de um único subagente, criando várias threads associadas a um único `agent`. As threads de consulta do [advisor](https://platform.claude.com/docs/pt-BR/managed-agents/multiagent-orchestration#give-the-session-an-advisor) estão isentas desse limite. O mesmo vale para as threads de uma [execução de workflow](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs).
</Note>

## Listar threads

Liste todas as threads associadas a uma sessão da seguinte forma:

<CodeGroup>
  ```bash cURL
  curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads" \
    -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:threads list --session-id "$SESSION_ID"
  ```

  ```python Python
  for thread in client.beta.sessions.threads.list(session.id):
      agent = thread.agent
      label = agent.type if agent.type == "advisor" else agent.name
      print(f"[{label}] {thread.status}")
  ```

  ```typescript TypeScript
  for await (const thread of client.beta.sessions.threads.list(session.id)) {
    const label = thread.agent.type === "advisor" ? thread.agent.type : thread.agent.name;
    console.log(`[${label}] ${thread.status}`);
  }
  ```

  ```csharp C#
  await foreach (var sessionThread in (await client.Beta.Sessions.Threads.List(session.ID)).Paginate())
  {
      var label = sessionThread.Agent.Name ?? sessionThread.Agent.Json.GetProperty("type").GetString();
      Console.WriteLine($"[{label}] {sessionThread.Status.Raw()}");
  }
  ```

  ```go Go
  threads := client.Beta.Sessions.Threads.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionThreadListParams{})
  for threads.Next() {
  	thread := threads.Current()
  	label := cmp.Or(thread.Agent.Name, thread.Agent.Type)
  	fmt.Printf("[%s] %s\n", label, thread.Status)
  }
  if err := threads.Err(); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  for (var thread : client.beta().sessions().threads().list(session.id()).autoPager()) {
      var agent = thread.agent();
      var label = agent.isAgent() ? agent.asAgent().name() : agent.type().asString();
      IO.println("[" + label + "] " + thread.status());
  }
  ```

  ```php PHP
  foreach ($client->beta->sessions->threads->list($session->id)->pagingEachItem() as $thread) {
      $label = $thread->agent instanceof \Anthropic\Beta\Agents\BetaManagedAgentsAdvisor
          ? $thread->agent->type
          : $thread->agent->name;
      echo "[{$label}] {$thread->status}\n";
  }
  ```

  ```ruby Ruby
  client.beta.sessions.threads.list(session.id).auto_paging_each do |thread|
    agent = thread.agent
    label = agent.type == :advisor ? agent.type : agent.name
    puts "[#{label}] #{thread.status}"
  end
  ```
</CodeGroup>

A lista completa inclui a thread principal. `parent_thread_id` (java: `parentThreadId`; php: `parentThreadID`; csharp, go: `ParentThreadID`) é `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) para a thread principal. Todas as outras threads são threads filhas. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) é `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`), exceto nas [threads de uma execução](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs#a-runs-threads).

Para listar apenas threads com determinados status, adicione `statuses[]` à solicitação e repita-o para informar mais de um status, como em `?statuses[]=running&statuses[]=idle`. Omita-o para retornar threads de todos os status.

## Interromper uma thread de sessão

Envie `user.interrupt` com `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) para parar uma thread específica. Omitir `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) interrompe todas as threads não arquivadas da sessão, incluindo a principal. Em uma sessão com workflows dinâmicos, uma interrupção não encerra nenhuma execução, e uma que nomeia a thread de uma execução não para nada. Uma interrupção fecha as chamadas de ferramentas pendentes de outras threads filhas, mas não conte com ela para fechar as de uma thread de execução. Consulte [Interromper uma sessão com execuções abertas](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs#interrupt-a-session-with-runs-open).

<CodeGroup>
  ```bash cURL
  curl -fsS "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", "session_thread_id": "'"$THREAD_ID"'"}]}'
  ```

  ```bash CLI
  ant beta:sessions:events send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }]
  });
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
  			Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: anthropic.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          ['type' => 'user.interrupt', 'session_thread_id' => $thread->id],
      ],
  );
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )
  ```
</CodeGroup>

Na thread de um subagente bloqueada em `requires_action`, a interrupção fecha cada chamada de ferramenta pendente com um resultado de ferramenta de erro ("Tool execution was interrupted before completion. Please retry.") e reemite `session.thread_status_idle` com `stop_reason: end_turn` diretamente; o modelo não é amostrado. Em uma thread filha ociosa com `end_turn` ou `budget_reached`, a interrupção não tem efeito. Uma interrupção que nomeia uma thread encerrada retorna um erro 400. Uma thread filha interrompida não envia ao agente da thread principal o relatório que envia quando um turno termina. Enquanto esse agente aguarda a thread filha, ele não inicia outro turno até que algo mais chegue até ele, como uma `user.message` ou o relatório de outra thread.

## Arquivar uma thread de sessão

Opcionalmente, arquive uma thread de sessão quando ela tiver concluído seu trabalho. Arquivar uma thread libera seu lugar dentro do limite de 25 threads filhas. O servidor arquiva as threads de uma [execução de workflow](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs) por conta própria. Você não precisa arquivá-las e não pode fazê-lo enquanto a execução estiver aberta.

<CodeGroup>
  ```bash cURL
  curl -fsS -X POST "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -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:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
  ```

  ```python Python
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
  ```

  ```typescript TypeScript
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id
  });
  console.log(archived.status, archived.archived_at);
  ```

  ```csharp C#
  var archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
  ```

  ```go Go
  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
  ```

  ```java Java
  var archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
  ```

  ```php PHP
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
  ```

  ```ruby Ruby
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"
  ```
</CodeGroup>

O arquivamento só é bem-sucedido se a thread estiver `idle`. Uma thread parada em `requires_action` conta como ociosa e pode ser arquivada diretamente; apenas uma thread em execução precisa ser interrompida primeiro:

<CodeGroup>
  ```bash cURL
  # Interrompe a thread e depois a arquiva
  curl -fsS "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", "session_thread_id": "'"$THREAD_ID"'"}]}'

  curl -fsS -X POST "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -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 send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"

  ant beta:sessions:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }]
  });
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id
  });
  console.log(archived.status, archived.archived_at);
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
  archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
  			Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: anthropic.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }

  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());

  archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [['type' => 'user.interrupt', 'session_thread_id' => $thread->id]],
  );
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"
  ```
</CodeGroup>

## Eventos da thread principal

Estes eventos expõem a atividade multiagente na thread principal em `/v1/sessions/{session_id}/events/stream`. Os eventos de direção de mensagem são nomeados em relação à thread em cujo fluxo aparecem: `agent.thread_message_received` significa que uma mensagem chegou a esta thread vinda de outra thread, e `agent.thread_message_sent` significa que esta thread enviou uma. A tarefa que o agente da thread principal delega, por exemplo, chega ao fluxo da própria thread filha como um evento `agent.thread_message_received`.

| Tipo                               | Descrição                                                                                                                                                                                                                                                                                                                                           |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session.thread_created`           | Uma thread foi criada. Inclui `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) e `agent_name` (java, php: `agentName`; csharp, go: `AgentName`).                                                                                                                                                |
| `session.thread_status_running`    | Uma thread iniciou atividade.                                                                                                                                                                                                                                                                                                                       |
| `session.thread_status_idle`       | O agente associado à thread está aguardando entrada. Inclui um `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`) indicando por que o agente parou.                                                                                                                                                                                  |
| `session.thread_status_terminated` | Uma thread foi encerrada e não aceita mais entradas, por exemplo, porque foi arquivada ou encontrou um erro irrecuperável. Uma thread de advisor também é encerrada quando sua consulta termina.                                                                                                                                                    |
| `agent.thread_message_received`    | Na thread principal, um subagente enviou ao agente da thread principal um relatório ou uma pergunta. Inclui `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`), `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) e `content` (csharp, go: `Content`). |
| `agent.thread_message_sent`        | Na thread principal, o agente da thread principal enviou a um subagente uma tarefa ou mensagem de acompanhamento. Inclui `to_session_thread_id` (java: `toSessionThreadId`; php: `toSessionThreadID`; csharp, go: `ToSessionThreadID`), `to_agent_name` (java, php: `toAgentName`; csharp, go: `ToAgentName`) e `content` (csharp, go: `Content`).  |

As consultas ao advisor emitem esses mesmos eventos de thread sob o nome reservado `anthropic.advisor` (como `agent_name` (java, php: `agentName`; csharp, go: `AgentName`) nos eventos de ciclo de vida da thread e `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) na entrega do conselho); consulte [Dar um advisor à sessão](https://platform.claude.com/docs/pt-BR/managed-agents/multiagent-orchestration#give-the-session-an-advisor) para ver a sequência.

As threads de uma [execução de workflow](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs) aparecem no fluxo principal da seguinte forma:

* **Eventos de ciclo de vida:** Cada thread de execução envia `session.thread_created`, com o `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) da execução, e seus eventos `session.thread_status_running`, `session.thread_status_idle` e `session.thread_status_terminated`.
* **Eventos de mensagem:** O prompt de uma thread de execução, um evento `agent.thread_message_received`, permanece em seu próprio fluxo.
* **Eventos de execução:** Eventos `workflow_run.*` também chegam a este fluxo; consulte [Eventos de execução](https://platform.claude.com/docs/pt-BR/managed-agents/workflow-runs#run-events).
* **Chamadas de ferramentas que aguardam você:** As chamadas de ferramentas de uma thread de execução que precisam do seu cliente são replicadas neste fluxo, como para qualquer thread filha. Consulte [Permissões de ferramentas e ferramentas personalizadas](https://platform.claude.com/docs/pt-BR/managed-agents/session-threads#tool-permissions-and-custom-tools).

## Eventos de thread de sessão

Eventos críticos são encaminhados para a thread principal. No entanto, você ainda pode querer investigar o raciocínio e as chamadas de ferramentas de um agente específico. Para isso, faça streaming ou liste os eventos da thread de sessão associada.

Cada thread de sessão tem seu próprio fluxo de eventos em `/v1/sessions/{session_id}/threads/{thread_id}/stream`, e ele aceita o mesmo parâmetro `event_deltas[]` que o fluxo no nível da sessão, para que você possa pré-visualizar o texto de um subagente à medida que o modelo o gera. Uma conexão pré-visualiza apenas a thread que está lendo: as pré-visualizações de uma thread filha nunca aparecem no fluxo no nível da sessão, então, para acompanhar um subagente ao vivo, abra o fluxo da própria thread dele. Consulte [Pré-visualizar eventos de threads de sessão](https://platform.claude.com/docs/pt-BR/managed-agents/event-deltas#preview-session-thread-events) para saber como ativar, acumular e reconciliar pré-visualizações.

Em uma execução de workflow, o servidor executa um workflow: um programa que o agente da thread principal escreve. Em cada uma das threads da execução, o primeiro `agent.thread_message_received` é o prompt que o workflow escreveu. Seu `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`) é o ID da thread principal, e a API não define seu `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`). A API não garante o texto do prompt, então não o analise. O evento `session.thread_status_terminated` da thread, no fluxo da thread principal, informa que a thread terminou. Nenhum evento registra o resultado que ela retornou ao workflow.

O fluxo de uma thread não reproduz eventos anteriores. Logo após `session.thread_created`, a lista de eventos da thread de uma execução pode estar vazia, porque o servidor grava o primeiro evento da thread depois dele. Portanto, abra primeiro o fluxo da thread, depois liste os eventos da thread e ignore cada evento recebido pelo fluxo cujo `id` (csharp, go: `ID`) a lista retornou.

<Tabs>
  <Tab title="Fazer streaming dos eventos da thread de sessão">
    <CodeGroup>
      ```bash cURL
      curl -fsSN "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream?beta=true" \
        -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:threads:events stream \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python
      with client.beta.sessions.threads.events.stream(
          thread.id,
          session_id=session.id,
      ) as stream:
          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.thread_status_idle":
                      break
      ```

      ```typescript TypeScript
      const stream = await client.beta.sessions.threads.events.stream(thread.id, {
        session_id: session.id
      });

      loop: 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.thread_status_idle":
            break loop;
        }
      }
      ```

      ```csharp C#
      await foreach (var evt in client.Beta.Sessions.Threads.Events.StreamStreaming(thread.ID, new() { SessionID = session.ID }))
      {
          if (evt.Value is BetaManagedAgentsAgentMessageEvent message)
          {
              foreach (var block in message.Content)
              {
                  if (block.Value is BetaManagedAgentsTextBlock textBlock)
                  {
                      Console.Write(textBlock.Text);
                  }
              }
          }
          else if (evt.Value is BetaManagedAgentsSessionThreadStatusIdleEvent)
          {
              break;
          }
      }
      ```

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

      loop:
      	for stream.Next() {
      		event := stream.Current()
      		switch event.Type {
      		case "agent.message":
      			for _, block := range event.AsAgentMessage().Content {
      				if block.Type == "text" {
      					fmt.Print(block.Text)
      				}
      			}
      		case "session.thread_status_idle":
      			break loop
      		}
      	}
      	if err := stream.Err(); err != nil {
      		panic(err)
      	}
      ```

      ```java Java
      try (var streamResponse = client.beta().sessions().threads().events().streamStreaming(
          thread.id(),
          EventStreamParams.builder().sessionId(session.id()).build()
      )) {
          loop:
          for (var event : (Iterable<BetaManagedAgentsStreamSessionThreadEvents>) streamResponse.stream()::iterator) {
              switch (event.type().value()) {
                  case AGENT_MESSAGE -> {
                      for (var block : event.asAgentMessage().content()) {
                          block.text().ifPresent(textBlock -> IO.print(textBlock.text()));
                      }
                  }
                  case SESSION_THREAD_STATUS_IDLE -> {
                      break loop;
                  }
              }
          }
      }
      ```

      ```php PHP
      $stream = $client->beta->sessions->threads->events->streamStream(
          $thread->id,
          sessionID: $session->id,
      );

      foreach ($stream as $event) {
          switch (true) {
              case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent:
                  foreach ($event->content as $block) {
                      if ($block instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock) {
                          echo $block->text;
                      }
                  }
                  break;
              case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionThreadStatusIdleEvent:
                  break 2;
          }
      }
      ```

      ```ruby Ruby
      client.beta.sessions.threads.events.stream_events(thread.id, session_id: session.id).each do |event|
        case event
        when Anthropic::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
          event.content.each do |block|
            print block.text if block.is_a?(Anthropic::Beta::Sessions::BetaManagedAgentsTextBlock)
          end
        when Anthropic::Beta::Sessions::BetaManagedAgentsSessionThreadStatusIdleEvent
          break
        end
      end
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Listar eventos da thread de sessão">
    Liste todos os eventos anteriores da thread de sessão para obter um histórico completo.

    <CodeGroup>
      ```bash cURL
      curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" \
        -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:threads:events list \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

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

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

      ```csharp C#
      var page = await client.Beta.Sessions.Threads.Events.List(thread.ID, new() { SessionID = session.ID });
      await foreach (var evt in page.Paginate())
      {
          Console.WriteLine($"[{evt.Json.GetProperty("type").GetString()}] {evt.ProcessedAt}");
      }
      ```

      ```go Go
      pager := client.Beta.Sessions.Threads.Events.ListAutoPaging(ctx, thread.ID, anthropic.BetaSessionThreadEventListParams{
      	SessionID: session.ID,
      })
      for pager.Next() {
      	event := pager.Current()
      	fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
      }
      if err := pager.Err(); err != nil {
      	panic(err)
      }
      ```

      ```java Java
      for (var event : client.beta().sessions().threads().events().list(
              thread.id(),
              EventListParams.builder().sessionId(session.id()).build()
          ).autoPager()) {
          var type = event._json().orElseThrow() instanceof JsonObject json
              ? json.values().get("type").asStringOrThrow()
              : "unknown";
          var processedAt = event.processedAt().map(OffsetDateTime::toString).orElse("pending");
          IO.println("[" + type + "] " + processedAt);
      }
      ```

      ```php PHP
      foreach (
          $client->beta->sessions->threads->events->list(
              $thread->id,
              sessionID: $session->id,
          )->pagingEachItem() as $event
      ) {
          echo "[{$event->type}] {$event->processedAt->format(DATE_RFC3339)}\n";
      }
      ```

      ```ruby Ruby
      client.beta.sessions.threads.events.list(
        thread.id,
        session_id: session.id
      ).auto_paging_each do |event|
        puts "[#{event.type}] #{event.processed_at}"
      end
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Permissões de ferramentas e ferramentas personalizadas

Se um subagente precisar de algo do seu cliente, como [permissão](https://platform.claude.com/docs/pt-BR/managed-agents/events-and-streaming#tool-confirmation) para executar uma chamada de ferramenta ou o [resultado de uma ferramenta personalizada](https://platform.claude.com/docs/pt-BR/managed-agents/events-and-streaming#handling-custom-tool-calls), o evento é replicado na **thread principal** com `session_thread_id` identificando a thread de sessão de origem. Uma chamada de ferramenta precisa da sua permissão sob `always_ask`, ou sob [`auto`](https://platform.claude.com/docs/pt-BR/managed-agents/permission-policies#let-the-server-evaluate-each-call-with-auto) quando o servidor não chega a uma determinação.

```json
{
  "type": "session.thread_status_idle",
  "id": "sevt_01ABC...",
  "session_thread_id": "sthr_01DEF...",
  "agent_name": "code-reviewer",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["sevt_01XYZ..."]
  }
}
```

Envie `user.tool_confirmation` (com `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`)) ou `user.custom_tool_result` (com `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)); o servidor encaminha a resposta para a thread correta automaticamente. A resposta pode aparecer na thread principal e na thread do subagente com valores de `id` (csharp, go: `ID`) diferentes. Para associar as duas cópias, compare `type` (csharp, go: `Type`) e `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) (ou `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)), e não `id` (csharp, go: `ID`).

A sessão fica `idle` somente quando nenhuma thread está `running`, então `session.status_idle` pode chegar muito depois da chamada de um subagente. Você não precisa esperar por ele: envie o `user.custom_tool_result` assim que o evento `agent.custom_tool_use` replicado chegar.

Sob `auto`, seus eventos `user.message` podem levar o servidor a permitir uma chamada que, de outra forma, ele negaria. Nada na thread de um subagente conta como sua intenção. Seu cliente não envia mensagens ali, e as mensagens que o agente da thread principal envia ao subagente não contam. Quando o servidor nega uma chamada sob `auto`, nada é replicado: o evento e o resultado de ferramenta de erro aparecem apenas no próprio [fluxo da thread](https://platform.claude.com/docs/pt-BR/managed-agents/session-threads#session-thread-events) do subagente, e o subagente continua em execução.

O exemplo a seguir fica dentro do loop de eventos do [manipulador de confirmação de ferramentas](https://platform.claude.com/docs/pt-BR/managed-agents/events-and-streaming#tool-confirmation). Para cada ID em `stop_reason.event_ids`, ele envia um `user.tool_confirmation` que permite a chamada. O mesmo padrão se aplica a `user.custom_tool_result`, que você também pode enviar assim que seu evento `agent.custom_tool_use` chegar.

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    Este workflow precisa de mais lógica do que um único comando de shell. Use o [Claude SDK](https://platform.claude.com/docs/pt-BR/cli-sdks-libraries/overview) para executá-lo.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Este workflow precisa de mais lógica do que um único comando de shell. Use o [Claude SDK](https://platform.claude.com/docs/pt-BR/cli-sdks-libraries/overview) para executá-lo.
  </CodeGroupItem>

  ```python Python
  for event_id in stop.event_ids:
      client.beta.sessions.events.send(
          session.id,
          events=[
              {
                  "type": "user.tool_confirmation",
                  "tool_use_id": event_id,
                  "result": "allow",
              }
          ],
      )
  ```

  ```typescript TypeScript
  for (const eventId of stop.event_ids) {
    await client.beta.sessions.events.send(session.id, {
      events: [
        {
          type: "user.tool_confirmation",
          tool_use_id: eventId,
          result: "allow"
        }
      ]
    });
  }
  ```

  ```csharp C#
  foreach (var eventId in requiresAction.EventIds)
  {
      await client.Beta.Sessions.Events.Send(session.ID, new()
      {
          Events =
          [
              new BetaManagedAgentsUserToolConfirmationEventParams
              {
                  Type = BetaManagedAgentsUserToolConfirmationEventParamsType.UserToolConfirmation,
                  ToolUseID = eventId,
                  Result = BetaManagedAgentsUserToolConfirmationEventParamsResult.Allow,
              },
          ],
      });
  }
  ```

  ```go Go
  for _, eventID := range stopReason.EventIDs {
  	params := anthropic.BetaManagedAgentsUserToolConfirmationEventParams{
  		Type:      anthropic.BetaManagedAgentsUserToolConfirmationEventParamsTypeUserToolConfirmation,
  		ToolUseID: eventID,
  		Result:    anthropic.BetaManagedAgentsUserToolConfirmationEventParamsResultAllow,
  	}
  	if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  		Events: []anthropic.BetaManagedAgentsEventParamsUnion{{OfUserToolConfirmation: &params}},
  	}); err != nil {
  		panic(err)
  	}
  }
  ```

  ```java Java
  for (var eventId : pendingToolUseIds) {
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserToolConfirmationEventParams.builder()
                  .type(BetaManagedAgentsUserToolConfirmationEventParams.Type.USER_TOOL_CONFIRMATION)
                  .toolUseId(eventId)
                  .result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW)
                  .build())
              .build()
      );
  }
  ```

  ```php PHP
  foreach ($event->stopReason->eventIDs as $eventId) {
      $client->beta->sessions->events->send($session->id, events: [[
          'type' => 'user.tool_confirmation',
          'tool_use_id' => $eventId,
          'result' => 'allow',
      ]]);
  }
  ```

  ```ruby Ruby
  event_ids.each do |event_id|
    client.beta.sessions.events.send_(session.id, events: [{
      type: "user.tool_confirmation",
      tool_use_id: event_id,
      result: "allow"
    }])
  end
  ```
</CodeGroup>

O padrão anterior responde às chamadas que um evento de ociosidade lista. No fluxo principal, o evento `session.thread_status_idle` de um subagente pode chegar antes dos eventos `agent.tool_use` ou `agent.mcp_tool_use` que seu `stop_reason.event_ids` lista. Um `user.tool_confirmation` para uma chamada cujo evento ainda não chegou pode retornar 400. Para evitar isso, responda a cada chamada cujo [`evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`)](https://platform.claude.com/docs/pt-BR/managed-agents/permission-policies#see-how-each-call-was-evaluated) seja `ask` quando o próprio evento dela chegar ao fluxo principal.
