SSE Streaming
Quick path: new to the API? Go from an API key to your first successful call in the Quickstart (~5 minutes), then come back here to add streaming. For long-running AI operations, use Server-Sent Events (SSE) to receive real-time progress updates instead of waiting for the full response.SSE is one option, not the only option. For non-browser consumers (server-side workers, batch jobs, AI agent tools), the synchronous
/v1/chat endpoint or polling on /v1/jobs/{id} are simpler and equally valid. See Server Integration for the patterns.Revert + active SSE. If your UI lets users revert a session while an SSE stream is still open, close the
EventSource first (or wait for the final event) before sending the revert call. Revert returns 409 while a chat job is in flight on the session, and any updates that arrive after revert from the original branch are stale.How it works
- Start an async chat request to get a
job_id - Open an SSE connection to stream progress
- Receive events as the AI processes your request
Setup
SSE uses EventSource, which doesn’t support custom headers. Pass your API key as the
api_key query parameter.Event types
The stream emits nine event types:intermediate, proposed_change_batch, document_sync, continue_prompt, documents_changed, model_fallback, final, usage, and error.
intermediate
Progress updates during AI processing.
Rendering intermediate events in your UI
Show everyintermediate event to users in real time. Operations on large documents (or with model_tier: "max" + thinking_depth: "deep") can take 30 seconds to several minutes — without visible progress, your UI looks frozen and indistinguishable from a crash.
Pattern 1 — Append-and-update an in-flight chat bubble (recommended for chat-style UIs):
timestamp field to detect stalls programmatically.
proposed_change_batch
In HITL mode (approval_mode: "ask_every_time"), the AI proposes document changes for review. proposed_change_batch is the only event that carries proposed changes — every HITL turn arrives through it, whether the AI proposes one change or hundreds. The whole turn comes as one SSE event carrying a changes array, so the wire load stays proportional to turns, not changes, and HITL UIs can render the entire approval card at once.
Older clients may still register a separate
proposed_change listener. That’s harmless — keep it for safety if you have it — but it never fires: single-change turns are delivered through proposed_change_batch too (with type: "single_approval", a changes array of length 1). Don’t build logic that waits for or branches on a standalone proposed_change event; it will never arrive.content field is a JSON-stringified string that must be parsed once more:
changes[] also carries the document_id it belongs to (see the field table below), so your UI can group the review list per document.
Edits to page headers, footers, footnote/endnote bodies, and comments arrive through this same event, each as its own ordinary entry in changes[] (the part’s block is its chunk_id). There is no separate event or approval track for them; approve or deny them like any other change.
The parsed payload:
Each entry in
changes[]:
See the Human-in-the-Loop guide for the complete approval workflow.
continue_prompt
For a very large edit, the AI completes as much as it can in one turn, keeps that work, and asks whether to continue with the rest. This can happen in either approval mode. Resume (or stop) by calling POST /v1/chat/{session_id}/continue.
content field is a JSON string — parse it for:
message— a ready-to-display prompt, already in the user’s language.done/total/remaining— progress counts you can show alongside it.
continue: true to keep going or false to stop and keep what’s done. The job resumes and may emit another continue_prompt for the next segment — handle it in a loop, the same way you handle repeated HITL approval rounds.
document_sync
Emitted before the AI begins processing, after the backend has prepared your document for editing. The event carries the prepared HTML containing the section identifiers the AI will reference when proposing changes.
document_html in the request. The event arrives once at the start of the stream, before any intermediate or proposed_change_batch events.
What to do with it: Apply the HTML to your editor immediately so the editor’s section IDs match the IDs the AI will reference in subsequent proposed_change_batch events. This is essential for HITL diff highlights to render correctly on freshly pasted or uploaded documents — without it, the editor and the AI may disagree on which section a change targets.
Multi-document sessions. When several documents are open,
document_sync carries focused_document_id — the document this prepared HTML belongs to. Apply it only to the matching (focused) tab, not whichever document happens to be on screen. Single-document sessions can ignore the field.documents_changed
Emitted when a turn touched more than one open document on auto-apply, or created a new document (creation applies immediately in either approval mode). Tells your UI which documents changed so non-focused tabs can badge and newly created tabs appear — for review-mode edits, proposed_change_batch already carries per-change document_id, so this event isn’t needed there.
content):
documents[]— one entry per changed document:document_id,title,change_count,changed_chunk_ids(sections to flash on tab switch), andcreated(truewhen the AI opened this as a brand-new document this turn — add a tab for it; itschange_countis0).focused_document_id— the document currently in focus after the turn.
approval_mode: "approve_all") turns that changed 2+ documents, and on any turn — either approval mode — where the AI created a new document (creation applies immediately even in review mode). Standard single-document auto-apply edit turns don’t emit it (the focused doc flashes inline); it may still fire for multi-document assistant/sub-agent turns. Ignore it if your integration only ever works with one document per session.
final
Job completed successfully. Contains the full result.
result also includes focused_document_id so you know which open document the turn finished on.
usage
Emitted after final with usage consumption data.
error
Job failed, was cancelled, or an auth error occurred.
model_fallback
Emitted only when the AI tier you requested is temporarily unresponsive upstream and SuperDocs automatically completed your request on the pro tier instead. Your request still succeeds — this event just tells you (and lets you tell your users) that a different tier served it.
Reconnect & resume
Every event carries a monotonically increasingsequence number. If your EventSource connection drops mid-job, reconnect with the last_sequence query parameter set to the highest sequence you already processed — the stream replays only newer events, never the full history:
last_sequence (or pass 0) to receive the full event history for the job — useful when a fresh client attaches to an already-running job. This also makes reconnecting after approve_change safe: already-rendered proposed_change_batch / document_sync events are not re-delivered.

