register, login, or connect. It is the thing you watch after any account call — it holds the status, the reason a human is needed, the live view URL, and the final outcome.
Runs are created by POST /v1/registrations, POST /v1/registrations/:id/login, and POST /v1/connections. There is no endpoint that creates a run directly. Observe one by polling GET /v1/runs/:id or by subscribing to the run.blocked / run.completed / run.failed webhooks.
The run object
Lifecycle
completed and failed are terminal and never change afterwards — an abort that races a completion loses.
Blocked codes
Failure codes
A blocked-style code such as
CAPTCHA_REQUIRED can also appear on a failed run. That happens when the browser session had already ended, so takeover was impossible — the code is kept because it is the truthful reason, not because a human could have stepped in.List runs
GET /v1/runs
Every run in your org, newest first — the activity feed behind the dashboard.
200: an array of list rows. A list row is a different, narrower projection than the run object — it joins the identity’s email and the target app, and omits result, liveViewUrl, and gatewaySessionId. Fetch GET /v1/runs/:id for those.
Get a run
GET /v1/runs/:id
200: the run object. 404 {"error": "not found"} otherwise.
Resume a blocked run
POST /v1/runs/:id/resume
Hands control back to the agent after a human cleared the gate in the live view — completed the CAPTCHA, entered the code, clicked submit. The agent re-observes the page and continues from wherever the human left it. No body.
200: the run object with "status": "running" and "error": null. Writes a run.resume audit event.
409 {"error": "run is not blocked", "code": "NOT_BLOCKED", "status": "running"} when the run isn’t blocked — the body carries the status it actually has, which is usually the answer to why.
Take over a live run
POST /v1/runs/:id/intervene
Pauses a running or awaiting_verification run so a human can drive the browser through the interactive liveViewUrl. The browser session stays alive; POST /:id/resume hands back. No body.
200: the run object with "status": "blocked" and "error": { "code": "MANUAL_INTERVENTION", "detail": "paused for manual takeover" }. Writes a run.intervene audit event.
409 {"error": "run is not intervenable", "code": "NOT_INTERVENABLE", "status": "queued"} when the run isn’t running or awaiting_verification — a queued run has no browser yet, a blocked one is already paused, and a terminal one is done.
Abort a run
POST /v1/runs/:id/abort
Terminates a non-terminal run and stops its browser session. If the run completes at the same moment, the completion wins and you get the 409. No body.
200: the run object with "status": "failed" and "error": { "code": "ABORTED", "detail": "aborted by user" }. If the registration was still pending, it is marked failed; an already-active account is untouched. Writes a run.abort audit event.
409 {"error": "run is already terminal", "code": "ALREADY_TERMINAL", "status": "completed"} if it already ended.
An aborted run counts as
failed, and failed runs never count against your run quota.Get the run recording
GET /v1/runs/:id/recording
The run’s browser-session video — the fastest way to see what the agent actually did. Ready shortly after the run ends; poll while it processes.
This endpoint returns
200 even when the recording can’t be produced; only an unknown run id gives 404.
Related
Runs
What a run is and how the loop works.
Human takeover
The block, the live view, and the resume.
Webhooks
Get pushed the block instead of polling for it.
