Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.sigmic.ai/llms.txt

Use this file to discover all available pages before exploring further.

Streaming Events

Real-time task events are delivered via Server-Sent Events (SSE) through the GET /api/v1/tasks/:id/stream endpoint. This is the recommended way to receive live updates as the AI agent processes your request.

Architecture

The API uses a fire-and-forget pattern:
  1. POST /tasks returns JSON immediately with the task ID
  2. GET /tasks/:id/stream is the unified SSE channel for all events
Task execution runs in the background. You can connect, disconnect, and reconnect to the stream at any time without affecting execution.

SSE Format

Events are delivered in the standard SSE format:
event: {event_type}
data: {json_data}

event: {event_type}
data: {json_data}
Each event consists of:
  • event - The event type identifier
  • data - JSON payload with event details
Events are separated by double newlines (\n\n).

Stream Phases

The stream delivers events in three phases:

Phase 1: History Replay

All saved conversation messages are emitted as history_message events.

Phase 2: History Complete

A single history_done event signals that history replay is complete and indicates whether the task is still running.

Phase 3: Live Events (if running)

If the task is currently executing, the stream delivers catch-up state followed by real-time events until the task completes.

Event Types

history_message

A replayed conversation message. Emitted for each saved message at the start of the stream.
{
  "id": "msg-1",
  "role": "user",
  "content": "Analyze this CSV file",
  "createdAt": "2026-01-26T15:50:54.870Z",
  "attachedFiles": [
    { "name": "data.csv", "path": "uploads/data.csv" }
  ]
}

history_done

Signals that all history messages have been sent.
{
  "messageCount": 4,
  "isStreaming": true
}
FieldDescription
messageCountNumber of history messages emitted
isStreamingtrue if the task is running (live events follow), false if task is idle

status

Processing status updates indicating task progress.
{
  "status": "processing",
  "message": "Task created: c37c1d78-4d23-4e53-949a-ddd775f25e01"
}
Status values:
StatusDescription
connectingEstablishing connection to the agent
processingTask is being processed
retryingRetrying after a temporary failure

thought

Agent’s thinking and reasoning process. Useful for understanding how the AI approaches the task.
{
  "subject": "Analyzing the problem",
  "description": "I need to calculate 2+2 which equals 4"
}

content

Text content from the agent’s response. May be delivered in chunks.
{
  "text": "The answer is ",
  "isPartial": true
}
FieldDescription
textThe content text
isPartialtrue if more content is coming, false for final chunk

tool_call

Indicates a tool is being executed by the agent.
{
  "callId": "call_123",
  "toolName": "read_file",
  "status": "executing",
  "args": {"path": "/uploads/data.csv"}
}
Tool call status values:
StatusDescription
pendingTool call queued
executingTool is currently executing
awaiting_approvalWaiting for manual approval (when autoExecute=false)

tool_result

Result of a completed tool execution.
{
  "callId": "call_123",
  "status": "success",
  "output": "File contents here..."
}

error

Error event indicating something went wrong.
{
  "message": "Task execution failed",
  "code": "EXECUTION_ERROR"
}

done

Task completion event with the final response.
{
  "finalResponse": "The answer to 2+2 is 4."
}
The done event signals that the current execution turn is complete. For completed tasks, the stream ends after this event. For ongoing conversations, you can send follow-ups and reconnect.

Subagent Events

When the AI agent delegates work to specialized subagents, these events track the subagent’s lifecycle and activity. Each subagent session begins with subagent_start and ends with subagent_end, with subagent_thought and subagent_tool events in between.

subagent_start

Emitted when the agent delegates work to a subagent.
{
  "agentName": "code-writer",
  "displayName": "Code Writer",
  "description": "Writes and edits code files",
  "objective": "Create a utility function for date formatting"
}
FieldDescription
agentNameUnique identifier for the subagent
displayNameHuman-readable name
descriptionWhat the subagent does
objectiveThe specific task delegated to the subagent

subagent_thought

Reasoning text from a running subagent.
{
  "agentName": "code-writer",
  "text": "I need to check the existing date utilities before writing a new one"
}
FieldDescription
agentNameWhich subagent produced this thought
textThe thought/reasoning text

subagent_tool

Tool usage within a subagent. Emitted at both the start and end of each tool call.
{
  "agentName": "code-writer",
  "status": "end",
  "toolName": "write_file",
  "output": "File written successfully"
}
FieldDescription
agentNameWhich subagent is using the tool
statusstart when the tool begins, end when it completes
toolNameName of the tool being used
argsTool arguments (present on start events)
outputTool output (present on end events)

subagent_end

Emitted when a subagent finishes execution.
{
  "agentName": "code-writer",
  "terminateReason": "GOAL",
  "result": "Created formatDate() in src/utils/date.ts"
}
FieldDescription
agentNameWhich subagent finished
terminateReasonGOAL (success), TIMEOUT, MAX_TURNS, ERROR, or ABORTED
resultSummary of what the subagent accomplished (on success)
errorError message (when terminateReason is ERROR)

Parsing SSE in Code

JavaScript

async function streamTask(taskId) {
  const response = await fetch(`${BASE_URL}/api/v1/tasks/${taskId}/stream`, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  });

  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 });
    const events = buffer.split('\n\n');
    buffer = events.pop() || '';

    for (const event of events) {
      if (!event.trim() || event.startsWith(':')) continue;

      const lines = event.split('\n');
      const eventType = lines[0]?.replace('event: ', '');
      const dataLine = lines[1]?.replace('data: ', '');

      if (!dataLine) continue;
      const data = JSON.parse(dataLine);

      // Handle event based on type
      console.log(eventType, data);
    }
  }
}

Python

import json
import requests

def stream_task(task_id):
    response = requests.get(
        f'{BASE_URL}/api/v1/tasks/{task_id}/stream',
        headers={'Authorization': f'Bearer {API_KEY}'},
        stream=True
    )

    event_type = None
    for line in response.iter_lines():
        if not line:
            continue

        line = line.decode('utf-8')
        if line.startswith('event:'):
            event_type = line.replace('event: ', '')
        elif line.startswith('data:'):
            data = json.loads(line.replace('data: ', ''))
            # Handle event based on type
            print(event_type, data)

Connection Handling

The server sends periodic heartbeat comments (:heartbeat) to keep the connection alive. These can be safely ignored by SSE parsers.
Disconnect and reconnect to the stream at any time. The stream replays full conversation history, then catches up to the current state. The task continues executing in the background regardless of stream connections.
Closing the stream connection does NOT cancel or stop the task. Execution continues in the background. Use DELETE /api/v1/tasks/:id to explicitly cancel a task.
Tasks may take several minutes to complete. Ensure your HTTP client has appropriate timeout settings. The 15-second heartbeat keeps the connection alive.

Polling as an Alternative

If you cannot use SSE streaming (e.g., due to infrastructure limitations), you can poll the task endpoint to get real-time progress via the output field.

Output Structure

When polling GET /api/v1/tasks/{id}, the response includes an output object that works the same way for both running and completed tasks:
{
  "output": {
    "content": "Here's what I found...",
    "updatedAt": "2026-01-26T15:51:02.123Z",
    "thoughts": [
      { "subject": "Analyzing", "description": "Looking at the files..." }
    ],
    "toolCalls": [
      {
        "callId": "call_123",
        "toolName": "read_file",
        "status": "success",
        "output": "file contents..."
      }
    ]
  },
  "pendingApprovals": [
    {
      "callId": "call_456",
      "toolName": "write_file",
      "args": { "path": "output.csv" }
    }
  ]
}
FieldDescription
output.contentAccumulated text response (partial while running, final when completed)
output.updatedAtISO timestamp when the output was last updated
output.thoughtsAgent’s thinking process
output.toolCallsAll tool executions with their current status
pendingApprovalsTool calls awaiting approval (for autoExecute=false tasks)
Use the task’s status field to determine if output.content is partial (when status is running or awaiting_approval) or final (when status is completed, failed, or cancelled).

Polling Example

async function pollWithProgress(taskId) {
  const API_BASE = 'https://api.sigmic.ai';
  let lastUpdatedAt = null;

  while (true) {
    const response = await fetch(`${API_BASE}/api/v1/tasks/${taskId}`, {
      headers: { 'Authorization': 'Bearer sigmic_your_key_here' }
    });

    const { data: task } = await response.json();

    // Display progress when output has been updated
    if (task.output && task.output.updatedAt !== lastUpdatedAt) {
      lastUpdatedAt = task.output.updatedAt;
      console.log('Content:', task.output.content);
      console.log('Last updated:', task.output.updatedAt);
      if (task.output.toolCalls) {
        console.log('Tools:', task.output.toolCalls.map(t => `${t.toolName}: ${t.status}`));
      }
    }

    // Check for pending approvals
    if (task.pendingApprovals?.length > 0) {
      console.log('Pending approvals:', task.pendingApprovals);
    }

    // Check for completion
    if (['completed', 'failed', 'cancelled'].includes(task.status)) {
      console.log('Final response:', task.output?.content);
      return task;
    }

    // Poll every 500ms
    await new Promise(r => setTimeout(r, 500));
  }
}

SSE vs Polling

ApproachProsCons
SSE StreamingReal-time updates, history replay, lower latencyRequires SSE support, persistent connection
PollingWorks everywhere, simpler implementationHigher latency, more API calls
Use SSE streaming when possible for the best user experience. Fall back to polling with the output field when SSE is not available.