MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
Streaming

Streaming Overview

Real-time delivery in MisarBlog — Server-Sent Events (SSE) streams and WebSocket channels.

MisarBlog delivers real-time data through two mechanisms:

  • Server-Sent Events (SSE) — an HTTP response whose body is a text/event-stream, where JSON events arrive incrementally over a single request. Used for AI research and newsletter send progress.
  • WebSocket — a persistent, bidirectional connection for user notifications and per-article live updates.

SSE endpoints

EndpointMethodAuthDescription
/api/v1/ai/researchPOSTSession (first-party editor)Stream an AI research run for the Research & Write panel
/api/newsletter/issues/:id/send-streamGETAPI key (mbk_…)Track a newsletter issue's send progress

These are the only text/event-stream endpoints. Other AI routes (the editor's inline generate and chat helpers) stream over the Vercel AI SDK data protocol, not SSE, and are first-party editor internals.

Event format

Each SSE endpoint emits newline-delimited events. Every event is a single line beginning with data: , followed by a JSON payload, and events are separated by a blank line (standard SSE framing):

data: {"type":"content","delta":"…partial text…"}

data: {"type":"done","result":{ … }}

data: [DONE]
  • Each data: line carries one JSON object. The exact shape is per-endpoint — see each page for its event schema.
  • data: [DONE] is the terminator. Stop reading once you receive it.
  • Blank lines separate events.

Reading an SSE stream

fetch + ReadableStream works in browsers and Node 18+ and lets you buffer partial lines across chunks:

async function readStream(url: string, init: RequestInit) {
  const res = await fetch(url, init);
  if (!res.ok) throw new Error(`Stream error: ${res.status}`);

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

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

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";

    for (const line of lines) {
      if (!line.startsWith("data: ")) continue;
      const payload = line.slice(6).trim();
      if (payload === "[DONE]") return;
      const event = JSON.parse(payload);
      // handle event per the endpoint's schema
    }
  }
}

Error handling

Endpoints deliver errors as a final data event before the terminator, rather than changing the HTTP status mid-stream:

data: {"error":"…"}

data: [DONE]

Check for an error (or, for AI research, an event with type: "error") field on each parsed event.

Next steps