GoRunner docs
Search or askSearch⌘K

Follow a run as it happens

Open an event stream on a run, and see each step start and finish as it happens.

4 min · Needs curl and jq

1.Start a run

Start a run and keep its ID. Any run works, however it started: Run a workflow, a webhook or a schedule.

Open the stream straight after. It has no replay: an event from before you connected is gone.

bash
RUN=$(curl -s -X POST "$GORUNNER/api/workflows/$ID/run?environment=production" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Ada Lovelace"}' | jq -r .data.id)

2.Open the stream

Ask for /api/runs/{id}/stream with Accept: text/event-stream. The token needs run:read.

The first event is connected. Each one after is a JSON object on a data: line. Here the first step had already finished, so the stream starts at the wait.

You should see run_completed, then the stream ends by itself.

bash
curl -N "$GORUNNER/api/runs/$RUN/stream" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" \
  -H "Accept: text/event-stream"
Output
event: connected
data: {"runId":"a274b3d9-…"}

data: {"type":"run_paused","runId":"a274b3d9-…","nodeId":"wait","status":"paused","data":{"resumeAt":"2026-10-25T18:22:09Z","resumeMode":"time","waitStep":"wait"}}

data: {"type":"run_resumed","runId":"a274b3d9-…","nodeId":"wait","status":"running"}

data: {"type":"step_completed","runId":"a274b3d9-…","nodeId":"wait","status":"completed"}

data: {"type":"step_started","runId":"a274b3d9-…","nodeId":"lower","status":"running","data":{"attempt":0}}

data: {"type":"step_completed","runId":"a274b3d9-…","nodeId":"lower","status":"completed","data":{"attempt":0,"duration_ms":0}}

data: {"type":"run_completed","runId":"a274b3d9-…","status":"completed"}

3.Read the events

run_started
The run started.
step_started
A step started. data.attempt counts from 0.
step_completed
A step finished, in data.duration_ms. A Branch adds which arms it took: data.matchedBranches and data.matchedBranch.
step_retrying
A step failed and will try again: attempt, maxRetries, delay_ms, after a step_failed for that try.
step_failed, step_skipped
A try of a step failed, with error, or the step was skipped.
run_paused, run_resumed
The run waits on a Delay or a person, then goes on.
run_completed, run_failed, run_cancelled
The end. Nothing follows.

nodeId is the step’s name. A step’s output isn’t in the stream: read it with Get a run.

const res = await fetch(`${GORUNNER}/api/runs/${RUN}/stream`, {
  headers: { Authorization: `Bearer ${TOKEN}`, Accept: "text/event-stream" },
});
if (res.status === 204) process.exit(0); // already over: read the run
const decoder = new TextDecoder();
let buffer = "";
for await (const chunk of res.body) {
  buffer += decoder.decode(chunk, { stream: true });
  let end;
  while ((end = buffer.indexOf("\n\n")) >= 0) {
    const frame = buffer.slice(0, end);
    buffer = buffer.slice(end + 2);
    const data = frame.split("\n").find((l) => l.startsWith("data: "));
    if (!data || /^event:/m.test(frame)) continue; // connected, reconnect
    const ev = JSON.parse(data.slice(6));
    console.log(ev.type, ev.nodeId ?? "", ev.status);
  }
}

4.When it ends, or breaks

The run ends
The server closes the stream after the last event.
Already over
Opening it answers 204 No Content. Read the run instead.
event: reconnect
The server is going away, or you read too slowly. Connect again, then read the run for what you missed.
: ping
Every 25 seconds, to keep the line open. Ignore it.
Too many
One token may hold 50 streams on one server. The next is 429 TOO_MANY_STREAMS.

A run_paused event never carries a resume link. Read a waiting step’s resumeUrl from its output with Get a run.

Checked against 12d85b3b