API reference
Events
Every run records an ordered stream of events in the open event protocol, protocol: 0, stored verbatim: protocol, run_id, sequence, timestamp, type, runner, and data. Sequences are gapless from 1. Nothing is appended after a terminal event (run.completed, run.failed, run.cancelled): whichever lands first wins, so a cancel racing a completion records exactly one ending.
Read events#
GET /v1/runs/:id/events
Key runs:read, or session: member.
| Query | Type | Meaning |
|---|---|---|
after |
integer, at least 0 | Return events after this sequence. Default 0. |
limit |
integer, 1 to 500 | Default 100. |
Response 200, with cache-control: no-store:
{
"events": [
{
"protocol": 0,
"run_id": "wb_d8m99qyw2e4qqhitfff63f1n",
"sequence": 1,
"timestamp": "2026-10-28T14:05:40.112Z",
"type": "run.started",
"runner": "opencode",
"data": {}
}
],
"next": 1
}next is the sequence to pass as after for the following page.
Stream events#
Send the same request with Upgrade: websocket to open a stream. The server replays every event after after, then sends each new event as one JSON text frame, and closes with code 1000 after the terminal event.
- The stream is read only. Send commands over HTTP; see Commands.
- A socket lives 15 minutes on the credential it connected with. After that, the next frame closes it with code 4001 (
reauthenticate) instead; connect again at once with the last sequence you saw asafter. - A client that disconnects reconnects with the last sequence it saw.
- A WebSocket upgrade authenticated by a session cookie must come from the dashboard origin, or it is refused with 403. A key with an
Authorizationheader needs no origin.
What events carry#
Events never contain secret values. Before an event is stored, every value injected into the run's sandbox is replaced with [redacted]: the organization's secrets the run received, the runner's password, the image pull token, and the run's GitHub credential.
Notable events for integrations:
| Type | Meaning |
|---|---|
repository.preparing, repository.ready |
Checkout started and finished, with repository and revision. repository.ready may carry warnings, such as a gh that could not be installed. |
input.requested, question.requested |
The run needs an answer. Its status is needs_input. |
plan.updated |
The agent's whole todo list. |
usage.updated |
Model tokens and cost for a step. |
outcome.available |
The outcome was kept, before the terminal event. |
preview.available |
A server was announced. |
delivery.completed |
The agent opened or updated a pull request: the run, outcome, repository, base revision, branch, base branch, head commit and tree, and pull request. Once per pull request and head commit. |
run.completed, run.failed, run.cancelled |
The end. The terminal event may carry infrastructure, the sandbox's duration and resources. |
Realtime#
GET /v1/realtime
Key runs:read, or session: member. A WebSocket on the organization's realtime channel. Every socket in the organization receives every frame.
| Query | Type | Meaning |
|---|---|---|
organization |
slug | For a session. |
Each frame is JSON with a type and a topic:
type |
topic |
Body |
|---|---|---|
run |
run:<id> |
run: id, status, previews, previews_until. Sent when a run moves to needs_input or ends, in a project or not, and when its previews or preview window change. Never a link. |
task |
project:<slug> |
task: a project task, as in Projects. |
project |
project:<slug> |
project: a project, as in Projects. |
connection |
connection:<user id> |
connection: user_id, key_id, client_name. An agent tool a person connected used its key for the first time. |
Frames are lossy: a client that reconnects loads current state again. The socket is read only, and one that sends a message is closed. It lives 15 minutes per credential, then closes with 4001 like a run event socket. The MCP wait tool uses this socket to wait on many runs.