> For the complete documentation index, see [llms.txt](https://docs.ibexa.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ibexa.ai/developers/rest-api/executions/attach-execution-stream.md).

# Attach Execution Stream

Watch an agent execution as it runs.

Emits `queued` while the execution is still waiting for a worker, then `started` once one has taken it and created its conversation, then the agent's own frames — any of `token` / `tool_call_start` / `tool_call_end` / `tool_call_error` / `mcp_auth_required` / `message` — and finally `done`, carrying the terminal `status` and `result`. So a client learns the outcome from the stream itself and needs no follow-up request.

Read-only, and deliberately weaker than triggering one: watching a run must not require the right to start one. It consumes no credits, starts nothing, and any number of clients may call it at once — a second browser tab, or the same client resuming after a dropped connection.

Send `Last-Event-ID` to resume. Every frame except the terminal one carries an `id` naming its position, and a reconnect carries on from the frame after it — no duplicate, no gap — through to `done`. That is what lets a client keep *appending* streamed output after a dropped connection rather than starting the run's output over. An id this API did not issue is ignored and the run replayed from its start.

The terminal `done` (and any `error`) carries an *empty* `id`, which resets the client's cursor: nothing follows it, so an auto-reconnecting client comes back without one instead of pinning itself to a run that has ended.

Always `200`. When there is nothing to serve — the run finished long enough ago that its stream has expired, or no worker picked it up while this connection waited — the single frame `event: no_active_run` is returned and the response ends, so a client needs no status-code handling to tell that apart from any other outcome. Fall back to `GET /executions/{execution_id}` there: the row is committed before the terminal frame is published, so by then the status and result are already readable.

```json
{"openapi":"3.1.0","info":{"title":"Ibexa Agentic Marketing Platform","version":"0.1.0"},"security":[{"OAuth2PasswordBearer":[]}],"components":{"securitySchemes":{"OAuth2PasswordBearer":{"type":"oauth2","flows":{"password":{"scopes":{},"tokenUrl":"/api/v1/login/access-token"}}}}},"paths":{"/api/v1/executions/{execution_id}/stream":{"get":{"tags":["executions","public"],"summary":"Attach Execution Stream","description":"Watch an agent execution as it runs.\n\nEmits `queued` while the execution is still waiting for a worker, then\n`started` once one has taken it and created its conversation, then the\nagent's own frames — any of `token` / `tool_call_start` / `tool_call_end` /\n`tool_call_error` / `mcp_auth_required` / `message` — and finally `done`,\ncarrying the terminal `status` and `result`. So a client learns the outcome\nfrom the stream itself and needs no follow-up request.\n\nRead-only, and deliberately weaker than triggering one: watching a run must\nnot require the right to start one. It consumes no credits, starts nothing,\nand any number of clients may call it at once — a second browser tab, or the\nsame client resuming after a dropped connection.\n\nSend `Last-Event-ID` to resume. Every frame except the terminal one carries\nan `id` naming its position, and a reconnect carries on from the frame after\nit — no duplicate, no gap — through to `done`. That is what lets a client keep\n*appending* streamed output after a dropped connection rather than starting\nthe run's output over. An id this API did not issue is ignored and the run\nreplayed from its start.\n\nThe terminal `done` (and any `error`) carries an *empty* `id`, which resets the\nclient's cursor: nothing follows it, so an auto-reconnecting client comes back\nwithout one instead of pinning itself to a run that has ended.\n\nAlways `200`. When there is nothing to serve — the run finished long enough ago\nthat its stream has expired, or no worker picked it up while this connection\nwaited — the single frame `event: no_active_run` is returned and the response\nends, so a client needs no status-code handling to tell that apart from any\nother outcome. Fall back to `GET /executions/{execution_id}` there: the row is\ncommitted before the terminal frame is published, so by then the status and\nresult are already readable.","operationId":"executions-attach_execution_stream","parameters":[{"name":"execution_id","in":"path","required":true,"schema":{"type":"integer","title":"Execution Id"}},{"name":"Last-Event-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last-Event-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"text/event-stream":{"itemSchema":{"type":"object","properties":{"data":{"type":"string"},"event":{"type":"string"},"id":{"type":"string"},"retry":{"type":"integer","minimum":0}}}}}},"401":{"description":"User not authenticated","content":{"application/json":{}}}}}}}}
```
