SSE Events
Streaming endpoints returntext/event-stream. This page is the complete list of the public event vocabulary. Anything not on this table is an internal event passed through verbatim — it is not a contract and may change.
Wire format
Each event is two lines plus a blank line:datais single-line JSON- Every payload carries a monotonically increasing
event_idstarting at 1, usable for dedup and gap detection - The
typefield mirrors theevent:line, so parsing onlydatais sufficient
Full event table
Internal event names map to public names through a fixed table. The public name is the contract; the internal name is not — internal renames do not affect you, while public-name changes go through a version process.Run lifecycle
run.in_progress fires once per turn. Within one run the model may call tools, read results, and reason again — each round is a turn, so receiving multiple run.in_progress events is normal. Do not treat them as duplicate requests.
Output content
Assembling the answer means concatenating every
output.text.delta delta in arrival order. Do not reorder by event_id; events already arrive in order and reordering only adds a failure mode.
Tool calls
Tool calls split into several events; the first four come from the internaltool_call event dispatched on its status field:
Unlisted
tool_call.<something> names are possible: any status outside the mapping table produces tool_call.${status} verbatim. Your UI needs a fallback renderer for the tool_call.* prefix rather than a hard-coded four-case switch whose default branch does nothing.tool_call.denied and tool_call.failed are different things:
failed= the tool ran and errored (timeout, invalid argument, missing target)denied= the tool never ran; a permission gate stopped it
Approvals and delegation
approval.required is a blocking event: the stream waits for your response. Ignore it and the run hangs until timeout.
Usage and quota
Consumption example
Boundaries and failure modes
Unrecognised event types are passed through, not dropped. That is deliberate: new internal events do not vanish on your side just because the mapping table lags. The cost is that you must tolerate unknown names — so your
default branch should ignore, not throw.Verify your integration
Minimal check:event: run.createdfirst- Several
event: output.text.delta event: run.completeddata: [DONE]last
run.failed, read the code in the payload and look it up in Error Codes.
Related pages
Endpoint Reference
Which endpoints stream
Error Codes
Reading the code inside a
run.failed payloadVerified 2026-08-11. Source:
services/core/src/lib/sse-event-mapper.ts (EVENT_MAP 16 entries + TOOL_CALL_STATUS_MAP 4 entries = 20 public event names; that file is the single source of truth for the vocabulary).
