# Streaming Event Object

Represents individual Server-Sent Events (SSE) streamed when `stream: true`. Each event is a JSON object on a `data:` line and carries an `event` field naming its type. Clients filter on the `event` name to drive UI. The stream always ends with a terminal `data: [DONE]` line.

## Reading the stream

Every event is delivered as one SSE frame:


```
data: {"event":"response.content_delta","delta":"Hello"}
```

A response stream proceeds roughly as: `response.processing` → `response.created` → (`response.context`) → content/tool/reasoning events → `response.completed` (or `response.cancelled` / `response.error`) → `data: [DONE]`.

## Event Types

### Lifecycle Events

**`response.processing`**

The very first event, emitted immediately as the stream opens (before setup completes) so clients can show activity right away.

details
summary
Show properties
**`event`** string - Always `response.processing`

**`status`** string - Always `processing`

**`thread_id`** string - Thread identifier

**`message`** string - Human-readable setup message

**`response.created`**

Emitted once after setup completes (authentication, organization resolution, turn lock). Marks the real start of generation.

details
summary
Show properties
**`event`** string - Always `response.created`

**`status`** string - Always `in_progress`

**`thread_id`** string - Thread identifier

**`user_id`** string - Authenticated user ID

**`org_id`** string - Organization ID

**`user_message_id`** string - ID of the stored user message

**`trace_id`** string - Request trace ID

**`activity_labels`** boolean - Whether activity labels are active for this turn, after resolving both the request's `activity_labels` parameter and the deployment setting. `true` means events on this stream may carry `activity`; `false` means none will. Always present, including when the request did not ask for labels

**`assistant_id`** string optional - Assistant ID if the thread has one

**`response.context`**

Emitted after `response.created` with the resolved request context (organization, timezone, location) used for grounding the response.

details
summary
Show properties
**`event`** string - Always `response.context`

**`user_id`**, **`org_id`**, **`org_name`** string - Identity context

**`timezone`**, **`country`**, **`location_source`** string - Resolved locale context

**`profile_timezone`**, **`detected_timezone`**, **`suggested_timezone`** string optional - Timezone resolution detail

**`timezone_mismatch`** boolean optional - Whether the profile and detected timezones differ

**`response.heartbeat`**

Sent during long pauses (tool execution, reasoning) to keep the connection open. It carries no response content, but it may carry an `activity` label.

Two independent cadences produce it, and a client does not need to tell them apart:

- **Connection keepalive** — after about 15 seconds with no frame of any kind. Carries `event` and `status` only.
- **Label refresh** — every 4 seconds that the visible activity label has been unchanged, on turns where `activity_labels` is `true` (see `response.created`). Carries an `activity` object describing what the turn is doing right now. A refresh that would repeat the label already on screen is suppressed, so identical labels are not re-sent.


Render `activity` when it is present and keep the last one when it is not — the same rule as every other event that may carry a label. Every `activity` object carries a `locale` field naming the language tag of its `text`; a label may be in another language than your app when the person wrote in that language, which is correct. A heartbeat never arrives after `[DONE]`.

details
summary
Show properties
**`event`** string - Always `response.heartbeat`

**`status`** string - Always `in_progress`

**`activity`** object optional - The turn's current activity label. Present only on label-refresh heartbeats; absent on connection keepalives and on turns with `activity_labels: false`. Carries **`locale`** (string), the canonical tag its `text` is written in (`en-GB`, `de-DE`, `de-CH`, `de-AT`, `de-AT-x-ziller`, `fr-CH` or `es-ES`): use it to set the `lang` attribute of the element that shows the label, never to decide whether to show it, and never translate `text`

**`response.completed`**

Emitted when the response finishes successfully. The final, content-bearing event before `[DONE]`.

details
summary
Show properties
**`event`** string - Always `response.completed`

**`status`** string - `completed`, or `awaiting_approval` when a tool call needs user approval

**`thread_id`** string - Thread identifier

**`response_id`** string - Unique response identifier

**`user_message_id`** string - ID of the stored user message

**`response`** array - Final response content blocks

**`stop_reason`** string - Why generation stopped (e.g. `end_turn`)

**`is_summarized`** boolean - Whether conversation history was summarized

**`response_blocks`** array optional - Structured blocks (in `structured` mode or when tools are used)

**`skin_degrades`** array optional - Skin kinds whose content could not be drawn and was replaced with a neutral fallback card. Present **only** when something degraded; omitted entirely otherwise, so its presence always means the answer is missing something the model produced. Each entry: **`kind`** string (the skin kind, or `unknown` for a kind that is not one of ours) and **`count`** integer (how many tokens of that kind degraded this turn), ordered by `count` descending. The same array appears on the non-streaming response body — see the [Response object](/docs/api-reference/objects/response-object)

**`client_context`** object optional - What the server did with the `client_context` manifest this request sent. Present only for a request that sent one; identical to the `client_context` payload on the earlier `response.client_context` event, which is the one worth reading — it arrives before the answer does

