Streaming
GET /v1/ask/:id/stream delivers an agent turn as server-sent events: persisted replay, live frames, keep-alives, terminal close, and a 20-stream workspace cap.
GET /v1/ask/:id/stream streams a turn started with [POST /v1/ask](post-ask) as server-sent events, so an integration can show the answer forming, surface tool activity as it happens, and render text deltas token by token instead of waiting out the poll interval. Streaming is additive: the turn is started and charged once by the submission, the [poll contract](ask-results) is unchanged, and streaming then polling the same turn (or the reverse) never double charges. Use whichever read model fits each consumer; both observe the same turn.
The stream opens by replaying every event the turn has already persisted, then follows with live events as the turn produces them, and closes when the turn's status reaches completed, failed, or awaiting_confirmation. Because the replay is sourced from the durable turn record, a dropped connection loses nothing: reconnect and the full sequence arrives again from the start. If the turn is already settled when you connect, you get the whole recorded sequence and an immediate close, which makes the stream a valid way to fetch a finished turn's event log.
A missing or cross-tenant ask_id is a real 404 issued before any SSE header is sent, never a 200 stream carrying an error frame, so standard HTTP error handling works before you enter stream parsing. The response is Content-Type: text/event-stream; each frame is a data: {json} line followed by a blank line, and a : keep-alive comment line goes out every 25 seconds so proxies and load balancers see traffic on an idle socket. SSE clients ignore comment lines automatically.
/v1/ask/:id/streamPath parameters
Frame kinds
Every frame is a JSON object discriminated by kind. The stream carries the answer and the audit trail of the turn; the model's raw reasoning (thinking.delta) is deliberately withheld from the public stream, because reasoning text is model-version-dependent and not a contract to build on.
Frame kinds
429. The ceiling exists because a held socket is a resource, not a request: close streams you are done with, and fall back to polling for consumers that do not need live frames. Stream connects are metered in their own daily rate-limit namespace, ask_stream, sized above the ask budget so reconnects never compete with submissions.End to end: scope, stream, then read the citations
The three surfaces compose. Scope an ask to one pipeline so the answer can only draw on that pipeline's documents, hold the stream to watch the turn work, and when the status frame reaches completed, fetch the poll payload once for the structured citations and the verification verdict. The stream is for watching; the poll payload is the settled, citable result.
1. Submit a scoped ask
2. Stream the turn (curl -N disables buffering)
3. Read the citations from the poll payload
Consuming the stream in TypeScript