---
title: Session-Threads
url: https://platform.claude.com/docs/de/managed-agents/session-threads
description: Liste, unterbrich und archiviere die Threads einer Multiagenten-Session, lies ihre Events und verwalte Tool-Berechtigungen über sie hinweg.
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

In einer [Multiagenten-Session](https://platform.claude.com/docs/de/managed-agents/multiagent-orchestration) arbeitet jeder Agent in seinem eigenen **„session thread" (Session-Thread)**. Diese Seite behandelt, wie du Threads auflistest, unterbrichst und archivierst, welche Events sie senden und wie Tool-Berechtigungen über sie hinweg funktionieren. Ein [Workflow-Run](https://platform.claude.com/docs/de/managed-agents/workflow-runs) erstellt ebenfalls Session-Threads.

## Primärer Thread und Session-Threads

Der **Event-Stream auf Session-Ebene** (`/v1/sessions/{session_id}/events/stream`) gilt als **„primary thread" (primärer Thread)** und enthält eine verdichtete Ansicht aller Aktivitäten über alle Threads hinweg. Du siehst nicht die vollständige Aktivität von [Subagenten](https://platform.claude.com/docs/de/managed-agents/multiagent-orchestration#delegate-to-subagents), aber du siehst den Beginn und das Ende ihrer Arbeit sowie blockierende Events wie Anfragen zu Tool-Berechtigungen.

In **Session-Threads** kannst du die Aktivität eines bestimmten Agenten im Detail untersuchen.

Der `status` (csharp, go: `Status`) der Session ist eine Aggregation aller Agentenaktivitäten; wenn mindestens ein Thread `running` ist, dann ist auch der Gesamtstatus der Session `running`. Ein laufender [Workflow-Run](https://platform.claude.com/docs/de/managed-agents/workflow-runs) kann die Session ebenfalls auf `running` halten, selbst wenn keiner seiner Threads arbeitet. Wenn kein Thread arbeitet und ein Thread auf deinen Client wartet, ist die Session `idle`; siehe [Erkennen, wann die Arbeit erledigt ist](https://platform.claude.com/docs/de/managed-agents/workflow-runs#know-when-the-work-is-done).

Ein [Session-Budget](https://platform.claude.com/docs/de/managed-agents/budgets) ist eine einzige gemeinsame Obergrenze für alle Threads einer Session. Wenn die Obergrenze erreicht wird, pausieren die Threads unabhängig voneinander, und die Kosten jedes Threads werden nach dem Modell berechnet, das diesen Thread bedient.

<Note>
  Eine Session kann höchstens 25 Kind-Threads gleichzeitig haben. Inaktive Threads zählen mit, bis du sie archivierst, und der primäre Thread zählt nicht mit. Der Agent des primären Threads kann mehrere Kopien eines einzelnen Subagenten aufrufen und so mehrere Threads erstellen, die einem `agent` zugeordnet sind. Konsultations-Threads eines [Advisors](https://platform.claude.com/docs/de/managed-agents/multiagent-orchestration#give-the-session-an-advisor) sind von diesem Limit ausgenommen. Ebenso die Threads eines [Workflow-Runs](https://platform.claude.com/docs/de/managed-agents/workflow-runs).
</Note>

## Threads auflisten

Liste alle Threads, die einer Session zugeordnet sind, wie folgt auf:

<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>

Die vollständige Liste enthält den primären Thread. `parent_thread_id` (java: `parentThreadId`; php: `parentThreadID`; csharp, go: `ParentThreadID`) ist für den primären Thread `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`). Jeder andere Thread ist ein Kind-Thread. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) ist `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`), außer bei [den Threads eines Runs](https://platform.claude.com/docs/de/managed-agents/workflow-runs#a-runs-threads).

Um nur Threads mit bestimmten Status aufzulisten, füge der Anfrage `statuses[]` hinzu und wiederhole den Parameter, um mehr als einen Status anzugeben, wie in `?statuses[]=running&statuses[]=idle`. Lass ihn weg, um Threads mit jedem Status zurückzugeben.

## Einen Session-Thread unterbrechen

Sende `user.interrupt` mit `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), um einen bestimmten Thread zu stoppen. Wenn du `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) weglässt, wird jeder nicht archivierte Thread in der Session unterbrochen, einschließlich des primären. In einer Session mit dynamischen Workflows beendet eine Unterbrechung keinen Run, und eine Unterbrechung, die einen Thread eines Runs benennt, stoppt nichts. Eine Unterbrechung schließt ausstehende Tool-Aufrufe anderer Kind-Threads, aber verlass dich nicht darauf, dass sie die eines Run-Threads schließt. Siehe [Eine Session mit offenen Runs unterbrechen](https://platform.claude.com/docs/de/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>

Bei einem auf `requires_action` blockierten Thread eines Subagenten schließt die Unterbrechung jeden ausstehenden Tool-Aufruf mit einem Fehler-Tool-Ergebnis („Tool execution was interrupted before completion. Please retry.") und sendet `session.thread_status_idle` mit `stop_reason: end_turn` direkt erneut; das Modell wird nicht gesampelt. Bei einem Kind-Thread, der mit `end_turn` oder `budget_reached` inaktiv ist, hat die Unterbrechung keine Wirkung. Eine Unterbrechung, die einen beendeten Thread benennt, gibt einen 400-Fehler zurück. Ein unterbrochener Kind-Thread sendet dem Agenten des primären Threads nicht den Bericht, den er sendet, wenn ein Turn endet. Während dieser Agent auf den Kind-Thread wartet, beginnt er keinen weiteren Turn, bis ihn etwas anderes erreicht, etwa eine `user.message` oder der Bericht eines anderen Threads.

## Einen Session-Thread archivieren

Archiviere einen Session-Thread optional, wenn er seine Arbeit abgeschlossen hat. Das Archivieren eines Threads gibt seinen Platz im Limit von 25 Kind-Threads frei. Der Server archiviert die Threads eines [Workflow-Runs](https://platform.claude.com/docs/de/managed-agents/workflow-runs) selbst. Du musst sie nicht archivieren, und du kannst es nicht, solange der Run offen ist.

<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>

Das Archivieren gelingt nur, wenn der Thread `idle` ist. Ein Thread, der auf `requires_action` wartet, gilt als inaktiv und kann direkt archiviert werden; nur ein laufender Thread muss zuerst unterbrochen werden:

<CodeGroup>
  ```bash cURL
  # Unterbrich den Thread und archiviere ihn dann
  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>

## Events des primären Threads

Diese Events machen Multiagenten-Aktivität auf dem primären Thread unter `/v1/sessions/{session_id}/events/stream` sichtbar. Events mit Nachrichtenrichtung sind relativ zu dem Thread benannt, in dessen Stream sie erscheinen: `agent.thread_message_received` bedeutet, dass eine Nachricht von einem anderen Thread auf diesem Thread angekommen ist, und `agent.thread_message_sent` bedeutet, dass dieser Thread eine gesendet hat. Die Aufgabe, die der Agent des primären Threads delegiert, kommt beispielsweise im eigenen Stream des Kind-Threads als `agent.thread_message_received`-Event an.

| Typ                                | Beschreibung                                                                                                                                                                                                                                                                                                                                                        |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session.thread_created`           | Ein Thread wurde erstellt. Enthält `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) und `agent_name` (java, php: `agentName`; csharp, go: `AgentName`).                                                                                                                                                         |
| `session.thread_status_running`    | Ein Thread hat eine Aktivität begonnen.                                                                                                                                                                                                                                                                                                                             |
| `session.thread_status_idle`       | Der dem Thread zugeordnete Agent wartet auf Eingabe. Enthält einen `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`), der angibt, warum der Agent angehalten hat.                                                                                                                                                                                   |
| `session.thread_status_terminated` | Ein Thread wurde beendet und nimmt keine weiteren Eingaben an, zum Beispiel weil er archiviert wurde oder auf einen nicht behebbaren Fehler gestoßen ist. Ein Advisor-Thread wird ebenfalls beendet, wenn seine Konsultation endet.                                                                                                                                 |
| `agent.thread_message_received`    | Auf dem primären Thread hat ein Subagent dem Agenten des primären Threads einen Bericht oder eine Frage gesendet. Enthält `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`), `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) und `content` (csharp, go: `Content`). |
| `agent.thread_message_sent`        | Auf dem primären Thread hat der Agent des primären Threads einem Subagenten eine Aufgabe oder eine Folgenachricht gesendet. Enthält `to_session_thread_id` (java: `toSessionThreadId`; php: `toSessionThreadID`; csharp, go: `ToSessionThreadID`), `to_agent_name` (java, php: `toAgentName`; csharp, go: `ToAgentName`) und `content` (csharp, go: `Content`).     |

Advisor-Konsultationen senden dieselben Thread-Events unter dem reservierten Namen `anthropic.advisor` (als `agent_name` (java, php: `agentName`; csharp, go: `AgentName`) bei den Thread-Lebenszyklus-Events und als `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) bei der Übermittlung des Ratschlags); siehe [Der Session einen Advisor geben](https://platform.claude.com/docs/de/managed-agents/multiagent-orchestration#give-the-session-an-advisor) für den Ablauf.

Die Threads eines [Workflow-Runs](https://platform.claude.com/docs/de/managed-agents/workflow-runs) erscheinen im primären Stream wie folgt:

* **Lebenszyklus-Events:** Jeder Run-Thread sendet `session.thread_created` mit der `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) des Runs sowie seine Events `session.thread_status_running`, `session.thread_status_idle` und `session.thread_status_terminated`.
* **Nachrichten-Events:** Der Prompt eines Run-Threads, ein `agent.thread_message_received`-Event, bleibt in dessen eigenem Stream.
* **Run-Events:** `workflow_run.*`-Events kommen ebenfalls in diesem Stream an; siehe [Run-Events](https://platform.claude.com/docs/de/managed-agents/workflow-runs#run-events).
* **Tool-Aufrufe, die auf dich warten:** Tool-Aufrufe eines Run-Threads, die deinen Client benötigen, werden wie bei jedem Kind-Thread in diesen Stream gespiegelt. Siehe [Tool-Berechtigungen und benutzerdefinierte Tools](https://platform.claude.com/docs/de/managed-agents/session-threads#tool-permissions-and-custom-tools).

## Session-Thread-Events

Kritische Events werden an den primären Thread weitergeleitet. Dennoch möchtest du vielleicht das Reasoning und die Tool-Aufrufe eines bestimmten Agenten untersuchen. Streame oder liste dazu die Events des zugehörigen Session-Threads auf.

Jeder Session-Thread hat seinen eigenen Event-Stream unter `/v1/sessions/{session_id}/threads/{thread_id}/stream`, und dieser akzeptiert denselben Parameter `event_deltas[]` wie der Stream auf Session-Ebene, sodass du den Text eines Subagenten in der Vorschau sehen kannst, während das Modell ihn generiert. Eine Verbindung zeigt nur Vorschauen für den Thread, den sie liest: Die Vorschauen eines Kind-Threads erscheinen nie im Stream auf Session-Ebene. Um einen Subagenten live zu verfolgen, öffne also seinen eigenen Thread-Stream. Unter [Session-Thread-Events in der Vorschau anzeigen](https://platform.claude.com/docs/de/managed-agents/event-deltas#preview-session-thread-events) erfährst du, wie du Vorschauen aktivierst, akkumulierst und abgleichst.

In einem Workflow-Run führt der Server einen Workflow aus: ein Programm, das der Agent des primären Threads schreibt. Auf jedem Thread des Runs ist das erste `agent.thread_message_received` der Prompt, den der Workflow geschrieben hat. Seine `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`) ist die ID des primären Threads, und die API setzt seinen `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) nicht. Die API garantiert den Text des Prompts nicht, also parse ihn nicht. Das `session.thread_status_terminated`-Event des Threads im Stream des primären Threads teilt dir mit, dass der Thread fertig ist. Kein Event zeichnet das Ergebnis auf, das er an den Workflow zurückgegeben hat.

Der Stream eines Threads spielt frühere Events nicht erneut ab. Direkt nach `session.thread_created` kann die Event-Liste eines Run-Threads leer sein, weil der Server das erste Event des Threads danach schreibt. Öffne also zuerst den Stream des Threads, liste dann die Events des Threads auf und überspringe jedes gestreamte Event, dessen `id` (csharp, go: `ID`) die Liste zurückgegeben hat.

<Tabs>
  <Tab title="Session-Thread-Events streamen">
    <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="Session-Thread-Events auflisten">
    Liste alle vergangenen Session-Thread-Events auf, um einen vollständigen Verlauf abzurufen.

    <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>

## Tool-Berechtigungen und benutzerdefinierte Tools

Wenn ein Subagent etwas von deinem Client benötigt, etwa die [Berechtigung](https://platform.claude.com/docs/de/managed-agents/events-and-streaming#tool-confirmation), einen Tool-Aufruf auszuführen, oder das [Ergebnis eines benutzerdefinierten Tools](https://platform.claude.com/docs/de/managed-agents/events-and-streaming#handling-custom-tool-calls), wird das Event in den **primären Thread** gespiegelt, wobei `session_thread_id` den ursprünglichen Session-Thread identifiziert. Ein Tool-Aufruf benötigt deine Berechtigung unter `always_ask` oder unter [`auto`](https://platform.claude.com/docs/de/managed-agents/permission-policies#let-the-server-evaluate-each-call-with-auto), wenn der Server zu keiner Entscheidung gelangt.

```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..."]
  }
}
```

Sende `user.tool_confirmation` (mit `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`)) oder `user.custom_tool_result` (mit `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)); der Server leitet die Antwort automatisch an den richtigen Thread weiter. Die Antwort kann auf dem primären Thread und auf dem Thread des Subagenten mit unterschiedlichen `id` (csharp, go: `ID`)-Werten erscheinen. Um die beiden Kopien abzugleichen, vergleiche `type` (csharp, go: `Type`) und `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) (oder `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)), nicht `id` (csharp, go: `ID`).