**`assistant_id`**, **`reasoning_content`**, **`usage`**, **`context_window`**, **`performance`**, **`annotations`**, **`invoked_skills`**, **`loaded_skins`**, **`skin_catalog`**, **`task_list`**, **`tools`**, **`available_tools`**, **`rules`**, **`system_messages`** optional - Present depending on the request's `include` parameter and what the turn produced

**`response.cancelled`**

Emitted when an in-progress stream is cancelled via [Cancel stream](/docs/api-reference/responses/cancel). Followed by `[DONE]`. `response.completed` is not emitted on a cancelled turn.

details
summary
Show properties
**`event`** string - Always `response.cancelled`

**`status`** string - Always `cancelled`

**`thread_id`** string - Thread identifier

**`partial_content_length`** integer - Characters generated before cancellation

**`response.error`**

Emitted when an error occurs during generation. Followed by `[DONE]`.

details
summary
Show properties
**`event`** string - Always `response.error`

**`error`** object optional - Standard error envelope (`code`, `message`, `system_message`, `type`, `status`, `details`, `trace_id`, `timestamp`). Some error paths emit these fields at the top level instead of nested.

### Content Events

**`response.content_delta`**

Emitted for each chunk of generated text.

details
summary
Show properties
**`event`** string - Always `response.content_delta`

**`delta`** string - Text chunk

**`task_list_id`** string optional - Present when the delta is task-list markup

**`response.output_text.delta`**

