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
/executeto/execute/stream - adds
--no-bufferso streamed events appear immediately in terminal output - adds
Accept: text/event-stream - lets you configure per-node
node_startmessages directly in the dialog
Event Protocol
Each SSE frame is newline-delimited JSON prefixed with data: .
| Event type | When it is emitted | Key fields |
|---|---|---|
queued | The workflow is at its concurrency limit; sent again whenever the run's place changes | execution_id, position, size, running, limit |
execution_started | Immediately after the request is accepted, or when a queued run starts | execution_id, server_now_ms |
node_start | Right before a node begins execution | node_id, node_label, started_at_ms, optional message |
node_retry | When a node retries | node_id, attempt, max_attempts |
node_complete | After a node finishes | node_id, node_label, status, output, execution_time_ms |
final_output | When an Output node returns early | node_label, output |
execution_complete | After the workflow ends | status, 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_startevent includes amessage - Start message defaults to
[START] {nodeLabel} - clearing a custom message falls back to the default text
node_completeis 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, andheader_authworkflows behave the same as the regular webhook endpoint- response caching still applies when
cache_ttl_secondsis configured - workflow rate limits still apply when
rate_limit_requestsandrate_limit_window_secondsare configured test_runbypasses both cache and rate limiting, the same way it does for/execute
Related
- Webhooks – Standard execute endpoint and cURL dialog
- Parallel Execution – How streaming fits into Heym's DAG scheduler
- Canvas Features – Editor controls including Run with cURL
- Security – Authentication and secret handling
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.