# Claude Managed Agents Events
Communication with a [[Claude Managed Agents|managed agent]] session is event-based, not request-response. You send **user events** (and optionally **system events**) to drive the agent; you receive **agent, session, and span events** back over a server-sent-event (SSE) stream. Every persisted event type is `{domain}.{action}`; the stream-only previews (`event_start`, `event_delta`) are the exception.
## User events (what you send)
| Type | Purpose |
|---|---|
| `user.message` | Text, image, or document content for the agent |
| `user.interrupt` | Stop the agent mid-execution |
| `user.custom_tool_result` | Respond to a `agent.custom_tool_use` call |
| `user.tool_confirmation` | Approve/deny an agent or MCP tool call that needs confirmation (`always_ask`, or `auto` when the server reaches no decision) |
| `user.define_outcome` | Set a rubric-graded goal (beta; research preview at launch) |
| `user.tool_result` | Self-hosted environments only: your integration returns built-in toolset results |
| `system.message` | Append system-level context for this and later turns (recent models only) |
## Agent events (what you receive)
| Type | Meaning |
|---|---|
| `agent.message` | Text content blocks (stream as they arrive) |
| `agent.thinking` | Progress signal only: the agent is thinking. Carries NO thinking content (the earlier version of this note said it did) |
| `agent.tool_use` / `agent.tool_result` | Built-in toolset calls and their results |
| `agent.mcp_tool_use` / `agent.mcp_tool_result` | MCP server tool calls |
| `agent.custom_tool_use` | Claude wants your client-side tool; reply with `user.custom_tool_result` |
| `agent.thread_context_compacted` | History was compacted to fit the context window |
| `agent.thread_message_sent` / `agent.thread_message_received` | [[Claude Managed Agents Multi-Agent\|Multiagent]] messages between threads |
`agent.tool_use` and `agent.mcp_tool_use` carry `evaluated_permission` (`allow`, `ask` or `deny`); only `ask` waits for you.
## Session and span events
| Type | Meaning |
|---|---|
| `session.status_running` / `session.status_idle` | Transitions between running and idle; idle carries a `stop_reason` (`end_turn`, `requires_action`, `budget_reached`...) |
| `session.status_rescheduled` / `session.status_terminated` | Transient retry / session ended (error or archive) |
| `session.updated` / `session.deleted` | Config change applied / session gone (stream ends) |
| `session.error` | Errors during processing, with a typed `error` and a `retry_status` |
| `session.usage` | Cumulative usage and list cost, with the budget echoed |
| `session.thread_created`, `session.thread_status_running` / `_idle` / `_rescheduled` / `_terminated` | Thread lifecycle (every session has a primary thread; [[Claude Managed Agents Multi-Agent\|multiagent]] sessions add more). The old `session.thread_idle` name is gone |
| `span.model_request_start` / `span.model_request_end` | Each model call; the end event has token counts |
| `span.outcome_evaluation_*` | [[Claude Managed Agents Outcomes\|Outcome]] grader progress |
## The send-then-stream pattern
~~The API buffers events until you attach a stream.~~ Wrong: only events emitted after the stream is opened are delivered, so open the stream BEFORE sending events to avoid a race. The usual flow is:
1. Create the session
2. Open the SSE stream
3. Send a `user.message`
4. Iterate events until `session.status_idle`
```python
with client.beta.sessions.events.stream(session.id) as stream:
client.beta.sessions.events.send(session.id, events=[
{"type": "user.message", "content": [{"type": "text", "text": "..."}]},
])
for event in stream:
match event.type:
case "agent.message":
for block in event.content:
print(block.text, end="")
case "agent.tool_use":
print(f"[tool: {event.name}]")
case "session.status_idle":
break
```
## Steering mid-execution
You can send more `user.message` events while the agent is running to add instructions without interrupting it; the agent folds them into its next turn. Use `user.interrupt` when you need to hard-stop and redirect (interrupt, then send a new `user.message`). On recent models, a `system.message` event appends privileged guidance mid-session, since the agent's `system` field is fixed for the session's lifetime.
## Tool confirmation flow
When a tool call needs confirmation (`always_ask`, or `auto` with no server decision), the session emits `session.status_idle` with `stop_reason.type: requires_action` and the blocking IDs in `stop_reason.event_ids`. For each one, reply with `user.tool_confirmation` (`tool_use_id`, `result: allow|deny`, optional `deny_message`). Once all are resolved, the session goes back to `running`. In [[Claude Managed Agents Multi-Agent|multi-agent]] sessions, echo the `session_thread_id` so the reply reaches the right subagent.
## Resuming streams
Event history is persisted server-side. If your SSE connection drops: open a new stream, list the full history to seed a set of seen event IDs, then tail the live stream and skip duplicates. Nothing persisted is lost, but stream-only previews (`event_deltas[]`) are never replayed.
## Previews (event deltas)
Opt in with the `event_deltas[]` stream parameter to receive `event_start` / `event_delta` previews as the model generates (for example, `agent.message` text while it's being written). They're never persisted. Each thread stream has its own previews.
## References
- https://platform.claude.com/docs/en/managed-agents/events-and-streaming
- Event types reference: https://platform.claude.com/docs/en/managed-agents/reference#event-types
- Permission policies: https://platform.claude.com/docs/en/managed-agents/permission-policies
## Related
- [[Claude Managed Agents]]
- [[Claude Managed Agents Sessions]]
- [[Claude Managed Agents Tools]]
- [[Claude Managed Agents Multi-Agent]]
- [[Claude Managed Agents Outcomes]]