Provider-passthrough text delta (see [Provider passthrough events](#provider-passthrough-events)). Carries an incremental text fragment forwarded verbatim from the upstream model stream.

**`response.block`**

Emitted for a structured response block that has no dedicated event (fallback). Rarely seen.

details
summary
Show properties
**`event`** string - Always `response.block`

**`block`** object - The raw block object (e.g. `text`, `tool_call`, `tool_result`). Tool call blocks may include an optional `icon_url` string with the tool's brand icon.

### Reasoning Events

**`response.reasoning.started`**

Emitted once when the model enters its reasoning phase.

details
summary
Show properties
**`event`** string - Always `response.reasoning.started`

**`status`** string - Always `in_progress`

**`response.reasoning.delta`**

Emitted for each chunk of reasoning content (models that stream thinking in real time).

details
summary
Show properties
**`event`** string - Always `response.reasoning.delta`

**`delta`** string - Reasoning text chunk

**`response.reasoning.completed`**

Emitted when the reasoning phase completes. Includes the full reasoning content.

details
summary
Show properties
**`event`** string - Always `response.reasoning.completed`

**`status`** string - Always `completed`

**`reasoning_content`** string - Complete reasoning text

### Tool & Connector Events

**`response.tool.started`**

A tool/connector invocation begins.

details
summary
Show properties
**`event`** string - Always `response.tool.started`

**`id`** string - Tool call ID

**`type`** string - Always `mcp_tool`

**`name`** string - Tool name

**`status`** string - Always `in_progress`

**`input`** object - Tool arguments

**`tool_group`** object optional - Stable grouping identity `{id, label}`. Consecutive tool calls sharing `tool_group.id` form one capability rail; absence is a deliberate group break

**`response.tool.progress`**

A long-running tool is still executing (emitted about every 3 seconds, e.g. image generation).

details
summary
Show properties
**`event`** string - Always `response.tool.progress`

**`id`**, **`name`** string - Tool identity

**`type`** string - Always `mcp_tool`

**`status`** string - Always `in_progress`

**`elapsed_ms`** integer - Elapsed execution time

**`response.tool.completed`**

A tool finished executing (raw completion marker).

details
summary
Show properties
**`event`** string - Always `response.tool.completed`

**`id`**, **`name`** string - Tool identity

**`type`** string - Always `mcp_tool`

**`status`** string - Always `completed`

**`input`** object - Tool arguments

**`output`** object - Tool result

**`response.tool.done`**

The finalized tool result, including success/failure and timing.

details
summary
Show properties
**`event`** string - Always `response.tool.done`

**`id`**, **`name`** string - Tool identity

**`type`** string - Always `mcp_tool`

**`status`** string - `completed` or `failed`

**`success`** boolean - Whether the tool succeeded

**`execution_time_ms`** integer - Execution duration

**`error`** string optional - Error detail when failed

**`result`**, **`result_metadata`**, **`auth_failed`**, **`tool_key`** optional - Additional result context

**`tool_group`** object optional - Stable grouping identity `{id, label}`, identical to the one on `response.tool.started` and on the persisted tool-call block, so a reloaded thread groups exactly like the live stream

**`response.function_call`**

A function-tool invocation (user/LLM-callable function, distinct from server-side tools).

details
summary
Show properties
**`event`** string - Always `response.function_call`

**`tool_call_id`** string - Call ID

**`name`** string - Function name

**`arguments`** object - Call arguments

**`response.connector.auth_required`**

A connector's authorization is expired or invalid; the user must reconnect. Emitted alongside the failed tool's `response.tool.done`.

details
summary
Show properties
**`event`** string - Always `response.connector.auth_required`

**`tool_key`** string - Connector identifier

**`tool_name`** string - Tool that failed

**`tool_call_id`** string - Call ID

**`message`** string - Explanation

**`action_name`**, **`icon_url`** string optional - Display detail

### Entity Change Events

**`entity.changed`**

One entity the turn changed, emitted the moment the tool call that changed it finishes — not at the end of the turn. A turn that moves five people emits five events while it is still streaming, so a page can update each row as it happens instead of jumping once at the end.

Only writes emit. A read emits nothing, and so does a write whose response did not actually name what it changed: an event carrying the wrong id is worse than no event, because a client would apply it to the wrong row and nothing on either side would fail. Absence means "we could not say", never "nothing changed".

When one call changes several entities, you get one event per entity, each with its own `seq`.

The same events are stored on the turn's tool call, so a client that reconnects after the live buffer has expired still receives them — with `replay` set to `true`.

details
summary
Show properties
**`event`** string - Always `entity.changed`

**`v`** integer - Contract version. Currently `1`.

**`kind`** string - What changed, for example `user`, `department`, `role`, `space`, `knowledge_store`, `file`, `member`, `invitation`, `grant`, `slice`, `source`, `connection`.

**`id`** string - The changed entity's id.

**`verb`** string - One of `created`, `updated`, `deleted`.

**`parent`** object optional - `{ kind, id }` of the entity this one belongs to, when the change only makes sense in context — a file inside a knowledge store, a member inside a department. `null` when there is no parent.

**`tool_call_id`** string - The tool call that produced the change, matching the `response.tool.*` events for the same call.

**`turn_id`** string optional - Groups every event from one turn. Ordering across turns is not defined.

**`seq`** integer - Position within the turn, from `1`. Apply events in this order.

**`replay`** boolean - `true` when the event is being re-delivered from the stored turn rather than emitted live.

**`ts`** string - ISO-8601 timestamp.


```json
{
  "event": "entity.changed",
  "v": 1,
  "kind": "department",
  "id": "dept_9c41e2f0a7b04d1e8f3c2a6b5d4e7c81",
  "verb": "updated",
  "parent": null,
  "tool_call_id": "call_8f2b1c",
  "turn_id": "turn_4d1e8f3c2a6b5d4e",
  "seq": 1,
  "replay": false,
  "ts": "2026-08-02T09:14:22Z"
}
```

### Web Search & Research Events

**`response.web_search.started`**

A web search is initiated.

details
summary
Show properties
**`event`** string - Always `response.web_search.started`

**`id`** string - Search ID

**`type`** string - Always `web_search`

**`name`** string - Always `web_search`

**`status`** string - Always `searching`

**`input`** object - The query

**`response.web_search.searching`**

A per-query update while results are being retrieved. Same payload shape as `response.web_search.started` (`event`, `id`, `type`, `name`, `status`, `input`).

**`response.web_search.page_fetch.started`**

Deep-search only — fetching the full content of one result page.

details
summary
Show properties
**`event`** string - Always `response.web_search.page_fetch.started`

**`id`** string - Search ID

**`type`** string - Always `web_search`

**`name`** string - Always `web_search`

**`status`** string - Always `fetching`

**`url`**, **`title`** string - Page being fetched

**`page_index`**, **`page_total`** integer optional - Progress counters

**`response.web_search.page_fetch.completed`**

Deep-search only — a page fetch finished.

details
summary
Show properties
**`event`** string - Always `response.web_search.page_fetch.completed`

**`id`** string - Search ID

**`type`** string - Always `web_search`

**`name`** string - Always `web_search`

**`status`** string - Always `fetched`

**`url`**, **`title`** string - The page

**`ok`** boolean - Whether the fetch succeeded

**`duration_ms`** integer - Fetch duration

**`page_index`**, **`page_total`**, **`bytes`**, **`engine`**, **`truncated`**, **`reason`**, **`error`** optional - Additional fetch detail

**`response.web_search.completed`**

All search results retrieved.

details
summary
Show properties
**`event`** string - Always `response.web_search.completed`

**`id`** string - Search ID

**`type`** string - Always `web_search`

**`name`** string - Always `web_search`

**`status`** string - Always `completed`

**`input`** object - Original query

**`output`** object - Results

**`result_metadata`** object optional - Enriched citation metadata

**`response.deep_research.status`**

A phase update during a multi-phase deep-research run.

details
summary
Show properties
**`event`** string - Always `response.deep_research.status`

**`phase`** string - Current research phase

Additional phase-specific fields are passed through alongside `phase`.

### Knowledge (RAG) Events

**`response.rag_search.completed`**

A knowledge (vector store) search finished. This is the *automatic* retrieval that runs while the turn is being prepared, before the first content event — so it arrives already complete and there is no matching "started" event for it.

details
summary
Show properties
**`event`** string - Always `response.rag_search.completed`

**`status`** string - Always `completed`

**`store_count`** integer optional - Knowledge stores queried

**`source_count`** integer optional - Documents retrieved

**`stores`** array optional - The knowledge stores this search covered, as objects with **`id`** (string, `ks_` prefixed) and **`name`** (string, the store's display name). `store_count` is this array's length, so the summary sentence and the list behind it always agree. Present only when the store names could be resolved: **absent means "unknown", never "none"** — on that turn `store_count` still reports the search, so show the count without an expandable list rather than an empty one. Internal thread-memory stores are excluded from both the array and the count. The same list appears as `searched_knowledge_stores` on `response.completed`, on the non-streaming response body, and on the assistant message's `message_metadata` (so it survives a reload)

**`activity`** object optional - A label for this retrieval, ready to render as-is (for example `Searched 4 of your knowledge stores`). Because the search is already finished when this event is sent, the wording is past tense and `stage` is `progress_fact` — unlike every other `activity` on the stream, it describes work that is over rather than work in flight, so do **not** hold it as the turn's current status. Present only when the turn has activity labels on (see `activity_labels` on `response.created`) **and** `store_count` is present; absent otherwise. Carries **`locale`** (string), the canonical tag its `text` is written in (`en-GB`, `de-DE`, `de-CH`, `de-AT`, `de-AT-x-ziller`, `fr-CH` or `es-ES`): use it to set the `lang` attribute of the element that shows the label, never to decide whether to show it, and never translate `text`

**`response.knowledge_sources`**

The knowledge sources the answer drew on — "where did this knowledge come from?". Emitted once (near `response.completed`) only when the response used retrieved knowledge. The same `knowledge_sources` array also appears on the `response.completed` event and on the non-streaming response body.

details
summary
Show properties
**`event`** string - Always `response.knowledge_sources`

**`knowledge_sources`** array - One entry per distinct source (a file in a knowledge store), deduplicated across passages, with `store_id` (`ks_`), `store_name`, `file_id`, `file_name`, `best_score`, `hit_count`, and a `chunks` array of the matched passages. See the [Response object](/docs/api-reference/objects/response-object) for the full field list.

**`knowledge_citations`** array optional - Present only when inline citations are enabled and the answer contains `[n]` markers; each maps a marker to `knowledge_sources[index - 1]` with character offsets.

`response.completed` additionally carries **`searched_knowledge_stores`** — the stores the turn *searched*, as `{ "id": "ks_…", "name": "…" }` entries. It is independent of `knowledge_sources`: a search that matched nothing produces this list and no sources. Unlike the `stores` array on `response.rag_search.completed`, which describes that one automatic retrieval, this is the whole turn — it also covers searches the model invoked itself, so it can be the longer list. See the [Response object](/docs/api-reference/objects/response-object#searched_knowledge_stores).

### Image Events

**`response.image_analysis.started`**

Image analysis (vision) begins.

details
summary
Show properties
**`event`** string - Always `response.image_analysis.started`

**`status`** string - Always `analyzing`

**`images`** array - Images being analyzed

**`count`** integer - Number of images

**`response.image.partial`**

A progressive preview frame during image generation (zero or more before the final image).

details
summary
Show properties
**`event`** string - Always `response.image.partial`

**`id`** string - Tool call ID

**`type`** string - Always `mcp_tool`

**`name`** string - Always `generate_image`

**`partial_index`**, **`partial_count`** integer - Preview frame position

**`b64`** string - Base64-encoded preview image

**`content_type`** string - Image MIME type (e.g. `image/png`)

**`status`** string - Always `in_progress`

**`width`**, **`height`** integer optional - Preview dimensions

### Document, Skin, Skill & Task Events

**`response.artifact_created`**

A document/artifact was created (e.g. by the `create_document` tool). Fires alongside `response.tool.done`.

details
summary
Show properties
**`event`** string - Always `response.artifact_created`

**`artifact_type`** string - Always `document`

**`item_id`**, **`smoc_entity_id`**, **`team_space_id`**, **`thread_id`**, **`tool_call_id`** string - Artifact identity

**`artifact_token`** string optional - Access token for the artifact

**`response.artifact_opened`**

An existing document was **resolved** by a read tool (e.g. `smoc_fetch_item`) — "open the last thing I was working on". Same job as `response.artifact_created`: open the document pane deterministically, without scanning the assistant's reply text for an `{{artifact | … }}` token.

A separate event rather than a reused one, because `response.artifact_created` announces a **new** document. Firing it for a read would put a "new document" chip in the conversation for a document the user already had.

details
summary
Show properties
**`event`** string - Always `response.artifact_opened`

**`artifact_type`** string - Artifact pillar, e.g. `document`

**`item_id`**, **`smoc_entity_id`**, **`team_space_id`**, **`thread_id`**, **`tool_call_id`** string - Artifact identity

**`title`** string optional - Title as resolved, so the pane can be labelled before the artifact loads

**`artifact_token`** string optional - Access token for the artifact

**`response.workspace_layout`**

The assistant rearranged the window — which side this chat occupies and what is open beside it — via the `arrange_workspace` tool.

The arrangement is also **persisted** on the thread (`thread_user_state.workspace_layout`), so it survives a refresh and a trip to another thread. This event is the live half, so the panel moves on this turn rather than on the next thread open. `layout` is the same object `GET /v1/threads/{thread_id}/user-state` returns, so a client handles one shape live and on restore.

Not part of the assistant's answer, so it is deliberately absent from `response.completed` — replaying a transcript must not re-arrange the user's window.

details
summary
Show properties
**`event`** string - Always `response.workspace_layout`

**`layout`** object - `{ dock, pane, panes, set_at, set_by }`. `dock` is `left`, `right` or `null` (full-width chat); `panes` is everything open beside the chat, **nearest the chat first** (up to 4); `pane` mirrors `panes[0]` and is `{ kind, item_id, title }` or `null`; `set_by` is `assistant` here. Render `panes` if you support more than one; a client that only has room for one can read `pane` alone and be correct. Re-resolve each `item_id` before opening it — access is re-checked on restore, never re-granted by having been stored.

**`thread_id`**, **`tool_call_id`** string - Which conversation was arranged, and by which call

**`response.message.boundary`**

Marks where one assistant message of a turn ends and the next begins.

A turn that calls tools is not one assistant message — it is several. The model speaks, the platform runs the tools it asked for, the model speaks again, and each of those is a separate message (they are stored as separate messages on the thread, and `GET /v1/threads/{thread_id}/messages` returns them that way after the fact). While the turn is live, every one of them streams through the same `response.content_delta` sequence. Without a boundary a client appending deltas verbatim renders the last sentence of one message run into the first of the next — *"…so I can create that slice for you.Done — your new slice has been created"*.

The split cannot be recovered from the text. A tool invocation is **not** a boundary: text, a tool card, then more text is routinely one message continuing, and a tool call frequently lands mid-sentence. Any rule over the delta text ("break before a capital following a full stop") corrupts that case. So the boundary is stated rather than inferred.

- Sent **between** two messages only — never before the first, never after the last.
- A round that runs tools and says nothing produces no message and therefore no boundary.
- **Purely additive**: it carries no content, so a client that ignores it reassembles byte-identical content from `response.content_delta` alone. Nothing is injected into the text, so there is nothing to strip before persisting.


details
summary
Show properties
**`event`** string - Always `response.message.boundary`

**`index`** integer - 0-based ordinal of the message that is *starting*. The first boundary in a turn is therefore always `index: 1`. It counts messages that actually carried text, so it is a stable key for a client's live message list — not a tool-call or round counter.

Example:


```json
{"event":"response.content_delta","delta":"I'll load the organization management tools."}
{"event":"response.tool.started","name":"load_tools"}
{"event":"response.tool.done","name":"load_tools"}
{"event":"response.message.boundary","index":1}
{"event":"response.content_delta","delta":"Done — the slice has been created."}
```

**`response.skin_token.pending`** / **`response.skin_token.progress`** / **`response.skin_token.ready`**

The lifecycle of a single inline visual token (a chart, a table, a task list) while it is being generated.

A visual is emitted inside the answer text as a `{{kind | key::value | …}}` token. Its payload can be large — a fifty-row list is thousands of characters — so the platform holds the token back until it is complete and delivers it as **one** `response.content_delta`, rather than letting the raw syntax type itself out in the message. These three events tell the client what is coming while that happens, so it can show a placeholder for the real component instead of a blank gap:

- **`pending`** — a token has started and its kind is known. Put up a skeleton. Carries the token's opening bytes in `partial`.
- **`progress`** — the payload is still growing. Sent roughly every 400 characters, each carrying the newly generated bytes in `partial`.
- **`ready`** — the token is complete and verified; the `response.content_delta` carrying it is sent immediately after. Retire the skeleton and discard every accumulated `partial`.


A `ready` is always sent for a token that sent a `pending`, including when the stream ends mid-token — a skeleton is never left open.

These events are **purely additive**. All of the answer's bytes still arrive through `response.content_delta`, so a client that ignores `response.skin_token.*` entirely reassembles identical content.

details
summary
Show properties
**`event`** string - One of `response.skin_token.pending`, `response.skin_token.progress`, `response.skin_token.ready`

**`skin_kind`** string | null - The token's kind (for example `task_list`, `bar_chart`), when the platform recognises it. `null` means the kind is not a published one — show a generic placeholder.

**`index`** integer - 1-based position of this token within the response. `pending`, `progress` and `ready` for the same token share an `index`.

**`chars`** integer - Size in characters of the token's text so far (on `ready`, of the final token).

**`repaired`** boolean optional - `ready` only. `true` when the platform corrected or replaced the token before sending it, so the delivered text differs from what the model wrote.

**`partial`** string optional - `pending` and `progress` only. An **unverified preview** of the token's bytes: `pending` carries the opening bytes, each `progress` carries the bytes generated since the previous event, so appending them in order rebuilds the token so far. Use it to fill the skeleton progressively (rows of a table, items of a list). It has NOT been verified or repaired — the platform may still rewrite the token, including its `skin_kind` — so **discard every accumulated `partial` the moment `ready` arrives** and render only the token delivered in the following `response.content_delta`. Never persist `partial` bytes.

Example:


```json
{"event":"response.skin_token.pending","skin_kind":"task_list","index":1,"chars":22,"partial":"{{task_list | title::S"}
{"event":"response.skin_token.progress","skin_kind":"task_list","index":1,"chars":1622,"partial":"…next 400+ chars…"}
{"event":"response.skin_token.ready","skin_kind":"task_list","index":1,"chars":4448,"repaired":false}
```

**`response.skin_activated`**

A page skin (theme/template) was activated on the thread.

details
summary
Show properties
**`event`** string - Always `response.skin_activated`

**`thread_id`**, **`kind`**, **`activated_by`**, **`source`**, **`activated_at`** - Activation context

**`response.skin_loaded`**

A page skin became available for rendering.

details
summary
Show properties
**`event`** string - Always `response.skin_loaded`

**`thread_id`**, **`skin_id`**, **`skin_name`**, **`scope`** - Skin identity

**`entry_type`** string - Always `skin`

**`response.skill_loaded`**

A skill/behavior was loaded into the response.

details
summary
Show properties
**`event`** string - Always `response.skill_loaded`

**`skill_id`**, **`skill_name`**, **`display_name`**, **`scope`**, **`thread_id`** - Skill identity

**`response.task.created`**

A live task checklist was created.

details
summary
Show properties
**`event`** string - Always `response.task.created`

**`thread_id`** string - Thread identifier

**`task_list`** object - The new task list (id, title, items, `completed` boolean)

**`response.task.updated`**

A single task's status changed.

details
summary
Show properties
**`event`** string - Always `response.task.updated`

**`thread_id`** string - Thread identifier

**`list_id`** string - Task list ID

**`task`** object - The updated task

**`list_completed`** boolean - True when this update finished the WHOLE list (every task reached a terminal status). Collapse the live checklist on this signal; the anchor message id follows on `response.completed` as `task_list.completed_message_id`

**`response.task.snapshot`**

A full snapshot of the task list's current state. Also emitted as a terminal snapshot if the stream errors mid-task-list.

details
summary
Show properties
**`event`** string - Always `response.task.snapshot`

**`thread_id`** string - Thread identifier

**`task_list`** object - Full task list (includes `completed` boolean)

**`terminal`** boolean optional - Present (true) only on the error path

**`response.task_gate.continuation`**

Emitted when the assistant produced a final answer while its task list still had unfinished tasks and the platform is continuing the run to complete them. The streamed text simply keeps flowing after this event; clients may render a subtle separator (for example, "continuing remaining tasks…") at the current text position.

details
summary
Show properties
**`event`** string - Always `response.task_gate.continuation`

**`continuation`** integer - 1-based index of this continuation within the turn

**`max_continuations`** integer - Maximum continuations the platform will force per turn

**`incomplete_tasks`** integer - Number of unfinished tasks at the exit attempt

**`list_ids`** array of strings - The task list identifier(s) with unfinished tasks

### Summary & Metadata Events

**`response.summary`**

Emitted before `response.completed` only when conversation history was summarized for this turn.

details
summary
Show properties
**`event`** string - Always `response.summary`

**`summary`** string - The summary text

**`messages_summarized`**, **`messages_kept`**, **`original_token_count`**, **`optimized_token_count`** integer - Summarization stats

**`usage`** object - Token usage for the summarization itself

**`response.performance`**

Emitted once when the first content token arrives, carrying the time-to-first-token measurement.

details
summary
Show properties
**`event`** string - Always `response.performance`

**`ttft_ms`** number - Time to first token, in milliseconds. Measured from the **start of the model call that produced the text**, not from when the request arrived — on a multi-round turn the anchor resets at each model-call boundary, so this excludes tool execution and any earlier tool-only rounds.

**`reasoning_ttft_ms`** number or null - Milliseconds from **request arrival** to the first reasoning delta. `null` when the model produced no reasoning. A large `ttft_ms` with a small value here is a model *thinking*; the same `ttft_ms` with `null` here is a model that sent nothing at all — `ttft_ms` alone cannot tell the two apart, because a reasoning delta is not assistant output and so does not close it.

**`trace_id`** string - Identifies this turn. Quote it when reporting a slow or failed turn; it is the key the server uses to look the turn up.

The three remaining request-anchored measurements — `visible_ttft_ms`, `provider_ttft_ms` and `gate_hold_ms` — are not on this event, because the first readable frame has not been sent yet when it fires. They arrive on the `performance` object of `response.completed`:

| Field | Measures | `null` when |
|  --- | --- | --- |
| `visible_ttft_ms` | Request arrival → the first frame the user could actually read. **The user-visible latency**, and the one to alert on. | Nothing was ever readable, or request-arrival time was unavailable |
| `provider_ttft_ms` | Request arrival → the first chunk carrying assistant bytes | The model never produced assistant bytes |
| `gate_hold_ms` | First provider byte → first readable frame — server-side buffering, not model time | The model never produced assistant bytes |
| `trace_id` | Identifies the turn (same value as on `response.performance`) | never |


Every one of these is `null` — never `0` — when it was not measured: `0` would mean "it took no time", which is a different fact.

**`response.annotations`**

Emitted before `response.completed` only when generated images were annotated by relevance.

details
summary
Show properties
**`event`** string - Always `response.annotations`

**`annotations`** array - Annotation objects with image metadata

#### Turn-metadata events

Four events describe the turn's *setup* — the tools the model was offered, the system messages it was sent, and the skins available to it. They are emitted early, immediately after `response.context` and **before the first `response.content_delta`**, so a client can render a tool inspector or prime its skin renderer while the answer is still streaming.

Every one of these fields is **also** present on `response.completed` today, carrying an identical value — with one deliberate exception, the optional per-entry `tokens` count on `available_tools` and `system_messages`. Computing it means running a tokenizer over the whole catalog and the whole system prompt, and these events fire *before the first token*, so that work would be paid straight out of your time-to-first-token. It is done once the answer is complete instead, and `tokens` therefore appears **only on `response.completed`**. Everything else, `chars` included, is on both carriers.

Read them from whichever carrier suits you; a client that ignores these events loses nothing. A future release will drop them from `response.completed` — at that point these events become the only carrier for everything except `tokens`, so prefer them in new code.

**`response.available_tools`**

The resolved tool catalog the model was given for this turn: what it was *offered*, as opposed to `tool_executions[]`, which is what it *ran*. Omitted entirely when the pipeline captured no offered-tool snapshot.

details
summary
Show properties
**`event`** string - Always `response.available_tools`

**`thread_id`** string - The thread this turn belongs to

**`available_tools`** object - The offered tool catalog; identical to `available_tools` on `response.completed` except that per-tool `tokens` is carried only there

**`response.system_messages`**

The truthful transcript of every `role: "system"` message the provider actually received for this turn, with per-source attribution and a per-message `chars` count. Per-message `tokens` is on the `response.completed` copy only (see above). Omitted when the turn recorded none.

details
summary
Show properties
**`event`** string - Always `response.system_messages`

**`thread_id`** string - The thread this turn belongs to

**`system_messages`** array - The system-message transcript; identical to `system_messages` on `response.completed` except that per-message `tokens` is carried only there

**`response.skin_catalog`**

The *lazy* skins available to this turn — the ones the model may request with `load_skin` but which were not injected into the system prompt. Omitted when the turn offers none.

details
summary
Show properties
**`event`** string - Always `response.skin_catalog`

**`thread_id`** string - The thread this turn belongs to

**`skin_catalog`** array - Unloaded catalog entries (`id`, `name`, `description`, `loaded: false`, `scope`); identical to `skin_catalog` on `response.completed`

**`response.loaded_skins`**

The skins already injected into this turn's system prompt. The counterpart of `response.skin_catalog` — the same catalog, split on `loaded`. Omitted when no skin was auto-loaded.

details
summary
Show properties
**`event`** string - Always `response.loaded_skins`

**`thread_id`** string - The thread this turn belongs to

**`loaded_skins`** array - Loaded catalog entries (`id`, `name`, `description`, `loaded: true`, `scope`); identical to `loaded_skins` on `response.completed`

**`response.client_context`**

What the server did with the `client_context` manifest this request sent. Emitted only for a request that sent one — never on an ordinary turn — and always before the first `response.content_delta`, so a client can tell the reader that the answer is coming from the sent selection alone rather than from the document itself. The same object is repeated on `response.completed` as `client_context`, for a reader that reconnected past this event.

A manifest can be dropped without any error: the turn still returns `200` and answers from what the request pasted into it. This event is how that is distinguishable from a turn whose manifest was honoured.

details
summary
Show properties
**`event`** string - Always `response.client_context`

**`thread_id`** string - The thread this turn belongs to

**`client_context`** object - What the manifest was honoured as

- **`honored`** string - `panel` (used in full), `document_identity` (used to identify the open document only — no screen capability was offered on this turn), or `ignored` (dropped; the turn ran as though no manifest was sent)
- **`surfaces_sent`** integer - How many surfaces the request's manifest carried
- **`reason`** string - Present only when `honored` is `ignored`. `copilot_panel_off` means the caller has the pinned copilot switched off


**`response.model_selected`**

Which model Auto picked to answer, at what effort and speed, and why. Sent only for a request with `model: "auto"`, exactly once per turn, before the first `response.content_delta`. A turn with a concrete model never gets one. The same choice is readable later from `GET /v1/threads/{thread_id}/messages/{message_id}/route`, which also carries the runner-up, the cost and the time to the first word.

details
summary
Show properties
**`event`** string - Always `response.model_selected`

**`thread_id`** string - The thread this turn belongs to

**`response_id`** string - The turn this choice answers

**`route_id`** string - The routing decision (`ard_` id); the same id the route read answers for

**`model`** string - The model key Auto picked

**`model_title`** string | null - Its customer-facing name

**`effort`** string - `off`, `low`, `medium`, `high` or `max`

**`speed_tier`** string - `standard` or `priority`

**`basis`** string - How the pick was made: `jev` (Auto read the message), `heuristic` (a quick read, used when the full read was not available in time), `sticky` (the same model as the previous reply of the thread) or `retry` (a retry asked for a smarter or faster answer)

**`priority`** object - The priority Auto routed with

- **`preset`** string - `balanced`, `faster`, `lower_cost`, `best_quality` or `custom`
- **`set_by`** object - The rule that set it: **`layer`** (`platform`, `organization`, `department`, `assistant`, `person` or `chat`) and **`name`** (its display name)


**`summary`** string - One line on the message in the person's language (like `set_by.name` for the built-in layers), e.g. `Coding, hard, needs careful reasoning`

**`retry_exhausted`** boolean - True when this turn was a retry that found nothing stronger or faster allowed

See [Auto Model](/docs/documentation/core-concepts/auto-model) and the [Auto model route object](/docs/api-reference/objects/auto-model-route-object).

:::note Reconnect
A reconnect that supplies `last_event_id` replays only the events *after* that cursor, so a client resuming mid-turn will not see these four again. Until they are removed from `response.completed`, that frame remains the fallback carrier.
:::

### Provider passthrough events

When the request routes to a provider whose native stream is forwarded transparently, a small family of provider-native events is passed through verbatim. They mirror the upstream provider's deltas and do not need separate handling — clients that only consume Aitronos events can ignore them:

- `response.output_item.added`, `response.output_item.done` — provider item lifecycle
- `response.function_call_arguments.delta`, `response.function_call_arguments.done` — provider tool-argument deltas
- `response.reasoning_text.delta`, `response.reasoning_summary_text.delta` — provider reasoning deltas
- `response.web_search_call.in_progress`, `response.web_search_call.searching`, `response.web_search_call.completed` — provider web-search call lifecycle


## Stream termination

The stream always ends with:


```
data: [DONE]
```

This terminal line follows `response.completed`, `response.cancelled`, or `response.error`.

Response Created

```json
{
  "event": "response.created",
  "thread_id": "thrd_xyz789",
  "status": "in_progress",
  "is_summarized": false,
  "output_mode": "text"
}
```

Text Delta

```json
{
  "event": "response.content_delta",
  "delta": "The capital of France"
}
```

Thinking Started

```json
{
  "event": "reasoning.started"
}
```

Thinking Delta

```json
{
  "event": "reasoning.content",
  "content": "The user is asking for weather information. I should call get_weather with city=Paris."
}
```

Thinking Completed

```json
{
  "event": "reasoning.completed"
}
```

Tool Calls Executing

```json
{
  "event": "response.block",
  "block": {
    "type": "tool_call",
    "tool_name": "get_weather",
    "arguments": {
      "city": "Paris"
    },
    "success": true,
    "execution_time_ms": 245,
    "id": "call_paris_weather",
    "result": "{\"temperature\":15,\"condition\":\"Partly Cloudy\"}"
  }
}
```

Tool Calls Completed

```json
{
  "event": "response.block",
  "block": {
    "type": "tool_call",
    "tool_name": "get_weather",
    "arguments": {
      "city": "Paris"
    },
    "success": true,
    "execution_time_ms": 245,
    "id": "call_paris_weather",
    "result": "{\"temperature\":15,\"condition\":\"Partly Cloudy\",\"humidity\":65}"
  }
}
```

Iteration Started

```json
{
  "event": "response.content_delta",
  "delta": "I'll check the current weather in Paris."
}
```

Response Done

```json
{
  "thread_id": "thrd_xyz789",
  "event": "response.completed",
  "status": "completed",
  "response_id": "resp_abc123",
  "is_summarized": false,
  "stop_reason": "end_turn",
  "response": [
    {
      "type": "text",
      "text": "The capital of France is Paris."
    }
  ]
}
```

Error Event

```json
{
  "event": "response.error",
  "status": "failed",
  "message": "Rate limit exceeded. Please try again later."
}
```

## Related Resources

- [Create response](/docs/api-reference/responses/create) - Generate streaming responses
- [Cancel stream](/docs/api-reference/responses/cancel) - Cancel an in-progress stream
- [Streaming events](/assets/streaming-events.bba0eb7ccc6ef01fae9e6e34446a9178b355b401d31b0ba78d2e64e79c73ea8a.a2623633.md) - Quick event-type overview
- [Response Object](/docs/api-reference/objects/response-object) - Non-streaming response format