SSE Streaming

Workflows can stream execution events in real time with Server-Sent Events (SSE). POST /api/workflows/{workflowid}/execute/stream

Workflows can stream execution events in real time with Server-Sent Events (SSE).

Endpoint

POST /api/workflows/{workflow_id}/execute/stream

This endpoint uses the same workflow-level authentication, rate limiting, and response caching rules as the standard execute endpoint documented in Webhooks.

Enabling SSE in the cURL Dialog

Open the Run with cURL dialog in the editor and enable SSE Streaming.

When enabled, Heym:

  • switches the generated command from /execute to /execute/stream
  • adds --no-buffer so streamed events appear immediately in terminal output
  • adds Accept: text/event-stream
  • lets you configure per-node node_start messages directly in the dialog

Event Protocol

Each SSE frame is newline-delimited JSON prefixed with data: .

Event typeWhen it is emittedKey fields
queuedThe workflow is at its concurrency limit; sent again whenever the run's place changesexecution_id, position, size, running, limit
execution_startedImmediately after the request is accepted, or when a queued run startsexecution_id, server_now_ms
node_startRight before a node begins executionnode_id, node_label, started_at_ms, optional message
node_retryWhen a node retriesnode_id, attempt, max_attempts
node_completeAfter a node finishesnode_id, node_label, status, output, execution_time_ms
final_outputWhen an Output node returns earlynode_label, output
execution_completeAfter the workflow endsstatus, outputs, execution_time_ms

Queued Runs

When the workflow is at its concurrency limit, the stream stays open and sends queued events while the run waits. A queued run executes in the background worker, possibly in another process, so its stream carries execution_started when the run starts and execution_complete when it ends, but no node_start, node_complete or token events in between. Its execution_complete has an empty node_results; open the run in Execution History for node detail. Closing the connection does not cancel a queued run. When the queue is full, the request is answered 429 before the stream opens.

Per-Node Start Messages

The cURL dialog stores SSE settings on the workflow itself:

  • Send start controls whether a node_start event includes a message
  • Start message defaults to [START] {nodeLabel}
  • clearing a custom message falls back to the default text
  • node_complete is always emitted and always includes the node output

Example stored config:

{
  "node-uuid-llm": {
    "send_start": true,
    "start_message": "[START] Draft reply"
  },
  "node-uuid-http": {
    "send_start": false,
    "start_message": null
  }
}

cURL Example

curl -X POST --no-buffer \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer <your-access-token>" \
  "https://app.heym.ai/api/workflows/{workflow_id}/execute/stream" \
  -d '{"message":"Hello"}'

Sample event stream:

data: {"type":"execution_started","execution_id":"...","server_now_ms":1735660800000}
 
data: {"type":"node_start","node_id":"...","node_label":"LLM","started_at_ms":1735660800000,"message":"[START] LLM"}
 
data: {"type":"node_complete","node_id":"...","node_label":"LLM","status":"success","output":{"text":"Hello world"},"execution_time_ms":312}
 
data: {"type":"execution_complete","status":"success","outputs":{"LLM":{"text":"Hello world"}},"execution_time_ms":315}

Security Notes

  • anonymous, jwt, and header_auth workflows behave the same as the regular webhook endpoint
  • response caching still applies when cache_ttl_seconds is configured
  • workflow rate limits still apply when rate_limit_requests and rate_limit_window_seconds are configured
  • test_run bypasses both cache and rate limiting, the same way it does for /execute

File uploads and run widgets

POST /api/file-intake/u/{token} (multipart) and POST /api/dashboards/widgets/{widget_id}/run (JSON inputs) accept Accept: text/event-stream. They send an immediate SSE comment and a heartbeat every 15 seconds while the workflow runs, followed by one JSON event:

data: {"type":"result","result":{"run_id":"...","status":"success","output":{}}}

A failure after streaming starts sends {"type":"error","status":500,"detail":"..."}. Validation and access refusals before streaming keep their HTTP status. Without that Accept header, these endpoints keep their existing JSON response. A disconnected listener does not cancel a run that has already started; check execution history before submitting it again. File runs and run widgets appear in active executions while running and retain the same execution ID in history.