Die Session wechselt erst zu `idle`, wenn kein Thread `running` ist, daher kann `session.status_idle` lange nach dem Aufruf eines Subagenten ankommen. Du musst nicht darauf warten: Sende das `user.custom_tool_result`, sobald das gespiegelte `agent.custom_tool_use`-Event ankommt.

Unter `auto` können deine `user.message`-Events den Server dazu bringen, einen Aufruf zu erlauben, den er sonst ablehnen würde. Nichts im Thread eines Subagenten zählt als deine Absicht. Dein Client sendet dort keine Nachrichten, und die Nachrichten, die der Agent des primären Threads an den Subagenten sendet, zählen nicht. Wenn der Server einen Aufruf unter `auto` ablehnt, wird nichts gespiegelt: Das Event und das Fehler-Tool-Ergebnis erscheinen nur im eigenen [Thread-Stream](https://platform.claude.com/docs/de/managed-agents/session-threads#session-thread-events) des Subagenten, und der Subagent läuft weiter.

Das folgende Beispiel gehört in die Event-Schleife des [Handlers für Tool-Bestätigungen](https://platform.claude.com/docs/de/managed-agents/events-and-streaming#tool-confirmation). Für jede ID in `stop_reason.event_ids` sendet es eine `user.tool_confirmation`, die den Aufruf erlaubt. Dasselbe Muster gilt für `user.custom_tool_result`, das du ebenfalls senden kannst, sobald das zugehörige `agent.custom_tool_use`-Event ankommt.

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    Dieser Workflow benötigt mehr Logik als ein einzelner Shell-Befehl. Verwende das [Claude SDK](https://platform.claude.com/docs/de/cli-sdks-libraries/overview), um ihn auszuführen.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Dieser Workflow benötigt mehr Logik als ein einzelner Shell-Befehl. Verwende das [Claude SDK](https://platform.claude.com/docs/de/cli-sdks-libraries/overview), um ihn auszuführen.
  </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>

Das vorherige Muster beantwortet die Aufrufe, die ein Idle-Event auflistet. Im primären Stream kann das `session.thread_status_idle`-Event eines Subagenten vor den `agent.tool_use`- oder `agent.mcp_tool_use`-Events ankommen, die sein `stop_reason.event_ids` auflistet. Eine `user.tool_confirmation` für einen Aufruf, dessen Event noch nicht angekommen ist, kann 400 zurückgeben. Um das zu vermeiden, beantworte jeden Aufruf, dessen [`evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`)](https://platform.claude.com/docs/de/managed-agents/permission-policies#see-how-each-call-was-evaluated) `ask` ist, sobald sein eigenes Event im primären Stream ankommt.
