Stream a run
GET/
Sends a run’s progress as it happens, as Server-Sent Events.
Needs an API token with the Read permission for runs (run:read).
Parameters
Path
idRequired · UUIDThe run’s ID.
What arrives
First a connected event. Then one unnamed event for each change: a JSON object with type, runId, nodeId, status, data and error. A : ping comment comes every 25 seconds; ignore it.
The stream closes after run_completed, run_failed or run_cancelled. A reconnect event means connect again, then read the run.
Nothing is replayed: you get only what happens after you connect. Step output isn’t streamed: read it with Get a run.
Responses
- 200OK
The stream is open. It ends after the run’s last event.
- 204No Content
The run is already over, so no stream opens. Read it with Get a run.
idisn’t a UUID. A JSON error, not a stream.- 403
FORBIDDENThe token lacks the Read permission for runs.
- 404
NOT_FOUNDNo run with this ID in the token’s workspace.
This person or token already follows 50 runs on this server.
Retry-Aftersays when to try again.The run couldn’t be looked up just now. Retry after
Retry-After; nothing is wrong with the run.
Any call can also answer: 401 UNAUTHORIZED · 401 TOKEN_ENDED · 403 TOKEN_SURFACE_OFF · 403 FEATURE_OFF · 403 EMBED_NOT_ALLOWED · 403 EMBED_NOT_AVAILABLE · 403 ACCOUNT_SUSPENDED · 429 RATE_LIMITED · 503 BUSY · 503 PAUSED. See Errors.
Request
GORUNNER="https://api.gorunner.io"
GORUNNER_TOKEN="grt_…" # needs Read for runs
RUN_ID="your-run-id" # data.id from Run a workflow
curl -N "$GORUNNER/api/runs/$RUN_ID/stream" \
-H "Authorization: Bearer $GORUNNER_TOKEN" \
-H "Accept: text/event-stream"const res = await fetch(`${GORUNNER}/api/runs/${RUN_ID}/stream`, {
headers: {
Authorization: `Bearer ${TOKEN}`,
Accept: "text/event-stream",
},
});
if (res.status === 204) {
// already over: read it with Get a run
}
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
for (;;) {
const { value, done } = await reader.read();
if (done) break;
console.log(value); // "data: {…}" lines
}import os, requests
GORUNNER = "https://api.gorunner.io"
TOKEN = os.environ["GORUNNER_TOKEN"]
RUN_ID = os.environ["RUN_ID"]
with requests.get(
f"{GORUNNER}/api/runs/{RUN_ID}/stream",
headers={"Authorization": f"Bearer {TOKEN}",
"Accept": "text/event-stream"},
stream=True,
) as res:
for line in res.iter_lines(decode_unicode=True):
if line.startswith("data: "):
print(line[6:])Response · 200 OK, a step, then the end
event: connected
data: {"runId":"8f0c41d2-6b7e-4c1a-9d3f-2a5b7c9e1f04"}
data: {"type":"step_started","runId":"8f0c41d2-…","nodeId":"parse_csv","status":"running","data":{"attempt":0}}
data: {"type":"step_completed","runId":"8f0c41d2-…","nodeId":"parse_csv","status":"completed","data":{"attempt":0,"duration_ms":3}}
data: {"type":"run_completed","runId":"8f0c41d2-…","status":"completed"}
: pingEvery field
It takes no body.
Response · 200 OK, each event
typestringWhat happened.
run_completed,run_failedandrun_cancelledare TERMINAL — nothing follows them, and the server closes the stream after sending one.run_pausedmeans the run suspended on a durable wait and may resume later (arun_resumedarrives on the SAME run id if it does, though a client that disconnected will have to re-subscribe).step_failedis sent for each failed attempt, so a step that will retry sendsstep_failedand thenstep_retrying, which is informational: the step has not failed the run yet.One of
run_started,run_completed,run_failed,run_cancelled,run_paused,run_resumed,step_started,step_completed,step_failed,step_retrying,step_skipped.runIdUUIDThe run this event belongs to.
nodeIdstringFor
step_*events (andrun_paused/run_resumed), the definition step NAME the event is about. Empty on whole-run events.statusstringThe status the subject moved to. Note
retryingappears here but is NOT a persisted step status.One of
running,completed,failed,cancelled,paused,skipped,retrying.dataobject or nullEvent-specific metadata, never step output.
step_started:{attempt}(none on a Branch or Switch). A wait’sstep_completedafter a resume has none.step_completed:{duration_ms, attempt}(andpinned: truewhen frozen test data was replayed instead of executing); a branching step’s (Branch or Switch) is{duration_ms, matchedBranches, matchedBranch}: the 0-based positions of the arms it took (empty when only Otherwise ran) and the first one’s name, orotherwise.step_retrying:{attempt, maxRetries, delay_ms}.run_paused:{waitStep, resumeMode, resumeAt?}. No event carries a resume token.errorstringThe failure message on
step_failed,step_retryingandrun_failed, or the cancellation reason onrun_cancelled. Absent otherwise.
204 No Content: no body.
What does a paused run send?
A run_paused event says what the run waits for: waitStep, resumeMode and, for a set time, resumeAt. It never carries a link that decides the run.