Skip to content

Streaming

Stream a deployment's build answers with Server-Sent Events: the build's steps and log lines as they happen. It's what hiraiship deploy prints while you wait.

Terminal
curl -N "https://api.hiraiship.com/api/v1/deployments/$DEPLOYMENT_ID/stream" \
  -H "Authorization: Bearer $HIRAISHIP_TOKEN"

Events

Each event has a name and a JSON data line, and a log event an id too:

Stream
event: step
data: {"step":"installing","status":"running","startedAt":"2026-10-03T16:40:16.000Z","finishedAt":null}
 
event: log
id: 4812
data: {"step":"installing","stream":"stdout","message":"added 212 packages in 9s","timestamp":"2026-10-03T16:40:25.000Z","id":4812}
 
event: done
data: {"status":"success","dispatchStatus":"live","publishDurationMs":6840}
EventWhendata
statusWhile the deployment waits, and when it leaves the queue.status, queuePosition (1 is next), startedAt, queueReason (plan_limit or capacity, see Build queue)
stepA step starts or ends: preparing, installing, building, packaging.step, status (running, success, failed), startedAt, finishedAt
logA line of the build's output.id, step, stream (stdout or stderr), message, timestamp
doneThe deployment succeeded or failed. The stream closes after it.status, dispatchStatus (live once it serves traffic), publishDurationMs

A new connection replays everything recorded so far, then continues live: you never miss the start of a build by connecting late.

While nothing happens, for example during a long install, the stream sends a comment line every 15 seconds so that proxies keep the connection open. Comments start with : and carry no event; skip them.

Stream
: keepalive

Reconnecting

To pick up where a dropped connection left off, send the id of the last log line you received as Last-Event-ID. The stream sends the steps and the queue status again, then only the log lines after that id. The browser's EventSource does this on its own when it reconnects.

Terminal
curl -N "https://api.hiraiship.com/api/v1/deployments/$DEPLOYMENT_ID/stream" \
  -H "Authorization: Bearer $HIRAISHIP_TOKEN" \
  -H "Last-Event-ID: 4812"

From Node.js

The browser's EventSource can't send an Authorization header, so read the stream with fetch:

follow-deployment.mjs
const response = await fetch(`https://api.hiraiship.com/api/v1/deployments/${process.env.DEPLOYMENT_ID}/stream`, {
  headers: { Authorization: `Bearer ${process.env.HIRAISHIP_TOKEN}` },
});
 
const decoder = new TextDecoder();
let buffer = '';
for await (const chunk of response.body) {
  buffer += decoder.decode(chunk, { stream: true });
  let end;
  while ((end = buffer.indexOf('\n\n')) !== -1) {
    const lines = buffer.slice(0, end).split('\n');
    buffer = buffer.slice(end + 2);
    const event = lines.find((line) => line.startsWith('event: '))?.slice(7);
    const data = JSON.parse(lines.find((line) => line.startsWith('data: '))?.slice(6) ?? 'null');
 
    if (event === 'log') process.stdout.write(`${data.message}\n`);
    if (event === 'done') process.exit(data.status === 'success' ? 0 : 1);
  }
}

Without a stream

If your client can't hold a connection open, poll Get a deployment until status is success or failed, and Get a deployment's build steps for the steps. A few seconds between calls is plenty.