MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
Streaming

AI Research Stream

Stream a typed AI research run — thinking stages, tool calls, citations, and article content — over SSE.

The AI research endpoint runs a deep-research agent and streams its progress as Server-Sent Events. It powers the editor's Research & Write panel: as the agent plans, searches the web, reflects, and writes, it emits typed events you can render live (stage labels, citation lists, and incremental article content).

This is a first-party, session-authenticated endpoint used by the MisarBlog editor — it is not part of the API-key REST surface. Requests must carry a valid MisarBlog session; there is no mbk_… key parameter.

Endpoint

POST /api/v1/ai/research

Request

Called from the editor (same-origin, session cookie)
const res = await fetch("/api/v1/ai/research", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query: "Rust vs Go for backend services in 2026" }),
});

Body parameters

FieldTypeRequiredDescription
querystringYesThe research topic or question. topic is accepted as an alias if query is omitted.

Response

Content-Type: text/event-stream

The stream is a passthrough of the research agent's typed events. Each data: line is a JSON object with a type field, and the stream ends with data: [DONE].

data: {"type":"thinking","stage":"plan"}

data: {"type":"tool_call"}

data: {"type":"tool_result","citations":[{"url":"https://…","title":"…"}]}

data: {"type":"reflection","covered":["performance"],"gaps":["tooling"]}

data: {"type":"content","delta":"## Performance\n\nBoth languages…"}

data: {"type":"done","result":{"content":"# Rust vs Go…","citations":[…]}}

data: [DONE]

Event types

typePayload fieldsMeaning
thinkingstageAgent entered a stage (see stages below).
tool_callAgent invoked a research tool (e.g. web search).
tool_resultcitationsNew sources discovered. Each citation: { url, title?, snippet? }.
reflectioncovered, gapsTopics covered so far and remaining gaps (string[]).
contentdeltaIncremental article text — concatenate delta values in order.
doneresultFinal { content, citations }. Marks a completed run.
errormessageThe run failed; message describes why.

Stages

The stage field on thinking events is one of: clarify, plan, execute, reflect, synthesize.

Consuming the stream

interface Citation { url: string; title?: string; snippet?: string }

const res = await fetch("/api/v1/ai/research", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query }),
});

const reader = res.body!.getReader();
const dec = new TextDecoder();
let buffer = "";
let article = "";
const citations: Citation[] = [];

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += dec.decode(value, { stream: true });
  const lines = buffer.split("\n");
  buffer = lines.pop() ?? "";

  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;
    const raw = line.slice(6).trim();
    if (raw === "[DONE]") break;

    const event = JSON.parse(raw);
    switch (event.type) {
      case "thinking":     updateStage(event.stage); break;
      case "tool_result":  citations.push(...(event.citations ?? [])); break;
      case "content":      article += event.delta ?? ""; break;
      case "done":         article = event.result?.content ?? article; break;
      case "error":        throw new Error(event.message ?? "Research failed");
    }
  }
}

Errors

StatusDescription
400Invalid JSON body, or missing query / topic.
401No valid MisarBlog session.
502The upstream research agent returned an error.

A failed run mid-stream is delivered as an error event rather than a status change:

data: {"type":"error","message":"Research failed"}

data: [DONE]