Skip to main content

SSE Streaming

For long-running AI operations, use Server-Sent Events (SSE) to receive real-time progress updates instead of waiting for the full response.

How it works

  1. Start an async chat request to get a job_id
  2. Open an SSE connection to stream progress
  3. Receive events as the AI processes your request

Setup

# 1. Start an async request
curl -X POST https://api.superdocs.app/v1/chat/async \
  -H "Authorization: Bearer sk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Rewrite all sections to be more concise",
    "session_id": "my-session",
    "document_html": "..."
  }'

# Response: { "job_id": "job_abc123", "session_id": "my-session", ... }

# 2. Stream progress (auth via query parameter)
curl -N "https://api.superdocs.app/v1/chat/my-session/stream?job_id=job_abc123&api_key=sk_YOUR_API_KEY"
SSE uses EventSource, which doesn’t support custom headers. Pass your API key as the api_key query parameter.

Event types

The stream emits 5 event types:

intermediate

Progress updates during AI processing.
event: intermediate
data: {"type": "intermediate", "content": "Analyzing document structure...", "sequence": 1, "timestamp": "2026-03-07T10:00:01Z"}

proposed_change

In HITL mode (approval_mode: "ask_every_time"), the AI proposes a document change for review. One event is emitted per proposed change.
event: proposed_change
data: {"type": "proposed_change", "content": "{\"change_id\": \"ch_1\", ...}", "sequence": 2}
The content field is a JSON string. Parse it to get the change details:
{
  "change_id": "ch_1",
  "operation": "edit",
  "chunk_id": "550e8400-e29b-41d4-a716-446655440000",
  "old_html": "<p>Original section content...</p>",
  "new_html": "<p>Updated content with GDPR compliance...</p>",
  "ai_explanation": "Added GDPR data processing requirements",
  "batch_id": "ch_1",
  "batch_total": 3,
  "insert_after_chunk_id": null
}
FieldTypeDescription
change_idstringUnique ID for this change. Use when calling /approve.
operationstring"edit", "create", or "delete".
chunk_idstring | nullThe document section being modified or deleted. Null for creates.
old_htmlstring | nullCurrent HTML content. Present for updates and deletes. Null for creates.
new_htmlstring | nullProposed new HTML content. Present for updates and creates. Null for deletes.
ai_explanationstringWhy the AI proposed this change. Show this to the user.
batch_idstring | nullGroups related changes. All changes in a batch share the same batch_id.
batch_totalnumber | nullHow many changes are in this batch. Wait for this many events before showing the review UI.
insert_after_chunk_idstring | nullFor create: where to insert the new section in the document.
See the Human-in-the-Loop guide for the complete approval workflow.

final

Job completed successfully. Contains the full result.
event: final
data: {"content": "I've made all sections more concise.", "result": {"response": "...", "document_changes": {...}, "usage": {...}}}

usage

Emitted after final with usage consumption data.
event: usage
data: {"monthly_used": 43, "monthly_limit": 500, "monthly_remaining": 457, "was_billable": true, "subscription_tier": "free"}

error

Job failed, was cancelled, or an auth error occurred.
event: error
data: {"error": "Request timed out"}

JavaScript example

const eventSource = new EventSource(
  `https://api.superdocs.app/v1/chat/my-session/stream?job_id=${jobId}&api_key=${apiKey}`
);

eventSource.addEventListener("intermediate", (event) => {
  const data = JSON.parse(event.data);
  console.log("Progress:", data.content);
});

eventSource.addEventListener("final", (event) => {
  const data = JSON.parse(event.data);
  console.log("Done:", data.result.response);
  // Update your editor with data.result.document_changes.updated_html
  eventSource.close();
});

eventSource.addEventListener("usage", (event) => {
  const data = JSON.parse(event.data);
  console.log(`Used ${data.monthly_used}/${data.monthly_limit} operations`);
});

eventSource.addEventListener("error", (event) => {
  if (event.data) {
    const data = JSON.parse(event.data);
    console.error("Error:", data.error);
  }
  eventSource.close();
});

Python example

import json
import requests

url = f"https://api.superdocs.app/v1/chat/my-session/stream?job_id={job_id}&api_key={api_key}"

with requests.get(url, stream=True) as response:
    for line in response.iter_lines():
        if not line:
            continue
        decoded = line.decode("utf-8")
        if decoded.startswith("data: "):
            data = json.loads(decoded[6:])
            if "content" in data:
                print(f"Progress: {data['content']}")
            elif "error" in data:
                print(f"Error: {data['error']}")
                break