Skip to main content

Streaming

Windmill streams two kinds of data from a running job over Server-Sent Events (SSE):

  • Progress: the status of each step of a flow as it runs, plus logs and explicit progress. Use it to show users where a long-running flow is at, from your own frontend or backend.
  • Output: text that a script or AI agent step yields while it runs, such as LLM tokens. Use it to display a response as it is being generated.
I want to...Use
Show which step a flow is at, with logs and progressRun the flow asynchronously, then subscribe to jobs_u/getupdate_sse
Get the text a script or flow yields, in one requestSSE stream webhook (jobs/run_and_stream)
Expose the text stream on a custom URLHTTP route in Sync SSE mode
Follow an AI agent's tool calls and tokensAI agent streaming

Stream flow progress​

Every job, and flows in particular, can be followed live with the job progress SSE endpoint:

GET /api/w/<workspace>/jobs_u/getupdate_sse/<job_id>

Each update event carries the flow_status of the flow whenever it changes. Its modules array has one entry per top-level step, with the step id, its type (WaitingForPriorSteps, WaitingForExecutor, WaitingForEvents for a step suspended until approval, InProgress, Success or Failure) and the job id of the step once it has started. Loops and branches also report their iteration or branch in the entry.

{
"type": "update",
"running": true,
"flow_status": {
"step": 1,
"modules": [
{ "type": "Success", "id": "a", "job": "0199...", "skipped": false },
{ "type": "InProgress", "id": "b", "job": "0199..." },
{ "type": "WaitingForPriorSteps", "id": "c" }
]
// other flow_status fields omitted
}
}

The last event has "completed": true and a job field with the completed job, including its result. The full list of fields is in Job progress event response.

To stream the progress of a flow you trigger:

  1. Start it with the asynchronous webhook, which returns the job id right away. The path is jobs/run/f/<flow_path>, so a flow at f/examples/onboarding is started with jobs/run/f/f/examples/onboarding.
  2. Open the SSE stream on that job id. Pass fast=true so the server checks for updates every 100ms, then every 500ms, before settling at every 3 seconds (without it, it checks every 3 seconds from the start). Pass no_logs=true if you only need step statuses.
BASE="https://app.windmill.dev/api/w/<workspace>"

JOB_ID=$(curl -s -X POST "$BASE/jobs/run/f/f/examples/onboarding" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com"}')

curl -N "$BASE/jobs_u/getupdate_sse/$JOB_ID?fast=true&no_logs=true" \
-H "Authorization: Bearer $TOKEN"

Add get_progress=true to also receive the progress percentage set from code with explicit progress. The same endpoint works for scripts, where new_logs gives the logs as they are written.

The SSE stream webhook does not include flow_status: it only sends the result stream and the final result. Use the two-step approach above when you need step-level progress.

Stream output​

Job result streaming​

Scripts in Python and TypeScript can stream back results as a text stream. The stream exists while the job is running, and the full content becomes the result once the job completes. In a flow, the stream comes from the last step, or from the early return step when one is set.

AI agent streaming​

AI agent steps support streaming token deltas, tool calls and tool results as structured JSON payloads.

Consuming an output stream​

Output streams can be consumed from: