Skip to main content
POST
/
v1
/
agents
/
{agent_id}
/
invoke
/
stream
Invoke Agent (Stream)
curl --request POST \
  --url https://api.xpander.ai/v1/agents/{agent_id}/invoke/stream \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "input": {
    "text": "Explain the benefits of your product in detail"
  }
}
'
{
  "event": "task_created",
  "data": {
    "id": "task-uuid-12345",
    "agent_id": "agent-uuid-12345",
    "status": "running"
  }
}
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

agent_id
string
required
Agent ID (UUID)

Request Body

The request body is identical to Invoke Agent (Sync). Only input.text is required.
input
object
required
id
string
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
boolean
default:"false"
When true, files in input.files are not injected into the LLM context window. See Invoke Sync: Processing Files.
think_mode
string
default:"default"
default or harder. Controls reasoning depth.
instructions_override
string
Additional instructions appended to the system prompt for this invocation only.
expected_output
string
Natural-language description of desired output

Query Parameters

version
string
Agent version to invoke. Defaults to latest deployed. Use "draft" to test undeployed changes.

Response Format

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:
task_created
event
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.
task_updated
event
Fired when the task status changes, usually to executing. Contains the updated task object.
think
event
Agent’s internal reasoning step. Contains the thought process as a string.
analyze
event
Agent’s analysis step before tool selection.
tool_call_request
event
Agent is calling a tool. Contains tool name, parameters, and reasoning.
tool_call_result
event
Tool returned a result. Contains the output from the tool.
chunk
event
A piece of the agent’s final response. Accumulate these to build the full result.
{"type": "chunk", "task_id": "...", "data": "partial response text..."}
task_finished
event
Task is done. Contains the final task object with status, result, and finished_at.
sub_agent_trigger
event
A sub-agent was triggered (multi-agent workflows).
plan_updated
event
Deep planning step was updated (when deep_planning is enabled on the agent).
auth_event
event
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\nDone:', 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\nDone. 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

Authorizations

x-api-key
string
header
required

API Key for authentication

Path Parameters

agent_id
string
required

Body

application/json
input
AgentExecutionInput · object
required

The input to send to the agent

id
string | null

Thread ID for multi-turn conversations. Pass the id from a previous task to continue the conversation.

expected_output
string | null

Natural-language description of the desired output

think_mode
enum<string> | null

Controls the agent's reasoning depth. "default" for standard reasoning, "harder" for deeper analysis.

Available options:
default,
harder
disable_attachment_injection
boolean | null

When true, files in input.files are not injected into the LLM context window

instructions_override
string | null

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.

Response

Successful Response