CAPABILITY · OBSERVABILITY
Watch the session while it runs, keep the recording after
POST /api/v1/live streams the browser back as Server-Sent Events while the agent works, and an opted-in run keeps a durable video you can fetch long afterwards.
POST /api/v1/live · GET /api/v1/runs/{id}/videoCapability
Inside live view & session video
Headless automation is a black box until it breaks. The live endpoint takes the same body as a run and answers with a Server-Sent Events stream instead of a result: a meta frame carrying the runId, then JPEG frames as the browser paints, then a terminal result frame. Recording is opt-in per run and tiered by plan, and a recorded run is replayable from durable storage after the fact.
A real stream, not a poll
The response is text/event-stream: `event: meta` with the runId, then `event: frame` JPEG frames, then `event: result`. No polling loop and no second connection.
Recording is opt-in and tiered
Pass record: true on a run to keep a video. It is opt-in because storage is not free, and the size ceiling is tiered by plan — a recorded run is also annotated by default so the video shows the cursor travel.
The recording ends on the end state
A recorded run holds a video tail so the last frame is the run's RESULT page rather than the moment before the final click, and the file is only published once it has stopped growing.
Fetched from durable storage
GET /api/v1/runs/{id}/video serves the bytes from storage, lazily persisted from the engine on first view. A run that was not recorded answers 404 rather than pretending.
How it works
The mechanism, in execution order
4 stages, in the order the runtime performs them — not a summary of them.
- 1Start with a streamPOST /api/v1/live with the same body as /run. The response is an SSE stream, and the meta frame gives you the runId immediately.
- 2Watch it paintFrame events carry JPEG snapshots of the session as it executes. A live run can also be interacted with mid-flight.
- 3Settle on the terminal frameThe result event ends the stream. Billing settles once on that frame or on disconnect — a failed run with no result is refunded in full.
- 4Replay laterA run created with record: true keeps a video. GET /api/v1/runs/{id}/video streams it back.
In practice
A call you can paste and run
One request, one stream: meta, frames, result. The recording is a separate fetch once the run is over.
# Stream the browser while it works
curl -N https://twin-browser.com/api/v1/live \
-H "Authorization: Bearer $TWIN_API_KEY" \
-H "content-type: application/json" \
-d '{"url":"https://app.example.com/reports",
"prompt":"open the Q2 report and export it as CSV",
"success":{"kind":"textVisible","value":"Export complete"},
"record":true}'
# event: meta
# data: {"runId":"run_…"}
# event: frame
# data: {"jpeg":"…base64…"}
# event: result
# data: {"success":true,"steps":6}
# Afterwards — the durable recording
curl "https://twin-browser.com/api/v1/runs/run_…/video" -H "Authorization: Bearer $TWIN_API_KEY" -o run.webm- Start with a streamdone
- Watch it paintrunning
- Settle on the terminal framequeued
- Replay laterqueued
What it costs, how to switch it on
Priced from the same rate card the API serves
Every credit figure on this page is read from the rate card behind GET /api/v1/pricing — it is not typed into the copy, so it cannot drift from what you are billed.
10-credit floor per streamed run
A live run is metered exactly like POST /run: higher-of(10-credit floor, metered model + compute + egress), settled once on the terminal frame or on disconnect. A run that failed or produced no result is refunded in full. Fetching a recording afterwards is not a separate charge; the storage ceiling is what your plan tiers.
Turning it on
- 1Swap the endpointPOST /api/v1/live takes the same body as POST /api/v1/run. Nothing else changes.
- 2Opt into recordingAdd record: true to any run, job or live call. It is off by default because storage is metered against your plan.
- 3Keep the runIdThe meta frame carries it. It is how you fetch the video, read the run, or drive it if it parks.
- 4Or record without watchingrecord_run and record_job over MCP submit a run that captures video without holding a stream open.
At a glance
The contract, in the fewest rows that say it
| Property | Twin Browser |
|---|---|
| Stream | POST /api/v1/live (text/event-stream) |
| Frames | meta → frame* → result |
| Recording | `record: true` on the run body |
| Playback | GET /api/v1/runs/{id}/video |
| Annotation | On by default for a recorded run |
| Settlement | Once, on the terminal frame or disconnect |
Limits
Where it stops, and what it deliberately does not do
A capabilities page with no limits section is a brochure. These are the ceilings, the defaults that will surprise you, and the things this capability is not.
Limits and defaults
- Recording is opt-in per call, and the video size ceiling is tiered by plan.
- GET /runs/{id}/video answers 404 when the run was not recorded or the recording is no longer available — a missing video is not an error state to retry.
- The stream settles billing once. A client that disconnects early still settles; it does not get a free run.
- Live frames are JPEG snapshots of the session, not a lossless capture — the durable recording is the artefact for review.
What it does not do
- It does not record every run silently. Nothing is captured unless you asked for it.
- It does not keep recordings without bound. Storage is capped per plan, and an old recording can be gone.
- It does not stream a run started elsewhere. To watch an already-parked run, use GET /runs/{id}/stream instead.
- It does not expose the video as a public URL. Playback is authenticated on your key like every other endpoint.
FAQ
Questions about live view & session video
Can I watch a run while it happens?
Is every run recorded?
Why does the recording show the result page and not the last click?
How do I watch a run that has already paused?
Keep reading
The rest of the platform
The capabilities this one works with, the tools that expose it, the guides that use it end to end, and the vocabulary behind it.
Human-in-the-loop handoff
A 2FA prompt, an approval push or a CAPTCHA the agent cannot clear returns status "paused" with a live session — resolve it automatically from a connected inbox, hand back a code, or drive the page yourself.
Async jobs & webhooks
Submit a run as a background job and get an id immediately — then poll it, stream its status, or receive an HMAC-signed webhook when it completes.
Deterministic replay
A successful run is minimized into a named, versioned skill — an ordered action path with its variable parts lifted out — and replaying it is a program, not a prompt.
Tools that expose it
Guides
Related reading
Put your agent to work. Keep the decision.
Start free. Hand your agent a goal on a site you authorize, set the guardrails, and let the first successful run compile the skill every run after it replays.