Invoke an agent with real-time streaming via Server-Sent Events (SSE). Events fire as the agent works, so you can render progress, chunks, tool activity, and the final result in an interactive UI.
Path Parameters
Request Body
The request body is identical to Invoke Agent (Sync) . Only input.text is required.
The message or prompt to send to the agent.
URLs of files for the agent to process. For large files, set disable_attachment_injection: true.
Task/thread ID for multi-turn conversations. Pass the id from a previous task response to continue the same conversation. Continued turns keep using that same ID.
disable_attachment_injection
default or harder. Controls reasoning depth.
Additional instructions appended to the system prompt for this invocation only.
Natural-language description of desired output
Query Parameters
Agent version to invoke. Defaults to latest deployed. Use "draft" to test undeployed changes.
Returns Content-Type: text/event-stream. Each SSE line is prefixed with data: and contains a JSON object with this top-level shape:
{
"type" : "task_created | task_updated | chunk | task_finished | ..." ,
"task_id" : "<task-id>" ,
"organization_id" : "<org-id>" ,
"time" : "2026-03-23T00:46:03.782693Z" ,
"data" : {}
}
For chunk events, data is a string fragment. For task lifecycle events, data is the task object snapshot at that moment.
If you continue a conversation by sending id: "<previous-task-id>", the stream keeps emitting that same task_id. Use Get Task for the latest turn state and Get Task Thread or Get Task Thread (Full) for the full message history.
Event Types
Events are emitted in this order during a typical invocation:
Fired immediately. Contains the task object with status: "pending". This is emitted for both brand-new threads and follow-up turns on an existing task/thread ID.
Fired when the task status changes, usually to executing. Contains the updated task object.
Agent’s internal reasoning step. Contains the thought process as a string.
Agent’s analysis step before tool selection.
Agent is calling a tool. Contains tool name, parameters, and reasoning.
Tool returned a result. Contains the output from the tool.
A piece of the agent’s final response. Accumulate these to build the full result. { "type" : "chunk" , "task_id" : "..." , "data" : "partial response text..." }
Task is done. Contains the final task object with status, result, and finished_at.
A sub-agent was triggered (multi-agent workflows).
Deep planning step was updated (when deep_planning is enabled on the agent).
MCP connector requires authentication. Contains an auth URL the user must visit.
Basic Example
curl -s --no-buffer -X POST "https://api.xpander.ai/v1/agents/<agent-id>/invoke/stream" \
-H "Content-Type: application/json" \
-H "x-api-key: <your-api-key>" \
-d '{"input": {"text": "Say hello in 5 words"}}'
Use --no-buffer (or -N) for real-time output.
Real Event Stream
Here’s what the actual SSE output looks like from a live invocation:
data: {"type":"task_created","task_id":"81c63a62-96f9-47bf-818c-8a0a3e4b6ec8","organization_id":"<org-id>","time":"2026-03-23T00:46:03.782693Z","data":{"id":"81c63a62-96f9-47bf-818c-8a0a3e4b6ec8","agent_id":"<agent-id>","input":{"text":"Reply with exactly RAW","files":[]},"status":"pending","source":"api","output_format":"markdown","events_streaming":true}}
data: {"type":"task_updated","task_id":"81c63a62-96f9-47bf-818c-8a0a3e4b6ec8","organization_id":"<org-id>","time":"2026-03-23T00:46:04.419449Z","data":{"id":"81c63a62-96f9-47bf-818c-8a0a3e4b6ec8","status":"executing",...}}
data: {"type":"chunk","task_id":"81c63a62-96f9-47bf-818c-8a0a3e4b6ec8","organization_id":"<org-id>","time":"2026-03-23T00:46:06.316302Z","data":"RAW"}
data: {"type":"task_finished","task_id":"81c63a62-96f9-47bf-818c-8a0a3e4b6ec8","organization_id":"<org-id>","time":"2026-03-23T00:46:06.797966Z","data":{"id":"81c63a62-96f9-47bf-818c-8a0a3e4b6ec8","status":"completed","result":"RAW","finished_at":"2026-03-23T00:46:06.752706Z",...}}
Consuming the Stream (Node.js)
const response = await fetch (
'https://api.xpander.ai/v1/agents/<agent-id>/invoke/stream' ,
{
method: 'POST' ,
headers: {
'x-api-key' : '<your-api-key>' ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
input: { text: 'Your task here' }
})
}
);
const reader = response . body . getReader ();
const decoder = new TextDecoder ();
while ( true ) {
const { done , value } = await reader . read ();
if ( done ) break ;
const lines = decoder . decode ( value ). split ( ' \n ' );
for ( const line of lines ) {
if (! line . startsWith ( 'data: ' )) continue ;
const event = JSON . parse ( line . slice ( 6 ));
switch ( event . type ) {
case 'chunk' :
// Stream text to UI as it arrives
process . stdout . write ( event . data );
break ;
case 'tool_call_request' :
console . log ( ` \n [Tool: ${ event . data ?. tool_name } ]` );
break ;
case 'task_finished' :
console . log ( ' \n\n Done:' , event . data . result );
break ;
}
}
}
Consuming the Stream (Python)
import requests
import json
response = requests.post(
'https://api.xpander.ai/v1/agents/<agent-id>/invoke/stream' ,
json ={ 'input' : { 'text' : 'Your task here' }},
headers ={
'x-api-key' : '<your-api-key>' ,
'Content-Type' : 'application/json'
},
stream = True
)
for line in response.iter_lines():
if not line:
continue
line = line.decode( 'utf-8' )
if not line.startswith( 'data: ' ):
continue
event = json.loads(line[ 6 :])
if event[ 'type' ] == 'chunk' :
print (event[ 'data' ], end = '' , flush = True )
elif event[ 'type' ] == 'tool_call_request' :
print ( f " \n [Calling tool: { event[ 'data' ].get( 'tool_name' , 'unknown' ) } ]" )
elif event[ 'type' ] == 'task_finished' :
print ( f " \n\n Done. Status: { event[ 'data' ][ 'status' ] } " )
Notes
Use curl --no-buffer or curl -N for real-time terminal output
The stream ends after the task_finished event — close the connection at that point
chunk events contain raw text fragments; concatenate them for the full response
tool_call_request and tool_call_result events let you show tool usage in your UI
think and analyze events show the agent’s reasoning (useful for debugging and transparency)
For multi-turn conversations, wait for task_finished before sending the next message with the same id
Reusing id continues the same task/thread; it does not mint a new top-level task ID for the follow-up turn
Get Task shows the latest turn state for that ID, while Get Task Thread and Get Task Thread (Full) show the accumulated conversation
See Also
API Key for authentication
input
AgentExecutionInput · object
required
The input to send to the agent
Thread ID for multi-turn conversations. Pass the id from a previous task to continue the conversation.
Natural-language description of the desired output
Controls the agent's reasoning depth. "default" for standard reasoning, "harder" for deeper analysis.
Available options:
default,
harder
disable_attachment_injection
When true, files in input.files are not injected into the LLM context window
Additional instructions appended to the agent's system prompt for this invocation only. Use this to adjust behavior per-request without changing the agent's configuration.