SSE Streaming
Server-Sent Events for harness session execution and job event proxying
Server-Sent Events (SSE) stream updates one way over plain HTTP: the model's response as it is generated, or a job's progress as it runs, with no socket to manage. Reach for SSE when you want streaming that works through restrictive proxies, or when you need the POST-based harness execute stream. SSE is the only real-time mechanism the platform offers external clients. There is no public WebSocket endpoint; see WebSocket Protocol.
SSE Streaming
Server-Sent Events (SSE) provide two streaming capabilities:
- Harness session execution — the turn's text, tool calls, jobs and sub-agents as typed events
- Job event proxy — forwarding WebSocket job updates as SSE
Harness Session SSE
When you call POST /api/harness/sessions/:id/execute, the response is an SSE stream.
Headers
Event Format
Each event is a single data: line holding a flat JSON object; type names the event and its fields sit beside it:
Stream Termination
A completed stream ends with a sync.completed event. A stream that fails ends with an error event whose code is EXECUTION_ERROR. Treat either one, or the connection closing, as the end of the turn.
Event types
See Message Execution for the full reference. The ones most clients handle:
| Category | Events |
|---|---|
| Message | message.delta, message.complete, message.citations, content.reset, content.correction |
| Tool | tool.started, tool.delta, tool.completed, tool.failed |
| Job | job.dispatched, job.progress, job.completed, job.failed |
| Interrupt | interrupt.requested, interrupt.resolved |
| Turn | turn.started, turn.completed, error |
| Sync | sync.completed, sync.conflict |
Job Event Proxy SSE
The job event proxy opens the compute backend's job WebSocket on the server and re-emits each update as SSE, so the client never connects to the backend directly.
Authenticated
GET /api/v1/jobs/:jobId/events (reference) needs a signed-in session: a Clerk session JWT in the Authorization header, or the session cookie a browser on the same origin sends. An ivy_ API key alone is refused with 503; poll GET /api/v1/jobs/:jobId from a server that only holds a key. The route checks that the caller can read the job.
Guest
GET /api/v1/jobs/:jobId/guest-events?guest_id=guest_... serves jobs started from a guest session. It needs no credential; the backend refuses the stream if guest_id does not match the job.
Event names
Each SSE event carries a name, so listen by name rather than on message:
| Event | When |
|---|---|
subscribed | The upstream job socket is open |
job_update | Status or progress changed; data.status holds the job status |
error | The upstream connection failed |
close | The stream is ending |
Client Implementation
For GET Endpoints (Job Events)
In a signed-in browser on the same origin, use the native EventSource API; it sends the session cookie and reconnects on its own. EventSource cannot set an Authorization header, so from a server, use a Clerk JWT, or poll GET /api/v1/jobs/{jobId}. The job stream does not accept an API key.
For POST Endpoints (Harness Execute)
EventSource only supports GET. For the harness execute endpoint (POST), use fetch with ReadableStream:
WebSocket vs SSE
External clients cannot open a job WebSocket. The job event proxy is how you follow a job from outside the platform.
When to Use Which
- SSE (EventSource): Job event proxy from a signed-in browser on the same origin
- SSE (fetch): harness session execution (POST)
- Polling:
GET /api/v1/jobs/{jobId}when you only need to check a job occasionally
Error Recovery
SSE Auto-Reconnect (EventSource)
EventSource automatically reconnects on connection drop. Override with:
Fetch Stream Error Recovery
For POST-based SSE (harness execute), implement manual reconnection:
Read next
- WebSocket Protocol — why there is no public job socket
- Message Execution — harness SSE event reference
- Jobs API — HTTP polling fallback
Was this page helpful?