DorkOS
Integrations

SSE Protocol

Server-Sent Events streaming protocol reference

SSE Protocol

DorkOS uses Server-Sent Events (SSE) for two purposes: real-time message streaming and cross-client session synchronization.

Message Streaming

When a client sends a message, the server responds with an SSE stream:

POST /api/sessions/:id/messages
Content-Type: application/json

{ "content": "Hello, Claude", "cwd": "/path/to/project", "clientMessageId": "optional-uuid" }

The optional clientMessageId enables server-echo ID reconciliation — the server includes JSONL-assigned message IDs in the done event so the client can remap streaming message IDs to their persistent equivalents.

Response (Content-Type: text/event-stream):

data: {"type":"text_delta","delta":"Hello"}

data: {"type":"text_delta","delta":"! How can"}

data: {"type":"text_delta","delta":" I help?"}

data: {"type":"done"}

The endpoint always returns a text/event-stream response. The web client uses direct SSE streaming regardless of whether DORKOS_RELAY_ENABLED is set.

Event Types

Text Events

Prop

Type

Thinking Events

Prop

Type

Tool Events

Prop

Type

Interactive Events

Prop

Type

Control Events

Prop

Type

Responding to Interactive Events

When approval_required is received, approve or deny before the stream continues:

# Approve
POST /api/sessions/:id/approve
Content-Type: application/json
{ "toolCallId": "tc_123" }

# Deny
POST /api/sessions/:id/deny
Content-Type: application/json
{ "toolCallId": "tc_123" }

Failing to respond to an approval_required event blocks the agent. The SSE stream stays open until the tool call is resolved.

Both /approve, /deny, and /submit-answers may return 409 with { code: "INTERACTION_ALREADY_RESOLVED" } if the SDK resolved the interaction before the request arrived. Treat this as success.

When question_prompt is received, submit the user's answer:

POST /api/sessions/:id/submit-answers
Content-Type: application/json
{ "toolCallId": "tc_123", "answers": { "0": "option_a" } }

When elicitation_prompt is received, submit the completed form:

POST /api/sessions/:id/submit-elicitation
Content-Type: application/json
{ "elicitationId": "eli_123", "action": "submit", "content": { "api_key": "sk-..." } }

To cancel (agent treats as user declining):

POST /api/sessions/:id/submit-elicitation
Content-Type: application/json
{ "elicitationId": "eli_123", "action": "cancel" }

Elicitation has a server-side timeout (timeoutMs from the event). If you do not respond within that window, the agent receives an empty result and continues or fails gracefully.

Session Sync Protocol

Subscribe to real-time session changes via a persistent SSE connection:

GET /api/sessions/:id/stream

Sync Events

Prop

Type

When a sync_update is received, re-fetch message history. GET /api/sessions/:id/messages supports ETag caching (If-None-Match / 304) for efficient polling.

The sync stream carries only sync_connected and sync_update events.

System Events Stream

For relay activity, tunnel status, and extension events, subscribe to the unified system events stream:

GET /api/events

This single persistent connection multiplexes all background events. Events are tagged with a type field using namespace prefixes:

PrefixEvents
relay.*Relay adapter activity, message delivery
tunnel.*Tunnel start/stop, URL changes
ext:{id}:*Extension-emitted events (namespaced per extension ID)

The DorkOS client uses a single GET /api/events connection for all background state, replacing the older per-resource SSE endpoints (/api/relay/stream, /api/tunnel/stream, etc.). Those endpoints no longer exist. External clients should use GET /api/events.

Session Write Coordination

POST /api/sessions/:id/messages prevents concurrent writes via client IDs.

Prop

Type

  • If another client holds the write lock, the server returns 409 with { error: "Session locked", code: "SESSION_LOCKED", lockedBy, lockedAt }
  • Locks auto-expire after 5 minutes
  • Locks are released when SSE connections close

Connection Lifecycle

Open the SSE connection

Send POST /api/sessions/:id/messages with message content. The response is Content-Type: text/event-stream.

Process streamed events

The server streams events as they arrive from the Claude Agent SDK. Update your UI in real time.

Handle interactive events

If approval_required or question_prompt arrive, respond via the appropriate endpoint before the stream continues.

Stream completes

On done, the message stream ends. Send another message or close the connection.

Subscribe for ongoing sync

For real-time updates from other clients or CLI writes, open a persistent session sync connection:

GET /api/sessions/:id/stream

For relay, tunnel, and extension events, use the unified system stream:

GET /api/events

Next Steps