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"}'