Skip to Content
APIStreaming (SSE)

Streaming (SSE)

The Computer Agents API uses Server-Sent Events (SSE) to stream real-time responses from agent executions. This enables you to show progress, tool calls, and results as they happen.

Overview

When you create a thread with messages or send a follow-up message, the API returns an SSE stream with events describing the execution progress.

curl -X POST https://api.computer-agents.com/v1/threads \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "messages": [{ "role": "user", "content": "Create hello.py" }], "stream": true }'

Event Format

Each event follows this format:

data: {"type":"event_type","...payload"}

Events are newline-separated. Parse each line starting with data: as JSON.


Event Types

thread.created

Emitted when a new thread is created.

{ "type": "thread.created", "thread": { "id": "thread_xxx", "status": "running", "environmentId": "env_xxx" } }

response.created

Emitted when the agent starts processing.

{ "type": "response.created", "response": { "id": "resp_xxx", "status": "in_progress" } }

response.output_item.added

Emitted when a new output item (message, tool call) is added.

{ "type": "response.output_item.added", "item": { "type": "message", "content": "" } }

response.content_part.added

Emitted when content is added to a message.

{ "type": "response.content_part.added", "part": { "type": "text", "text": "I'll create " } }

response.content_part.delta

Emitted for incremental content updates (streaming text).

{ "type": "response.content_part.delta", "delta": { "text": "a hello.py file" } }

response.item.completed

Emitted when an output item is fully completed.

{ "type": "response.item.completed", "item": { "type": "message", "role": "assistant", "content": "I've created hello.py with the following content..." } }

response.completed

Emitted when the full response is complete.

{ "type": "response.completed", "response": { "id": "resp_xxx", "status": "completed", "output": [...], "usage": { "inputTokens": 500, "outputTokens": 250 } } }

stream.completed

Final event indicating the stream has ended.

{ "type": "stream.completed", "thread": { "id": "thread_xxx", "status": "completed" }, "usage": { "inputTokens": 500, "outputTokens": 250, "totalCost": 0.0075 } }

error

Emitted when an error occurs.

{ "type": "error", "error": { "code": "execution_failed", "message": "Agent execution failed: timeout" } }

Client Implementation

JavaScript/TypeScript

async function executeWithStream(task: string) { const response = await fetch('https://api.computer-agents.com/v1/threads', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: [{ role: 'user', content: task }], stream: true }) }); 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 lines = buffer.split('\n'); buffer = lines.pop() || ''; // Keep incomplete line in buffer for (const line of lines) { if (line.startsWith('data: ')) { try { const event = JSON.parse(line.slice(6)); handleEvent(event); } catch (e) { console.error('Failed to parse event:', line); } } } } } function handleEvent(event: any) { switch (event.type) { case 'thread.created': console.log('Thread created:', event.thread.id); break; case 'response.content_part.delta': process.stdout.write(event.delta.text); break; case 'response.item.completed': console.log('\nItem completed:', event.item.type); break; case 'stream.completed': console.log('\nDone! Cost:', event.usage.totalCost); break; case 'error': console.error('Error:', event.error.message); break; } }

Python

import requests import json def execute_with_stream(task: str): response = requests.post( 'https://api.computer-agents.com/v1/threads', headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' }, json={ 'messages': [{'role': 'user', 'content': task}], 'stream': True }, stream=True ) for line in response.iter_lines(): if line: line = line.decode('utf-8') if line.startswith('data: '): try: event = json.loads(line[6:]) handle_event(event) except json.JSONDecodeError: pass def handle_event(event): event_type = event.get('type') if event_type == 'thread.created': print(f"Thread created: {event['thread']['id']}") elif event_type == 'response.content_part.delta': print(event['delta']['text'], end='', flush=True) elif event_type == 'stream.completed': print(f"\nDone! Cost: ${event['usage']['totalCost']}") elif event_type == 'error': print(f"Error: {event['error']['message']}")

React Hook

import { useState, useCallback } from 'react'; interface StreamState { threadId: string | null; content: string; status: 'idle' | 'streaming' | 'completed' | 'error'; error: string | null; cost: number | null; } export function useAgentStream() { const [state, setState] = useState<StreamState>({ threadId: null, content: '', status: 'idle', error: null, cost: null }); const execute = useCallback(async (task: string) => { setState(s => ({ ...s, status: 'streaming', content: '', error: null })); try { const response = await fetch('/api/threads', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: [{ role: 'user', content: task }], stream: true }) }); 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 lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data: ')) { const event = JSON.parse(line.slice(6)); if (event.type === 'thread.created') { setState(s => ({ ...s, threadId: event.thread.id })); } else if (event.type === 'response.content_part.delta') { setState(s => ({ ...s, content: s.content + event.delta.text })); } else if (event.type === 'stream.completed') { setState(s => ({ ...s, status: 'completed', cost: event.usage.totalCost })); } else if (event.type === 'error') { setState(s => ({ ...s, status: 'error', error: event.error.message })); } } } } } catch (error) { setState(s => ({ ...s, status: 'error', error: error instanceof Error ? error.message : 'Unknown error' })); } }, []); return { ...state, execute }; }

Non-Streaming Mode

Set stream: false to receive a single JSON response instead:

curl -X POST https://api.computer-agents.com/v1/threads \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "messages": [{ "role": "user", "content": "Create hello.py" }], "stream": false }'

Response:

{ "thread": { ... }, "execution": { "success": true, "response": "I've created hello.py...", "actions": ["Created: hello.py"], "durationMs": 5234, "usage": { "inputTokens": 150, "outputTokens": 50 } } }

Non-streaming mode waits for the full execution to complete before responding. Use streaming for better UX with progress updates.


Error Handling

Always handle the error event type:

if (event.type === 'error') { const { code, message } = event.error; switch (code) { case 'budget_exceeded': showUpgradePrompt(); break; case 'execution_timeout': showRetryOption(); break; default: showErrorMessage(message); } }

Common Error Codes

CodeDescription
budget_exceededUser budget exhausted
execution_timeoutTask took too long
execution_failedAgent execution error
rate_limitedToo many requests

Last updated on