Skip to main content

HTTP Streaming API

The default /chat endpoint returns a single JSON response after the agent finishes thinking. That works fine for simple queries. But when an agent runs a 9-minute deep research pipeline or chains four tool calls back-to-back, the client stares at a spinner with no feedback.

Streaming mode fixes this. Send "stream": true in the request body and the response switches from JSON to Server-Sent Events (SSE). Progress events arrive in real-time as the agent works.

Quick Start

curl -N -X POST http://localhost:3777/chat \
-H "Content-Type: application/json" \
-d '{"message": "Research the current state of quantum computing", "stream": true}'

The -N flag disables curl's output buffering so events print as they arrive.

Output:

event: progress
data: {"type":"SYSTEM_PROGRESS","toolName":"deep_research","phase":"decomposing","message":"Decomposing query into sub-questions (iter 1/3, 0 findings)","progress":0.1}

event: progress
data: {"type":"SYSTEM_PROGRESS","toolName":"deep_research","phase":"searching","message":"Searching sources \"quantum error correction 2026\" (iter 1/3, 4 findings, 3 sources)","progress":0.33}

event: progress
data: {"type":"SYSTEM_PROGRESS","toolName":"deep_research","phase":"extracting","message":"Extracting content from sources (iter 2/3, 8 findings, 7 sources)","progress":0.5}

event: progress
data: {"type":"SYSTEM_PROGRESS","toolName":"deep_research","phase":"synthesizing","message":"Synthesizing report (iter 3/3, 14 findings, 12 sources)","progress":0.9}

event: reply
data: {"type":"REPLY","reply":"## Current State of Quantum Computing\n\n...","personaId":"seed_my_agent"}

Request Format

POST /chat
Content-Type: application/json
{
"message": "your prompt here",
"stream": true,
"sessionId": "optional-session-id"
}

The only addition is "stream": true. All other fields from the standard /chat endpoint work identically: sessionId, tenantId, reset, research, toolFailureMode.

Response Format

When stream is true, the response uses Content-Type: text/event-stream with Cache-Control: no-cache and Connection: keep-alive.

SSE Event Types

Three event types are emitted:

progress -- Tool Execution Updates

Fired whenever a tool reports progress. Most common during research and browser automation.

event: progress
data: {"type":"SYSTEM_PROGRESS","toolName":"deep_research","phase":"searching","message":"Searching sources \"CRISPR safety\"","progress":0.4}
FieldTypeDescription
type"SYSTEM_PROGRESS"Always this value for progress events
toolNamestringName of the tool reporting progress
phasestringCurrent phase (tool-specific)
messagestringHuman-readable status message
progressnumber | null0-1 completion fraction, or null if indeterminate

Research phases: decomposing, searching, extracting, analyzing_gaps, synthesizing, complete.

reply -- Final Agent Response

Sent once when the agent finishes.

event: reply
data: {"type":"REPLY","reply":"The answer is...","personaId":"seed_my_agent"}
FieldTypeDescription
type"REPLY"Always this value for the final reply
replystringThe agent's text response
personaIdstringWhich persona generated the response

error -- Turn Failure

Sent if the LLM or a tool call fails.

event: error
data: {"type":"ERROR","error":"Rate limit exceeded. Retry after 30 seconds."}
FieldTypeDescription
type"ERROR"Always this value for errors
errorstringError message

After an error event, the stream ends. No reply event follows.

Non-Streaming Mode (Default)

When stream is omitted or false, the endpoint returns a single JSON response:

{
"reply": "The answer is...",
"personaId": "seed_my_agent"
}

No SSE, no progress events. The HTTP connection stays open until the agent is done.

Auto-Approve Header

By default, tools with side effects (file writes, shell commands, etc.) require human approval through the HITL UI. In streaming mode, waiting for approval blocks the entire stream.

The X-Auto-Approve: true header bypasses approval for side-effect tools:

curl -N -X POST http://localhost:3777/chat \
-H "Content-Type: application/json" \
-H "X-Auto-Approve: true" \
-d '{"message": "Create a summary.txt file with the key findings", "stream": true}'

Security constraints:

  • Only honored for loopback requests (localhost) or authenticated requests (valid x-wunderland-chat-secret header)
  • Remote requests without a chat secret are ignored -- tools still require HITL approval
  • Read-only tools (web search, file read, etc.) are always auto-approved regardless of this header

JavaScript Client Example

const response = await fetch('http://localhost:3777/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: 'Research the latest advances in fusion energy',
stream: true,
sessionId: 'user-42',
}),
});

const reader = response.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 });

// Parse SSE frames from the buffer
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep incomplete line in buffer

let eventType = '';
for (const line of lines) {
if (line.startsWith('event: ')) {
eventType = line.slice(7);
} else if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));

if (eventType === 'progress') {
console.log(`[${data.phase}] ${data.message}`);
if (data.progress !== null) {
updateProgressBar(data.progress);
}
} else if (eventType === 'reply') {
console.log('Agent:', data.reply);
} else if (eventType === 'error') {
console.error('Error:', data.error);
}
}
}
}

curl Examples

Simple streaming chat

curl -N -X POST http://localhost:3777/chat \
-H "Content-Type: application/json" \
-d '{"message": "Hello", "stream": true}'

Streaming with deep research

curl -N -X POST http://localhost:3777/chat \
-H "Content-Type: application/json" \
-d '{"message": "Analyze the economic impact of AI on the labor market", "stream": true, "research": "deep"}'

Authenticated streaming with auto-approve

curl -N -X POST http://localhost:3777/chat \
-H "Content-Type: application/json" \
-H "x-wunderland-chat-secret: my-secret" \
-H "X-Auto-Approve: true" \
-d '{"message": "Write a report to output.md", "stream": true}'

Streaming with session context

# First message
curl -N -X POST http://localhost:3777/chat \
-H "Content-Type: application/json" \
-d '{"message": "Research quantum computing breakthroughs in 2026", "stream": true, "sessionId": "research-1"}'

# Follow-up in same session
curl -N -X POST http://localhost:3777/chat \
-H "Content-Type: application/json" \
-d '{"message": "Now compare those to classical computing limits", "stream": true, "sessionId": "research-1"}'