workflow-runs changedmanaged-agents/workflow-runs
Recorded here
Lines+33added
Lines−33removed
From line
10
where the diff opens
First seen
9 Oct 2026
this site's first read of the page
Recorded edits9to this page, all time
The whole hunk
from line 10, old and new numbered
/
from line 10
1010 betaHeader: managed-agents-2026-04-01
1111---
1212
13A **workflow** is a program that an agent writes to run many agents and combine what they return. A **workflow run** is the execution of one workflow. **[Dynamic workflows](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#dynamic-workflows)** is the feature that lets an agent write workflows and start runs. You turn it on or off with the `workflows` setting in the agent's `multiagent` block.
13A **workflow** is a program that an agent writes to run many agents and combine what they return. A **workflow run** is the execution of one workflow. **[Dynamic workflows](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#dynamic-workflows)** is the feature that lets an agent write workflows and start runs. You turn it on or off with the `workflows` (java, php; csharp, go: `Workflows`) setting in the agent's `multiagent` (java, php; csharp, go: `Multiagent`) block.
1414
1515The server runs a workflow in the background. Its agents work in [session threads](https://platform.claude.com/docs/en/managed-agents/session-threads) that the server creates as the workflow needs them. You follow runs on the session's [event stream](https://platform.claude.com/docs/en/managed-agents/events-and-streaming). Only the agent starts a run. No event you send ends one; archiving the session can.
1616
from line 26
2626
2727* **Workflow run:** The server runs the workflow in the background, as one workflow run. A session can have several runs open at the same time.
2828* **Phases:** A workflow can divide its work into phases. A phase is a named stage of the run, such as "Read the contracts". You follow a run's progress by its phase events.
29* **Agent threads:** In a phase, the program runs agents. Each agent works in its own [session thread](https://platform.claude.com/docs/en/managed-agents/session-threads), on a prompt that the program wrote. An agent in a run can be an inline agent, which the program defines itself, or a predefined agent, which you list in [`workflows.predefined_agents`](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#predefined-and-inline-agents). For what each thread shows, see [A run's threads](https://platform.claude.com/docs/en/managed-agents/workflow-runs#a-runs-threads).
29* **Agent threads:** In a phase, the program runs agents. Each agent works in its own [session thread](https://platform.claude.com/docs/en/managed-agents/session-threads), on a prompt that the program wrote. An agent in a run can be an inline agent, which the program defines itself, or a predefined agent, which you list in the [`predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) list of `workflows` (java, php; csharp, go: `Workflows`)](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#predefined-and-inline-agents). For what each thread shows, see [A run's threads](https://platform.claude.com/docs/en/managed-agents/workflow-runs#a-runs-threads).
3030
3131The program can do the following:
3232
from line 48
4848
4949A run starts as running or as idle. Reaching the budget, for example, pauses a running run, which makes it idle; raising or removing the budget then makes it run again, unless an interrupt paused it too. A running run ends when its workflow finishes, the agent stops it, it fails, its lifetime passes, or the session is archived. An idle run can end too, for example when the agent stops it or the session is archived.
5050
51A run is **open** from its `workflow_run.created` event until its `workflow_run.status_ended` event, whether it's running or idle. A run is idle while it's paused, for example at the session's budget. A run's lifetime is 24 hours by default. The agent can set a shorter lifetime when it starts the run. Time that a run spends waiting on your client counts toward that lifetime. A pause doesn't stop a run's lifetime from passing, so a run that stays paused can end with `timeout_error`. The following events report a run's start, its phases, and its end. A pause at the budget sends one too. A pause after an interrupt might send none. Every `workflow_run.*` event includes `workflow_run_id`, which is `null` only on a `workflow_run.error` when no run was created.
51A run is **open** from its `workflow_run.created` event until its `workflow_run.status_ended` event, whether it's running or idle. A run is idle while it's paused, for example at the session's budget. A run's lifetime is 24 hours by default. The agent can set a shorter lifetime when it starts the run. Time that a run spends waiting on your client counts toward that lifetime. A pause doesn't stop a run's lifetime from passing, so a run that stays paused can end with `timeout_error`. The following events report a run's start, its phases, and its end. A pause at the budget sends one too. A pause after an interrupt might send none. Every `workflow_run.*` event includes `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`), which is `null` only on a `workflow_run.error` when no run was created.
5252
5353## Run events
5454
55Run events arrive on the session's event stream, which is the primary thread's stream, and listing the session's events returns them too. Run events don't trigger [webhooks](https://platform.claude.com/docs/en/managed-agents/webhooks). Status events from the run's threads arrive on the same stream. Each names its thread in `session_thread_id`, and a run's threads are the ones whose `session.thread_created` event had the run's `workflow_run_id`.
55Run events arrive on the session's event stream, which is the primary thread's stream, and listing the session's events returns them too. Run events don't trigger [webhooks](https://platform.claude.com/docs/en/managed-agents/webhooks). Status events from the run's threads arrive on the same stream. Each names its thread in `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), and a run's threads are the ones whose `session.thread_created` event had the run's `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`).
5656
57| Event | When it arrives | What to do |
58| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
59| `workflow_run.created` | The agent started a run. Includes `workflow_run_id` (`wrun_…`), the run's `name` and `description`, and `phases`, the phases the workflow declares, each with an `id`, a `name`, and a `description`. A `description` is `null` when the workflow gives none. `phases` is always present and can be empty. The run's and phases' `name` and `description` are text the model wrote, so they can repeat words from your request. A run's `name` can also be one the server assigned. | Track the run as open. Show its `name`, and progress against `phases`. |
60| `workflow_run.status_running` | When the run starts to execute, which can be a while after `created`, and each time it resumes after a pause at the budget. A resume after an interrupt might not send it. A run that starts idle might get `workflow_run.status_idle` first. | Show the run as running. |
61| `workflow_run.status_idle` | The run was paused, for example at the session's budget. The event doesn't say why. A pause after an interrupt might not send it. | To continue, see [Budgets and limits](https://platform.claude.com/docs/en/managed-agents/workflow-runs#budgets-and-limits) or [Interrupt a session with runs open](https://platform.claude.com/docs/en/managed-agents/workflow-runs#interrupt-a-session-with-runs-open). |
62| `workflow_run.phase_started`, `workflow_run.phase_ended` | The workflow entered or left a phase, or the run's end closed a phase that was still open. The end event doesn't say whether the phase's work finished. Both include `workflow_run_phase_id`. The end also has `phase_started_id`, the `id` of the start event it closes. Neither has the phase's name: look it up by `workflow_run_phase_id` in the `phases` of `workflow_run.created`. | Update progress. Phases run one at a time, in the order of `phases`, each at most once, but the API doesn't guarantee it. Match a phase's end to its start by `phase_started_id`. Handle more than one open phase, a phase that isn't in `phases`, and a listed phase that never starts, even in a run that completes. Every phase that starts also ends, before the run's `workflow_run.status_ended`. |
63| `workflow_run.status_ended` | The run ended. Always the last of the run's `workflow_run.*` events. Includes `result`. | Read `result` (next table). The agent then gets a turn to read how the run ended. At the budget, or while the primary thread waits on your client, that turn comes later. After an interrupt, that turn might not come: send a `user.message`, or read `result` yourself. After an archive or termination, it doesn't come. |
64| `workflow_run.error` | The server reports an error of a run, or a start that it refused. A run that ends in `error` gets this event, with the same error, before its `workflow_run.status_ended`. Includes `error`: a `type` and a `message` that is safe to log. `workflow_run_id` is `null` when no run was created. | Log it, and don't take it as the run's end. If `workflow_run_id` is `null`, no run started. Otherwise keep tracking the run until its `workflow_run.status_ended`. |
57| Event | When it arrives | What to do |
58| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
59| `workflow_run.created` | The agent started a run. Includes `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) (`wrun_…`), the run's `name` (java, php; csharp, go: `Name`) and `description` (java, php; csharp, go: `Description`), and `phases` (java, php; csharp, go: `Phases`), the phases the workflow declares, each with an `id` (java, php; csharp, go: `ID`), a `name` (java, php; csharp, go: `Name`), and a `description` (java, php; csharp, go: `Description`). A `description` (java, php; csharp, go: `Description`) is `null` when the workflow gives none. `phases` (java, php; csharp, go: `Phases`) is always present and can be empty. The run's and phases' `name` (java, php; csharp, go: `Name`) and `description` (java, php; csharp, go: `Description`) are text the model wrote, so they can repeat words from your request. A run's `name` (java, php; csharp, go: `Name`) can also be one the server assigned. | Track the run as open. Show its `name` (java, php; csharp, go: `Name`), and progress against `phases` (java, php; csharp, go: `Phases`). |
60| `workflow_run.status_running` | When the run starts to execute, which can be a while after `created`, and each time it resumes after a pause at the budget. A resume after an interrupt might not send it. A run that starts idle might get `workflow_run.status_idle` first. | Show the run as running. |
61| `workflow_run.status_idle` | The run was paused, for example at the session's budget. The event doesn't say why. A pause after an interrupt might not send it. | To continue, see [Budgets and limits](https://platform.claude.com/docs/en/managed-agents/workflow-runs#budgets-and-limits) or [Interrupt a session with runs open](https://platform.claude.com/docs/en/managed-agents/workflow-runs#interrupt-a-session-with-runs-open). |
62| `workflow_run.phase_started`, `workflow_run.phase_ended` | The workflow entered or left a phase, or the run's end closed a phase that was still open. The end event doesn't say whether the phase's work finished. Both include `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`). The end also has `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`), the `id` (java, php; csharp, go: `ID`) of the start event it closes. Neither has the phase's name: look it up by `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`) in the `phases` (java, php; csharp, go: `Phases`) of `workflow_run.created`. | Update progress. Phases run one at a time, in the order of `phases` (java, php; csharp, go: `Phases`), each at most once, but the API doesn't guarantee it. Match a phase's end to its start by `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`). Handle more than one open phase, a phase that isn't in `phases` (java, php; csharp, go: `Phases`), and a listed phase that never starts, even in a run that completes. Every phase that starts also ends, before the run's `workflow_run.status_ended`. |
63| `workflow_run.status_ended` | The run ended. Always the last of the run's `workflow_run.*` events. Includes `result` (java, php; csharp, go: `Result`). | Read `result` (java, php; csharp, go: `Result`) (next table). The agent then gets a turn to read how the run ended. At the budget, or while the primary thread waits on your client, that turn comes later. After an interrupt, that turn might not come: send a `user.message`, or read `result` (java, php; csharp, go: `Result`) yourself. After an archive or termination, it doesn't come. |
64| `workflow_run.error` | The server reports an error of a run, or a start that it refused. A run that ends in `error` gets this event, with the same error, before its `workflow_run.status_ended`. Includes `error` (java, php; csharp, go: `Error`): a `type` (java, php; csharp, go: `Type`) and a `message` (java, php; csharp, go: `Message`) that is safe to log. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) is `null` when no run was created. | Log it, and don't take it as the run's end. If `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) is `null`, no run started. Otherwise keep tracking the run until its `workflow_run.status_ended`. |
6565
6666| `result` | Meaning |
6767| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
from line 76
7676
7777For example, you ask the [contract-review agent](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows) which of 300 contracts have a change-of-control clause, and the agent starts a run:
7878
791. `workflow_run.created` names the run "Find change-of-control clauses" and lists the phases "Read the contracts" and "Reconcile the findings" in `phases`. Then `workflow_run.status_running` follows.
802. Phase events mark each phase, and each thread the run creates sends `session.thread_created` with the run's `workflow_run_id`.
791. `workflow_run.created` names the run "Find change-of-control clauses" and lists the phases "Read the contracts" and "Reconcile the findings" in `phases` (java, php; csharp, go: `Phases`). Then `workflow_run.status_running` follows.
802. Phase events mark each phase, and each thread the run creates sends `session.thread_created` with the run's `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`).
81813. `workflow_run.status_ended` arrives with `result: {"type": "completed"}`.
82824. The agent answers, "41 of the 300 contracts have one," and `session.status_idle` arrives with `end_turn`.
8383
from line 130
130130
131131Each agent in a run works in its own [session thread](https://platform.claude.com/docs/en/managed-agents/session-threads), which the server creates as the workflow needs it. You can list, read, and stream a run's threads like any child thread, and answer their tool calls from the primary stream. To stop them, ask the agent to stop the run (see [Interrupt a session with runs open](https://platform.claude.com/docs/en/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)). You can't stop one by its ID, or archive one while its run is open.
132132
133* **Grouping:** A run's thread carries the run's `workflow_run_id`, as does the `session.thread_created` event that announces it. Other threads, and the `session.thread_created` events that announce them, have `workflow_run_id` set to `null`.
134* **Agent:** `agent` shows the agent the thread runs. For an agent you listed in [`multiagent.workflows.predefined_agents`](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows), `agent` has that agent's `id` and `version`, as on the thread of a subagent you listed. For an agent the workflow defines (an inline agent), `agent` has `type` `inline` and no `id` or `version`. It has the system prompt the workflow wrote, not the session agent's. It also has the name and description the workflow gave it; the server assigns a name if the workflow gave none. It uses the model of the session agent, the agent the session runs. Its tools, MCP servers, and skills are a subset of the session agent's. It gets all of them, but the API doesn't guarantee that. Its tools keep their permission policies.
133* **Grouping:** A run's thread carries the run's `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`), as does the `session.thread_created` event that announces it. Other threads, and the `session.thread_created` events that announce them, have `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) set to `null`.
134* **Agent:** `agent` (java, php; csharp, go: `Agent`) shows the agent the thread runs. For an agent you listed in the [`predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) list of `workflows` (java, php; csharp, go: `Workflows`)](https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows) in `multiagent` (java, php; csharp, go: `Multiagent`), `agent` (java, php; csharp, go: `Agent`) has that agent's `id` (java, php; csharp, go: `ID`) and `version` (java, php; csharp, go: `Version`), as on the thread of a subagent you listed. For an agent the workflow defines (an inline agent), `agent` (java, php; csharp, go: `Agent`) has `type` (java, php; csharp, go: `Type`) `inline` and no `id` (java, php; csharp, go: `ID`) or `version` (java, php; csharp, go: `Version`). It has the system prompt the workflow wrote, not the session agent's. It also has the name and description the workflow gave it; the server assigns a name if the workflow gave none. It uses the model of the session agent, the agent the session runs. Its tools, MCP servers, and skills are a subset of the session agent's. It gets all of them, but the API doesn't guarantee that. Its tools keep their permission policies.
135135* **What the threads share:** A run's threads work in the session's sandbox, so every thread works with the same files. That includes the files of a memory store the session mounts. An agent the workflow defines uses its MCP servers with the credentials the session resolves for them. Each thread has its own conversation history.
136136* **Events:** A run thread's `session.thread_created`, `session.thread_status_running`, `session.thread_status_idle`, and `session.thread_status_terminated` events also arrive on the primary stream (see [Run events](https://platform.claude.com/docs/en/managed-agents/workflow-runs#run-events)). Its message events stay on its own stream. Its thread webhooks are sent as for any child thread. For what the thread's own stream records, see [Session thread events](https://platform.claude.com/docs/en/managed-agents/session-threads#session-thread-events).
137* **Phases:** No event or field says which phase a thread works in, and threads of one run can have the same `agent_name`. Follow a run's progress by its phase events, and tell its threads apart by `session_thread_id`.
137* **Phases:** No event or field says which phase a thread works in, and threads of one run can have the same `agent_name`. Follow a run's progress by its phase events, and tell its threads apart by `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`).
138138* **Thread limit:** A run's threads are exempt from the session's [child-thread limit](https://platform.claude.com/docs/en/managed-agents/session-threads#primary-thread-and-session-threads).
139139* **Starting runs:** Only the agent on the session's primary thread starts runs. An agent working in a run's thread can't start a run of its own, so runs don't nest.
140140* **Archiving:** The server archives each thread no later than the end of its run. It can archive one sooner, once the thread returns its result or the run finishes with it. If the thread is still running or waiting on your client at that point, the server stops it first. An archived thread stays in the thread list, with status `terminated`. You don't need to archive a run's threads yourself. While the run is open, a request to archive one that the server hasn't archived yet returns 400 with `error.details.error_code: "workflow_run_open"`.
from line 153
153153While a run is running, expect the session to stay `running`, even while none of its threads is working. It goes `idle` with `requires_action` when no thread is working and a thread waits on your client. An idle by itself doesn't mean that the work is done. The work is done when both are true:
154154
1551551. Every run you've seen created has its `workflow_run.status_ended`.
1562. After that, a `session.status_idle` arrives with `stop_reason` `end_turn`, and your own request, such as an interrupt, didn't cause it. After you interrupt, count only an idle that comes after your next `user.message` or `user.define_outcome`.
1562. After that, a `session.status_idle` arrives with `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`) `end_turn`, and your own request, such as an interrupt, didn't cause it. After you interrupt, count only an idle that comes after your next `user.message` or `user.define_outcome`.
157157
158158* **Paused runs:** A paused run doesn't keep the session `running`, so the session can go idle while the run is still open. At the budget, for example, the session goes idle with `budget_reached`. The work isn't done until the run ends.
159159* **Another run:** The agent can start a new run when it reads a result, so check again.
160160* **Outcomes:** If you [defined an outcome](https://platform.claude.com/docs/en/managed-agents/define-outcomes), no evaluation starts while a run is open, whether it's running or idle. The turn in which the agent reads the run's result can start one.
161* **`retries_exhausted`:** The agent's turn failed on an error: retries ran out, or the error can't be retried, such as a billing failure. A run might still be running when this idle comes. If a run ended and the agent hasn't read its result yet, the server starts a new turn with no input from you. The session goes `running` again, so wait for the next idle. If the session stays idle, read the `session.error` that came before it and fix the cause. Then send a `user.message`, or read each run's `result` yourself.
161* **`retries_exhausted`:** The agent's turn failed on an error: retries ran out, or the error can't be retried, such as a billing failure. A run might still be running when this idle comes. If a run ended and the agent hasn't read its result yet, the server starts a new turn with no input from you. The session goes `running` again, so wait for the next idle. If the session stays idle, read the `session.error` that came before it and fix the cause. Then send a `user.message`, or read each run's `result` (java, php; csharp, go: `Result`) yourself.
162162
163163## Follow a run
164164
from line 165
165165This sample follows a session from your message to the agent's answer. It opens the stream and sends the message. Then it does the following:
166166
167167* **Tracks each run** from its `workflow_run.created` to its `workflow_run.status_ended`, and prints each phase as it starts.
168* **Answers custom tool calls** when each `agent.custom_tool_use` arrives, because a run's thread can wait on your client while the session stays `running`. If your agent's tools [ask for confirmation](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#tool-confirmation), add a branch that answers each `agent.tool_use` or `agent.mcp_tool_use` whose `evaluated_permission` is `ask`. The sample has none, because a branch that allows every call would turn `always_ask` into always allow.
168* **Answers custom tool calls** when each `agent.custom_tool_use` arrives, because a run's thread can wait on your client while the session stays `running`. If your agent's tools [ask for confirmation](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#tool-confirmation), add a branch that answers each `agent.tool_use` or `agent.mcp_tool_use` whose `evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`) is `ask`. The sample has none, because a branch that allows every call would turn `always_ask` into always allow.
169169* **Stops** when the [work is done](https://platform.claude.com/docs/en/managed-agents/workflow-runs#know-when-the-work-is-done): no run is open, and the session goes idle with `end_turn`. It also stops if the session terminates. On an idle with any other stop reason except `requires_action`, such as `budget_reached`, `retries_exhausted`, or `refusal`, it prints the reason and stops, so handle those in your own code. It stops on `retries_exhausted` even when the server is about to start a new turn by itself. It keeps waiting on `requires_action`, and on `end_turn` while a run is open.
170170
171171<CodeGroup>
from line 761
761761
762762## Interrupt a session with runs open
763763
764Send [`user.interrupt`](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#interrupt-the-agent) with no `session_thread_id`, or with the primary thread's ID. It stops the agent's turn. It ends no run. The session's runs might pause or keep running, and their events might not show which. A paused run's lifetime keeps passing, so the run can end with `timeout_error` while it's paused.
764Send [`user.interrupt`](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#interrupt-the-agent) with no `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), or with the primary thread's ID. It stops the agent's turn. It ends no run. The session's runs might pause or keep running, and their events might not show which. A paused run's lifetime keeps passing, so the run can end with `timeout_error` while it's paused.
765765
766* **Waiting tool calls:** After the interrupt, a run thread's tool call might still wait on your client. Answer each one. To cancel a call that asks for confirmation, deny it. To cancel a custom tool call, send a result with `is_error` set to `true` and a text in `content` that says why. While the session is `idle` with `requires_action`, a `user.message` returns 400, so answer the calls first.
766* **Waiting tool calls:** After the interrupt, a run thread's tool call might still wait on your client. Answer each one. To cancel a call that asks for confirmation, deny it. To cancel a custom tool call, send a result with `is_error` (java, php: `isError`; csharp, go: `IsError`) set to `true` and a text in `content` (java, php; csharp, go: `Content`) that says why. While the session is `idle` with `requires_action`, a `user.message` returns 400, so answer the calls first.
767767* **To stop the runs:** Send a `user.message` asking the agent to stop its runs. A stopped run ends with `result` `{"type": "stopped"}`. While the session is `idle` with `budget_reached`, a `user.message` returns 400 until you raise or remove the budget. Raising or removing it also resumes the runs that the budget paused, unless the interrupt also paused them.
768768* **To continue:** Send a `user.message` asking the agent to continue its runs. After an interrupt, the runs might wait for this message. If the session is `idle` with `budget_reached`, raise or remove the budget first.
769769* **Run results:** A run that ends after the interrupt still sends `workflow_run.status_ended`.
from line 770
770770
771771## While a run is open
772772
773| Request | While a run is open | What to do |
774| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
775| Archive or delete the session | Might return 400 while a run is open, whatever the session's status. The error's `error.details.error_code` can be `"workflow_run_open"`. Might also succeed. | Ask the agent to stop its runs, or wait until each run has ended. A paused run ends by itself only when its lifetime passes. Then send the request once the session is `idle`. An archive that succeeds ends each open run with `{"type": "stopped"}`. After an archive, a run's `workflow_run.status_ended`, and the `workflow_run.phase_ended` of a phase that was still open, don't arrive on the stream. List the session's events to read them. After a delete that succeeds, no `workflow_run` event reports the end of the session's runs. |
776| Archive one of a run's threads | Returns 400 with `error.details.error_code: "workflow_run_open"` while the run is open, running or idle, unless the server has already archived the thread. | Nothing. The server archives a run's threads. |
777| Update the session's `agent` | Returns 400 with `error.details.error_code: "workflow_run_open"` while any run is open, even a paused one. Updating the underlying agent is still accepted, and the session keeps its own copy. A request that also sends other fields, such as `budget`, is rejected whole. | Wait until every run has its `workflow_run.status_ended`, or ask the agent to stop its runs. |
778| Answer a tool call or tool confirmation from a run's thread | Allowed. It arrives on the primary stream, and its `session_thread_id` names the thread. | Answer as soon as the event arrives, passing the event's `id` as `tool_use_id` or `custom_tool_use_id`. Don't wait for `session.status_idle`: the session can stay `running` while the run's other threads work. Once the server has archived the thread, a tool result for one of its calls has no effect, and it can return 400. When a tool result returns 400, find the call's thread in the thread list. If its status is `terminated`, the result came too late, so drop it. Send each tool result in a request of its own, because the server refuses a whole request when it refuses one of its events. A tool confirmation that comes too late returns 200, which doesn't mean that the tool ran. |
773| Request | While a run is open | What to do |
774| ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
775| Archive or delete the session | Might return 400 while a run is open, whatever the session's status. The error's `error.details.error_code` can be `"workflow_run_open"`. Might also succeed. | Ask the agent to stop its runs, or wait until each run has ended. A paused run ends by itself only when its lifetime passes. Then send the request once the session is `idle`. An archive that succeeds ends each open run with `{"type": "stopped"}`. After an archive, a run's `workflow_run.status_ended`, and the `workflow_run.phase_ended` of a phase that was still open, don't arrive on the stream. List the session's events to read them. After a delete that succeeds, no `workflow_run` event reports the end of the session's runs. |
776| Archive one of a run's threads | Returns 400 with `error.details.error_code: "workflow_run_open"` while the run is open, running or idle, unless the server has already archived the thread. | Nothing. The server archives a run's threads. |
777| Update the session's `agent` (java, php; csharp, go: `Agent`) | Returns 400 with `error.details.error_code: "workflow_run_open"` while any run is open, even a paused one. Updating the underlying agent is still accepted, and the session keeps its own copy. A request that also sends other fields, such as `budget` (java, php; csharp, go: `Budget`), is rejected whole. | Wait until every run has its `workflow_run.status_ended`, or ask the agent to stop its runs. |
778| Answer a tool call or tool confirmation from a run's thread | Allowed. It arrives on the primary stream, and its `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) names the thread. | Answer as soon as the event arrives, passing the event's `id` (java, php; csharp, go: `ID`) as `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) or `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`). Don't wait for `session.status_idle`: the session can stay `running` while the run's other threads work. Once the server has archived the thread, a tool result for one of its calls has no effect, and it can return 400. When a tool result returns 400, find the call's thread in the thread list. If its status is `terminated`, the result came too late, so drop it. Send each tool result in a request of its own, because the server refuses a whole request when it refuses one of its events. A tool confirmation that comes too late returns 200, which doesn't mean that the tool ran. |
779779
780780### Rebuild run state after you reconnect
781781
782Rebuild each run's state from the session's events. The stream doesn't replay what you missed: a new connection delivers only events emitted after it opened. So list the events with a `types` filter, one `types[]` entry for each event type, as in [List past events](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#list-past-events). Pass each response's `next_page` as `page` until `next_page` is `null` or missing. `workflow_run.created`, `workflow_run.status_running`, `workflow_run.status_idle`, and `workflow_run.status_ended` give each run's state, except that a run paused after an interrupt might still show as running. `workflow_run.phase_started` and `workflow_run.phase_ended` rebuild progress. A run with no status event yet hasn't started to execute. No endpoint lists runs.
782Rebuild each run's state from the session's events. The stream doesn't replay what you missed: a new connection delivers only events emitted after it opened. So list the events with a `types` (java, php; csharp, go: `Types`) filter, one entry for each event type, as in [List past events](https://platform.claude.com/docs/en/managed-agents/events-and-streaming#list-past-events). Pass each response's `next_page` (java, php: `nextPage`; csharp, go: `NextPage`) as `page` (java, php; csharp, go: `Page`) until `next_page` (java, php: `nextPage`; csharp, go: `NextPage`) is `null` or missing. `workflow_run.created`, `workflow_run.status_running`, `workflow_run.status_idle`, and `workflow_run.status_ended` give each run's state, except that a run paused after an interrupt might still show as running. `workflow_run.phase_started` and `workflow_run.phase_ended` rebuild progress. A run with no status event yet hasn't started to execute. No endpoint lists runs.
783783
784784## Budgets and limits
785785
786786A run's model requests count toward the [session's budget](https://platform.claude.com/docs/en/managed-agents/budgets). A run has no price of its own. The tokens its agents use are billed like the session's other tokens, at each model's rates. For all of a session's charges, see [Claude Managed Agents pricing](https://platform.claude.com/docs/en/about-claude/pricing#claude-managed-agents-pricing).
787787
788* **One run's usage:** List the session's threads and add up the token counts in `usage` of the threads with the run's `workflow_run_id`. The list includes archived threads, whose status is `terminated`, so a finished run's threads are counted. Pass each response's `next_page` as `page` until `next_page` is `null` or missing, and skip a thread whose `usage` is `null`. If you add up the threads' `list_cost` instead, the total leaves out session runtime, and each figure is rounded separately.
788* **One run's usage:** List the session's threads and add up the token counts in `usage` (java, php; csharp, go: `Usage`) of the threads with the run's `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`). The list includes archived threads, whose status is `terminated`, so a finished run's threads are counted. Pass each response's `next_page` (java, php: `nextPage`; csharp, go: `NextPage`) as `page` (java, php; csharp, go: `Page`) until `next_page` (java, php: `nextPage`; csharp, go: `NextPage`) is `null` or missing, and skip a thread whose `usage` (java, php; csharp, go: `Usage`) is `null`. If you add up the threads' `list_cost` (java, php: `listCost`; csharp, go: `ListCost`) instead, the total leaves out session runtime, and each figure is rounded separately.
789789* **At the budget:** Every open run pauses, and the session reports `idle` with `budget_reached`, or `requires_action` if a tool call is also waiting. Each thread finishes the model request it already started, so a run can pass the budget by one request for each working thread. Raising or removing the budget resumes the runs it paused, unless an interrupt also paused them. If the session's usage includes a model with no list price, only removing the budget does; see [Models without a list price](https://platform.claude.com/docs/en/managed-agents/budgets#models-without-a-list-price).
790790
791791| Limit | Value | At the limit |
from line 795
795795| Run lifetime | 24 hours by default, or the lifetime the agent sets | The run ends with `timeout_error`. No event says what lifetime the agent set. |
796796| Runs open at once in a session | 10 by default | The server refuses to start another run. The agent's tool call gets an error, and you get a `workflow_run.error` whose `error.type` is `max_workflow_runs_error`. Idle runs count toward the limit. |
797797
798The server shortens a run's or phase's `name` to 64 characters and its `description` to 256. The server has other limits on workflows, and rules for them, that aren't listed here. What you see depends on when the server finds the problem:
798The server shortens a run's or phase's `name` (java, php; csharp, go: `Name`) to 64 characters and its `description` (java, php; csharp, go: `Description`) to 256. The server has other limits on workflows, and rules for them, that aren't listed here. What you see depends on when the server finds the problem:
799799
800800| What happens | What you see |
801801| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
No line in this hunk matches that.