Skip to main content
AutoSage can stream live progress, sources, charts, and final answer content so your users can follow a run as it happens. This guide covers the event stream, how to read it, and the non-streaming alternative.

The streaming endpoint

POST /chats/:id/messages/stream sends a message to an existing chat and returns the response as Server-Sent Events (SSE). The connection stays open, emitting events until the response is complete.
export AUTOSAGE_KEY="sk_live_your_key_here"
export AUTOSAGE_URL="https://api.autosage.ai/api/v1"

curl -N -X POST "$AUTOSAGE_URL/chats/CHAT_ID/messages/stream" \
  -H "Authorization: Bearer $AUTOSAGE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "What is our refund window, and how has it changed?",
    "model": "MODEL_ID"
  }'
The -N flag disables curl’s output buffering so you see events as they stream.

Event types

Each SSE message carries an event with a JSON payload. Handle these types:
EventWhat it carriesHow to handle it
pingKeep-alive heartbeatIgnore it. It only keeps the connection open.
user_messageConfirmation of the message you sentOptional — useful to render the user’s turn.
agent_thoughtA progress update from a multi-step runShow as optional progress UI.
data_previewA preview of available grounded dataUse for optional context or progress UI.
todo_updateUpdated task state from the agentRefresh task or progress UI.
mcp_tool_call / mcp_tool_resultTool activity from an MCP serverShow only when your product exposes tool progress.
final_responseThe assistant’s answer in contentTreat isComplete: true as the canonical complete answer. Also support partial events for protocol compatibility.
sourcesCited source passagesRender as citations when they arrive.
chartA structured chart the answer producedRender the chart.
tokensUsage information for the turnRecord for accounting if needed.
doneThe stream has finished successfullyClose the connection.
errorAn error occurred during generationSurface the message and close the connection.
The main runtime normally emits one final_response with isComplete: true and the complete answer in content. Clients should also support partial final_response events: append partial content, then replace the accumulated text with the canonical content when isComplete is true.

Read the stream in JavaScript

This example reads the SSE body with fetch, parses each event, and accumulates the answer.
const res = await fetch(
  `${AUTOSAGE_URL}/chats/${chatId}/messages/stream`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${AUTOSAGE_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      content: "What is our refund window?",
      model: "MODEL_ID",
    }),
  }
);

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let answer = "";

stream: while (true) {
  const { value, done } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });
  const messages = buffer.split("\n\n");
  buffer = messages.pop() ?? ""; // keep the incomplete trailing chunk

  for (const message of messages) {
    const eventLine = message.match(/^event:\s*(.+)$/m)?.[1];
    const dataLine = message.match(/^data: (.+)$/m)?.[1];
    if (!dataLine) continue;

    const payload = JSON.parse(dataLine);

    const eventType = payload.type ?? eventLine;

    switch (eventType) {
      case "ping":
        break; // keep-alive, ignore
      case "final_response":
        answer = payload.isComplete
          ? payload.content // canonical complete answer
          : answer + payload.content; // compatible with partial events
        renderAnswer(answer);
        break;
      case "sources":
        renderSources(payload.sources);
        break;
      case "chart":
        renderChart(payload.chart);
        break;
      case "done":
        break stream; // stream complete
      case "error":
        showError(payload.message);
        break stream;
    }
  }
}
For the best experience, show progress events while the run is active, render final_response content when it arrives, and show sources as soon as they are emitted. Always handle done and error to close the stream cleanly, and treat ping as a no-op.

One active stream per chat

A chat has one active stream at a time. If the connection drops mid-response, the partial answer generated so far is saved to the chat. Reopen the chat to see what was produced and continue the conversation from there — you don’t lose progress. To stop a stream in progress, call:
curl -X POST "$AUTOSAGE_URL/chats/CHAT_ID/stop" \
  -H "Authorization: Bearer $AUTOSAGE_KEY"

Non-streaming alternative

If you don’t need a live experience, POST /chats/:id/messages returns the complete response in a single JSON body once generation finishes.
curl -X POST "$AUTOSAGE_URL/chats/CHAT_ID/messages" \
  -H "Authorization: Bearer $AUTOSAGE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "What is our refund window?",
    "model": "MODEL_ID"
  }'
The response contains the full answer text, its sources, and usage information — the same content the stream produces, delivered all at once.

Next steps

Chat lifecycle

How a chat turn is created, streamed, saved, and resumed.

Streaming reference

The event protocol in full detail.

Chats API

Endpoints for creating chats and sending messages.

Rate limits & credits

Handle backoff and credit balance in your client.