Streaming Execution
Run a workflow over a Server-Sent Events stream and observe each node as it executes.
The streaming execution endpoint runs a workflow and pushes per-node progress over a long-lived HTTP connection using Server-Sent Events (SSE). The editor's Execution Panel → Current uses this endpoint to render live runs; you can also call it directly when you want real-time visibility into a run from your own code.
Endpoint
POST /api/workflows/[workflowId]/execute/stream
Replace [workflowId] with the ID of the workflow you want to run.
When to use streaming vs the regular execute endpoint
| You want… | Use |
|---|---|
| Fire-and-forget execution, no progress feedback needed | POST /api/workflows/[id]/execute (the regular endpoint) |
| To trigger from outside FlowTrux (CRM, partner system, cron in another platform) | Webhook trigger with webhookResponse: minimal |
| Real-time per-node feedback in your own UI / CLI / monitoring tool | This endpoint |
| Early cancel from the client | This endpoint - closing the connection aborts the run |
The regular execute endpoint returns one JSON response when the run finishes (or when the configured webhook response mode is satisfied). The streaming endpoint emits an event every time a node starts or completes, plus terminal complete / error / cancelled events.
Authentication
Same as POST /api/workflows/[id]/execute: the request must include a valid session for a user with the canExecute permission on the workflow's workspace. There is no separate API-key path - use the webhook endpoint when you need an external integration.
Request
- Method:
POST - Headers:
Content-Type: application/json - Body (optional): JSON object that becomes the manual trigger payload, available in the workflow as
{{trigger.data}}.
If the workflow's Trigger node has staticData configured, the body merges over it (request body wins on key conflicts).
Response
The response is a long-lived HTTP connection with:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Events are emitted in the SSE wire format - one event: line, one data: line (JSON), and a blank line:
event: node_start
data: {"nodeId":"agent-1","nodeName":"Classify ticket","nodeType":"agent"}
event: node_complete
data: {...}
event: complete
data: {"success":true,"executionId":"exec-abc123"}
Event types
| Event | Emitted when | Payload |
|---|---|---|
start | The run begins, before any node executes | { workflowId, workflowName, timestamp } |
node_start | A node enters execution | { nodeId, nodeName, nodeType } |
node_complete | A node finishes (success or error) | Full ExecutionLog - nodeId, nodeName, status, output, durationMs, input, plus debug fields (LLM request, tool calls, external requests, logic condition/variables) where applicable |
complete | The run finishes successfully | { success: true, executionId, error: null, cancelled: false } |
error | The run finishes with an error | { success: false, executionId, error: "<message>", cancelled: false } |
cancelled | The run was aborted (client closed the connection or used the editor Stop button) | { success: false, executionId, error: "Execution cancelled by user", cancelled: true } |
Each run produces exactly one terminal event - complete, error, or cancelled. After the terminal event, the server closes the stream.
Cancellation
Closing the HTTP connection from the client side aborts the run. The server listens for the request abort signal and propagates it through the engine - the currently-running node is cancelled (where supported), no further nodes execute, and the run finishes with status CANCELLED in the execution history.
In fetch-based clients, this means calling controller.abort() on the AbortController you passed to fetch.
Examples
curl
curl -N -X POST \
https://your-domain.com/api/workflows/wf-12345/execute/stream \
-H "Content-Type: application/json" \
-H "Cookie: <your-session-cookie>" \
-d '{"customerId":"cust-789"}'
The -N flag disables curl's output buffering so you see each event as it arrives.
JavaScript (browser / Node 18+)
The Fetch API exposes the response body as a ReadableStream; parse SSE frames manually:
async function streamExecution(workflowId, triggerData) {
const controller = new AbortController();
const response = await fetch(
`/api/workflows/${workflowId}/execute/stream`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(triggerData ?? {}),
signal: controller.signal,
}
);
if (!response.ok || !response.body) {
throw new Error(`Stream failed: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// SSE frames are separated by "\n\n"
const frames = buffer.split("\n\n");
buffer = frames.pop() ?? "";
for (const frame of frames) {
const eventLine = frame.split("\n").find((l) => l.startsWith("event: "));
const dataLine = frame.split("\n").find((l) => l.startsWith("data: "));
if (!eventLine || !dataLine) continue;
const event = eventLine.slice(7).trim();
const data = JSON.parse(dataLine.slice(6));
switch (event) {
case "node_start":
console.log("→", data.nodeName);
break;
case "node_complete":
console.log("✓", data.nodeName, `${data.durationMs}ms`);
break;
case "complete":
case "error":
case "cancelled":
console.log(event, data);
return; // stream ends after terminal event
}
}
}
}
// Cancel mid-run:
// controller.abort();
The browser's built-in
EventSourceis not suitable here because it doesn't supportPOSTbodies. Usefetchwith a manual SSE parser, as shown above.
Notes
- SSRF and rate limiting - the streaming endpoint goes through the same access checks as the regular execute endpoint (
verifyWorkflowAccesswithcanExecute). It does not have a separate quota. - Background runs. If you start a streaming run, navigate away from the editor, and come back, the run no longer streams - but it shows up under the Execution Panel → Current → Background runs section because the engine kept executing after the connection closed (subject to your trigger configuration).
- Concurrency. Multiple SSE connections to the same workflow are independent runs - there is no streaming-merge view.
Related
- Webhooks - for triggering workflows from external systems
- Execution Panel - the editor surface that consumes this endpoint live