progressItems tells you what happened; the live view shows
you the page it happened on — which is the difference between “the agent failed at the
payment step” and “oh, there is a full-screen coupon modal over the pay button”.
It is a live JPEG stream of the running browser, delivered over Server-Sent Events, rendered
into a self-contained page you can drop in an <iframe>.
Mint a token
Response (200)
url directly:
connecting…, live, ended: succeeded, disconnected).
The token is the access control
The checkout UUID alone grants nothing. The token is a signed JWT pinned to onecheckoutId claim and checked on every request to the embed routes — so a token minted for
one run cannot watch another, and knowing a checkout id is not enough to watch it.
This is not incidental. A live browser stream that authorises on an id you might have logged,
copied into a ticket, or leaked in a URL is a hole; requiring a separately minted,
short-lived, single-checkout credential closes it.
10 minutes
Fixed, not configurable.
expiresAt is authoritative. After it passes both embed routes
return 401 Invalid or expired embed token as plain text.one checkout
A token whose
checkoutId claim does not match the path segment is rejected, even if it is
otherwise valid and unexpired.mint a new one
There is no refresh. A run outlasting the window needs a fresh
POST /v1/checkouts/{id}/embed-token and a reload of the iframe src. Minting is cheap
and unlimited — for a long run, re-mint on a timer at roughly half the TTL.It is read-only
The stream carries frames out. Nothing goes in.permissions is ["read"] on the checkout’s browser object because that is the only mode
that exists. There is no click-through, no keyboard forwarding, no takeover. A viewer cannot
touch the merchant’s page, and a leaked token cannot be used to drive a purchase.
If you want to influence the run, that is what user actions are for. The
live view is a window, not a remote desktop.
Stream lifecycle
The embed page consumesGET /embed/{checkoutId}/stream?token=…, an SSE endpoint with four
event types:
Connecting to a checkout that is already terminal, or that has no browser session, emits
status then ended immediately rather than hanging. The viewer’s own connection uses a
separate observation channel, so watching never disturbs the run driving the page.
Gotchas
browser.embedTokenUrl is a pointer, not a viewable URL
browser.embedTokenUrl is a pointer, not a viewable URL
GET /v1/checkouts/{id} includes:POST to it to mint a token, and use the url that comes back.There is nothing to watch before the browser starts
There is nothing to watch before the browser starts
A
queued checkout has no browser session. The token mints fine, but the stream ends
immediately. Wait for status: "running" — or for browser to appear on the checkout —
before showing the frame.The stream ends when the checkout does
The stream ends when the checkout does
On a terminal status the stream sends
ended and closes. There is no replay: the live
view is live only. For after-the-fact review, progressItems and
receipt.evidence.url are what you have.Frames are JPEG, not video
Frames are JPEG, not video
Bandwidth scales with how much the page is changing. It is fine for an operator console
watching one or two runs; it is not designed for a wall of fifty concurrent streams.