Follow Discord
Sweep 09 Oct 2026 · 17:27Z Build v2.1.296 517 read Stable v2.1.287 Latest v2.1.296 Next v2.1.296 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · api

events-and-streaming changedmanaged-agents/events-and-streaming

Nearest release: v2.1.296, published an hour after this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Recorded here
Lines+177added
Lines−189removed
From line 748 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits19to this page, all time

The whole hunk

from line 748, old and new numbered
/
lines
from line 748
748748 
7497491. The session emits the tool call as an `agent.custom_tool_use`, `agent.tool_use`, or `agent.mcp_tool_use` event.
7507502. The session pauses with a `session.status_idle` event whose `stop_reason.type` is `requires_action`. The blocking event IDs are in the `stop_reason.event_ids` array.
7513. For each blocking event ID, send a [`user.custom_tool_result`](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#return-a-custom-tool-result) or a [`user.tool_confirmation`](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#confirm-a-tool-call) event.
7513. For each blocking event ID, send a [`user.custom_tool_result`](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#return-a-custom-tool-result) or a [`user.tool_confirmation`](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#confirm-a-tool-call) event. A custom tool result doesn't have to wait for step 2.
7527524. Once all blocking events are resolved, the session transitions back to `running`.
753753 
754754In a multiagent session, a subagent's blocking events are cross-posted to the primary thread. See [Tool permissions and custom tools](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#tool-permissions-and-custom-tools).
from line 757
757757 
758758The `agent.custom_tool_use` event contains the tool name and input. Execute the tool in your system. Then send a `user.custom_tool_result` event, passing the event ID in the `custom_tool_use_id` parameter along with the result content.
759759 
760You can send the result as soon as the `agent.custom_tool_use` event arrives, without waiting for `session.status_idle`. The session still emits `session.status_idle` with a `requires_action` stop reason for the call, and your client can ignore it. A second result for the same call is accepted and has no effect.
761 
762When you [reconnect](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#reconnect-without-missing-events) to a paused session, `stop_reason.event_ids` on the latest `session.status_idle` event lists the calls to answer.
763 
764The following example answers each call as it arrives:
765 
760766<CodeGroup>
761767 ```bash cURL
762768 exec {stream_fd}< <(curl --fail-with-body -sS -N \
from line 776
770776 while IFS= read -r -u "$stream_fd" line; do
771777 [[ $line == data:* ]] || continue
772778 event_json="${line#data: }"
773 stop_reason=$(jq -r 'select(.type == "session.status_idle") | .stop_reason.type // empty' <<<"$event_json")
774 case "$stop_reason" in
775 requires_action)
776 while IFS= read -r event_id; do
777 # Execute the tool and send the result back
778 result=$(call_tool "$event_id")
779 jq -n --arg id "$event_id" --arg result "$result" \
780 '{events: [{type: "user.custom_tool_result", custom_tool_use_id: $id, content: [{type: "text", text: $result}]}]}' |
781 curl --fail-with-body -sS \
782 "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
783 -H "x-api-key: $ANTHROPIC_API_KEY" \
784 -H "anthropic-version: 2023-06-01" \
785 -H "anthropic-beta: managed-agents-2026-04-01" \
786 -H "content-type: application/json" \
787 -d @-
788 done < <(jq -r '.stop_reason.event_ids[]' <<<"$event_json")
779 case $(jq -r '.type' <<<"$event_json") in
780 agent.custom_tool_use)
781 # Execute the tool and send the result back
782 result=$(call_tool "$(jq -r '.name' <<<"$event_json")" "$(jq -c '.input' <<<"$event_json")")
783 jq --arg result "$result" \
784 '{events: [{type: "user.custom_tool_result", custom_tool_use_id: .id, content: [{type: "text", text: $result}]}]}' <<<"$event_json" |
785 curl --fail-with-body -sS \
786 "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
787 -H "x-api-key: $ANTHROPIC_API_KEY" \
788 -H "anthropic-version: 2023-06-01" \
789 -H "anthropic-beta: managed-agents-2026-04-01" \
790 -H "content-type: application/json" \
791 -d @-
789792 ;;
790 end_turn)
791 break
793 session.status_idle)
794 if [[ $(jq -r '.stop_reason.type' <<<"$event_json") == end_turn ]]; then
795 break
796 fi
792797 ;;
793798 esac
794799 done
from line 808
803808 ```python Python
804809 with client.beta.sessions.events.stream(session.id) as stream:
805810 for event in stream:
806 if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
807 match stop_reason.type:
808 case "requires_action":
809 for event_id in stop_reason.event_ids:
810 # Look up the custom tool use event and execute it
811 tool_event = events_by_id[event_id]
812 result = call_tool(tool_event.name, tool_event.input)
811 match event.type:
812 case "agent.custom_tool_use":
813 # Execute the tool
814 result = call_tool(event.name, event.input)
813815 
814 # Send the result back
815 client.beta.sessions.events.send(
816 session.id,
817 events=[
818 {
819 "type": "user.custom_tool_result",
820 "custom_tool_use_id": event_id,
821 "content": [{"type": "text", "text": result}],
822 },
823 ],
824 )
825 case "end_turn":
816 # Send the result back
817 client.beta.sessions.events.send(
818 session.id,
819 events=[
820 {
821 "type": "user.custom_tool_result",
822 "custom_tool_use_id": event.id,
823 "content": [{"type": "text", "text": result}],
824 },
825 ],
826 )
827 case "session.status_idle":
828 if event.stop_reason and event.stop_reason.type == "end_turn":
826829 break
827830 ```
828831 
from line 832
829832 ```typescript TypeScript
830833 const stream = await client.beta.sessions.events.stream(session.id);
831834 
832 for await (const event of stream) {
833 if (event.type !== "session.status_idle") continue;
834 if (event.stop_reason.type === "end_turn") break;
835 if (event.stop_reason.type !== "requires_action") continue;
835 loop: for await (const event of stream) {
836 switch (event.type) {
837 case "agent.custom_tool_use": {
838 // Execute the tool
839 const result = await callTool(event.name, event.input);
836840 
837 for (const eventId of event.stop_reason.event_ids) {
838 // Look up the custom tool use event and execute it
839 const toolEvent = eventsById.get(eventId);
840 if (!toolEvent) continue;
841 const result = await callTool(toolEvent.name, toolEvent.input);
842 
843 // Send the result back
844 await client.beta.sessions.events.send(session.id, {
845 events: [
846 {
847 type: "user.custom_tool_result",
848 custom_tool_use_id: eventId,
849 content: [{ type: "text", text: result }],
850 },
851 ],
852 });
841 // Send the result back
842 await client.beta.sessions.events.send(session.id, {
843 events: [
844 {
845 type: "user.custom_tool_result",
846 custom_tool_use_id: event.id,
847 content: [{ type: "text", text: result }],
848 },
849 ],
850 });
851 break;
852 }
853 case "session.status_idle":
854 if (event.stop_reason.type === "end_turn") break loop;
855 break;
853856 }
854857 }
855858 ```
from line 860
857860 ```csharp C#
858861 await foreach (var streamEvent in client.Beta.Sessions.Events.StreamStreaming(session.ID))
859862 {
860 if (streamEvent.Value is not BetaManagedAgentsSessionStatusIdleEvent idle) continue;
861 
862 if (idle.StopReason?.Value is BetaManagedAgentsSessionRequiresAction requiresAction)
863 if (streamEvent.Value is BetaManagedAgentsAgentCustomToolUseEvent toolUse)
863864 {
864 foreach (var eventId in requiresAction.EventIds)
865 {
866 // Look up the custom tool use event and execute it
867 var toolEvent = eventsById[eventId];
868 var result = await CallTool(toolEvent.Name, toolEvent.Input);
865 // Execute the tool
866 var result = await CallTool(toolUse.Name, toolUse.Input);
869867 
870 // Send the result back
871 await client.Beta.Sessions.Events.Send(session.ID, new()
872 {
873 Events =
874 [
875 new BetaManagedAgentsUserCustomToolResultEventParams
876 {
877 Type = BetaManagedAgentsUserCustomToolResultEventParamsType.UserCustomToolResult,
878 CustomToolUseID = eventId,
879 Content =
880 [
881 new BetaManagedAgentsTextBlock
882 {
883 Type = BetaManagedAgentsTextBlockType.Text,
884 Text = result,
885 },
886 ],
887 },
888 ],
889 });
890 }
868 // Send the result back
869 await client.Beta.Sessions.Events.Send(session.ID, new()
870 {
871 Events =
872 [
873 new BetaManagedAgentsUserCustomToolResultEventParams
874 {
875 Type = BetaManagedAgentsUserCustomToolResultEventParamsType.UserCustomToolResult,
876 CustomToolUseID = toolUse.ID,
877 Content =
878 [
879 new BetaManagedAgentsTextBlock
880 {
881 Type = BetaManagedAgentsTextBlockType.Text,
882 Text = result,
883 },
884 ],
885 },
886 ],
887 });
891888 }
892 else if (idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn)
889 else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle
890 && idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn)
893891 {
894892 break;
895893 }
from line 900
902900 
903901 loop:
904902 for stream.Next() {
905 event, ok := stream.Current().AsAny().(anthropic.BetaManagedAgentsSessionStatusIdleEvent)
906 if !ok {
907 continue
908 }
909 switch stopReason := event.StopReason.AsAny().(type) {
910 case anthropic.BetaManagedAgentsSessionRequiresAction:
911 for _, eventID := range stopReason.EventIDs {
912 // Look up the custom tool use event and execute it
913 toolEvent := eventsByID[eventID]
914 result := callTool(toolEvent.Name, toolEvent.Input)
915 // Send the result back
916 if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
917 Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
918 OfUserCustomToolResult: &anthropic.BetaManagedAgentsUserCustomToolResultEventParams{
919 Type: anthropic.BetaManagedAgentsUserCustomToolResultEventParamsTypeUserCustomToolResult,
920 CustomToolUseID: eventID,
921 Content: []anthropic.BetaManagedAgentsUserCustomToolResultEventParamsContentUnion{{
922 OfText: &anthropic.BetaManagedAgentsTextBlockParam{
923 Type: anthropic.BetaManagedAgentsTextBlockTypeText,
924 Text: result,
925 },
926 }},
927 },
928 }},
929 }); err != nil {
930 panic(err)
931 }
903 switch event := stream.Current().AsAny().(type) {
904 case anthropic.BetaManagedAgentsAgentCustomToolUseEvent:
905 // Execute the tool
906 result := callTool(event.Name, event.Input)
907 // Send the result back
908 if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
909 Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
910 OfUserCustomToolResult: &anthropic.BetaManagedAgentsUserCustomToolResultEventParams{
911 Type: anthropic.BetaManagedAgentsUserCustomToolResultEventParamsTypeUserCustomToolResult,
912 CustomToolUseID: event.ID,
913 Content: []anthropic.BetaManagedAgentsUserCustomToolResultEventParamsContentUnion{{
914 OfText: &anthropic.BetaManagedAgentsTextBlockParam{
915 Type: anthropic.BetaManagedAgentsTextBlockTypeText,
916 Text: result,
917 },
918 }},
919 },
920 }},
921 }); err != nil {
922 panic(err)
932923 }
933 case anthropic.BetaManagedAgentsSessionEndTurn:
934 break loop
924 case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
925 if _, ok := event.StopReason.AsAny().(anthropic.BetaManagedAgentsSessionEndTurn); ok {
926 break loop
927 }
935928 }
936929 }
937930 if err := stream.Err(); err != nil {
from line 934
941934 
942935 ```java Java
943936 try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
944 stream.stream()
945 .filter(BetaManagedAgentsStreamSessionEvents::isSessionStatusIdle)
946 .map(idleEvent -> idleEvent.asSessionStatusIdle().stopReason())
947 .takeWhile(stopReason -> !stopReason.isEndTurn())
948 .filter(stopReason -> stopReason.isRequiresAction())
949 .flatMap(stopReason -> stopReason.asRequiresAction().eventIds().stream())
950 .forEach(eventId -> {
951 // Look up the custom tool use event and execute it
952 var toolEvent = eventsById.get(eventId);
953 var result = callTool(toolEvent.name(), toolEvent.input());
937 loop:
938 for (var event : (Iterable<BetaManagedAgentsStreamSessionEvents>) stream.stream()::iterator) {
939 switch (event.type().value()) {
940 case AGENT_CUSTOM_TOOL_USE -> {
941 // Execute the tool
942 var toolUse = event.asAgentCustomToolUse();
943 var result = callTool(toolUse.name(), toolUse.input());
954944 
955 // Send the result back
956 client.beta().sessions().events().send(
957 session.id(),
958 EventSendParams.builder()
959 .addEvent(BetaManagedAgentsUserCustomToolResultEventParams.builder()
960 .type(BetaManagedAgentsUserCustomToolResultEventParams.Type.USER_CUSTOM_TOOL_RESULT)
961 .customToolUseId(eventId)
962 .addTextContent(result)
963 .build())
964 .build());
965 });
945 // Send the result back
946 client.beta().sessions().events().send(
947 session.id(),
948 EventSendParams.builder()
949 .addEvent(BetaManagedAgentsUserCustomToolResultEventParams.builder()
950 .type(BetaManagedAgentsUserCustomToolResultEventParams.Type.USER_CUSTOM_TOOL_RESULT)
951 .customToolUseId(toolUse.id())
952 .addTextContent(result)
953 .build())
954 .build());
955 }
956 case SESSION_STATUS_IDLE -> {
957 if (event.asSessionStatusIdle().stopReason().isEndTurn()) {
958 break loop;
959 }
960 }
961 }
962 }
966963 }
967964 ```
968965 
from line 967
970967 $stream = $client->beta->sessions->events->streamStream($session->id);
971968 
972969 foreach ($stream as $event) {
973 if ($event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent && $event->stopReason) {
974 switch (true) {
975 case $event->stopReason instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionRequiresAction:
976 foreach ($event->stopReason->eventIDs as $eventId) {
977 // Look up the custom tool use event and execute it
978 $toolEvent = $eventsById[$eventId];
979 $result = callTool($toolEvent->name, $toolEvent->input);
970 switch (true) {
971 case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsAgentCustomToolUseEvent:
972 // Execute the tool
973 $result = callTool($event->name, $event->input);
980974 
981 // Send the result back
982 $client->beta->sessions->events->send(
983 $session->id,
984 events: [
985 [
986 'type' => 'user.custom_tool_result',
987 'custom_tool_use_id' => $eventId,
988 'content' => [['type' => 'text', 'text' => $result]],
989 ],
990 ],
991 );
992 }
993 break;
994 case $event->stopReason instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionEndTurn:
975 // Send the result back
976 $client->beta->sessions->events->send(
977 $session->id,
978 events: [
979 [
980 'type' => 'user.custom_tool_result',
981 'custom_tool_use_id' => $event->id,
982 'content' => [['type' => 'text', 'text' => $result]],
983 ],
984 ],
985 );
986 break;
987 case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent:
988 if ($event->stopReason instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionEndTurn) {
995989 break 2;
996 }
990 }
991 break;
997992 }
998993 }
999994 ```
from line 996
1001996 ```ruby Ruby
1002997 client.beta.sessions.events.stream_events(session.id).each do |event|
1003998 case event
999 when Anthropic::Beta::Sessions::BetaManagedAgentsAgentCustomToolUseEvent
1000 # Execute the tool
1001 result = call_tool.call(event.name, event.input)
1002 # Send the result back
1003 client.beta.sessions.events.send_(
1004 session.id,
1005 events: [
1006 {
1007 type: "user.custom_tool_result",
1008 custom_tool_use_id: event.id,
1009 content: [{type: "text", text: result}]
1010 }
1011 ]
1012 )
10041013 when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
1005 stop_reason = event.stop_reason
1006 case stop_reason
1007 when Anthropic::Beta::Sessions::BetaManagedAgentsSessionRequiresAction
1008 stop_reason.event_ids.each do |event_id|
1009 # Look up the custom tool use event and execute it
1010 tool_event = events_by_id[event_id]
1011 result = call_tool.call(tool_event.name, tool_event.input)
1012 # Send the result back
1013 client.beta.sessions.events.send_(
1014 session.id,
1015 events: [
1016 {
1017 type: "user.custom_tool_result",
1018 custom_tool_use_id: event_id,
1019 content: [{type: "text", text: result}]
1020 }
1021 ]
1022 )
1023 end
1024 when Anthropic::Beta::Sessions::BetaManagedAgentsSessionEndTurn
1025 break
1026 end
1014 break if event.stop_reason.is_a?(Anthropic::Beta::Sessions::BetaManagedAgentsSessionEndTurn)
10271015 end
10281016 end
10291017 ```
Feedback