Create a checkout
201.
Headers
string
Makes this create safe to retry. A second
POST with the same key returns the original
checkout with 200 instead of starting a second run; the same key with a different body is a
409.Strongly recommended on every create. Without it, a POST that times out after we accepted it
is indistinguishable from one that never landed, and the retry buys the item twice. Empty or
whitespace-only is treated as absent.A key lives 24 hours from the creation of the checkout that claimed it. After that it is
released and the same string starts a new purchase.See Idempotent creates below and
Idempotency.Body
The top level is strict: an unknown field is a400, not a field we drop for you. A misspelled
subjectt would otherwise have returned 201 with an untagged checkout the whole account can read.
target, constraints and metadata stay permissive inside — they are extension points.
400 — a misspelled field
object
required
string
UUID of a buyer profile you own. Must exist and belong to your
tenant, or you get
400 buyerProfileId not found.It is checked against this checkout’s subject, not the key’s. A
subject-bound key reaching for another end user’s profile gets that same
400 — from where it stands the profile does not exist. A tenant-wide key naming one end user’s
profile alongside another’s subject gets a 403 that names both. An untagged profile on a
tagged checkout is allowed, and so is the reverse.Without one the agent has no address or email, and most checkouts cannot be completed.string
Your own id for the end user this purchase is for. 1 to 200 characters after trimming, opaque to
us, never sent to the merchant. Comes back on the checkout when set, and omitted when not.Empty or whitespace-only is a
400 — a subject that means nothing is worse than none, because it
reads as isolation you do not have.On a subject-bound key this is filled in for you: omit it and the
checkout is stamped with the key’s subject; name a different one and the request is a 403.
On a tenant-wide key it is how a row gets tagged in the first place, and how you can later filter
the list with ?subject=.It also namespaces the Idempotency-Key — two end users may use the same key string without
colliding.object
default:"{}"
object
default:"{}"
Arbitrary JSON, stored and echoed back on every read. Never sent to the merchant, never
interpreted by us, not searchable. Use it to carry your own order reference.
Response
201 with the full checkout object, in status queued, already carrying the
intent_started progress item.
Idempotent creates
With anIdempotency-Key header, this endpoint has a second success code:
The body is hashed with its keys sorted and its defaults applied, so key order does not matter
and
"constraints": {} matches an omitted constraints. Keys live in a (tenant, subject)
namespace and expire 24 hours after the checkout that claimed them was created — released by an
hourly sweep, so the boundary is approximate to within an hour.
409
200.
A replay is checked before the quota, so it does not consume one of your active-checkout slots,
and does not launch a second browser. It is checked after URL, sandbox, subject and
buyer-profile validation, so a replay whose target has since become unreachable — or whose buyer
profile has since been deleted — can still return 400 rather than the checkout you expected.
This is idempotency of the call, not of the run. Once a checkout is
failed, its key returns
that failure until the key expires. Retrying for real means a new checkout and a new key.Errors
403
target.allowedDomains as well as target.url — the
offending host named in the message may be either.
Get a checkout
200.
string
required
UUID.
string
required
queued · running · awaiting_user_action · succeeded · failed · cancelled.string
Omitted when the checkout is not tagged to an end user. Your own id when it is — set
explicitly at creation, or stamped from the key’s binding. See
End-user isolation.
object
required
Exactly what you sent, plus
kind: "direct_url".object
required
{} if you sent none.string
Omitted when the checkout has no buyer profile.
object
required
{} if you sent none.object
Omitted until a browser session exists.
Its presence means “a browser exists and there is something to watch”.
embedTokenUrl
points at the endpoint that mints a viewable URL; it is not viewable itself.array
required
Every event so far, ordered by
sequence ascending. Each item is
{ kind, label, data, at, sequence }. See
the timeline.object
Omitted unless an action is pending right now.Always absent on a terminal checkout — cancelling or failing a run expires any question
still open, so
succeeded, failed and cancelled never advertise a pending action.detectedTotal is what the merchant’s own checkout says this order costs — shipping and tax
included — read off the live page at the moment the run stopped to ask. Present on card
actions. Show it to your user before you answer: after that the money has moved, and
receipt.total only exists once the checkout has succeeded, which is too late to be a
confirmation step. It is best-effort, so treat it as optional — omitted entirely when no total
could be parsed, and currency can be absent when the page shows a bare number. A confident
wrong price would be worse than no price, so nothing is guessed.See User actions.object
Omitted unless the run failed.
{ "reason": "automation_failed" } — one of
max_cost_exceeded, user_cancelled, user_action_expired, automation_failed. The
detail message is on the final terminal progress item, not here.object
string
required
ISO 8601.
string
required
ISO 8601.
Errors
404 Checkout not found — including when it belongs to another tenant, or to another end user
on a subject-bound key.
List checkouts
200.
Query
integer
default:"20"
An integer from 1 to 100. Anything else is a
400.string
The
nextCursor from the previous page — an ISO timestamp. Returns checkouts created
strictly before it. A value that is not a timestamp is a 400.string
Filter to one status. An unrecognised value returns
400 Unknown status 'x'.string
Filter to one end user. Composes with
status, limit and cursor; a
subject with no checkouts is an empty page, not an error.On a subject-bound key the page is already narrowed to its own end user, so
the parameter is redundant. Passing its own subject is fine; naming a different one is a 403 —
refused rather than quietly ignored.Response
Deliberately a summary, not the full object. The full shape carries the entire event timeline, which is exactly what a list view must not fetch.string
Omitted when the checkout is not tagged to an end user — the same convention as the full
object. The third row above is an untagged checkout.
string | null
Lifted from
target.url.string | null
Lifted from
target.request.object
Omitted unless failed.
object
Omitted unless a receipt exists. Note this is the short form —
{ total, merchantOrderId } with no evidence.string
Omitted unless an action is pending. The id only — fetch the checkout for the message and
schema.
string | null
Pass back as
?cursor=. null on the last page.A subject-bound key sees only its own end user’s checkouts here — other
people’s runs are absent from the page rather than redacted out of it. A tenant-wide key sees
everybody’s, tells them apart by the
subject on each row, and narrows to one with ?subject=.Cancel a checkout
202.
202 with an empty body.
Cancellation is asynchronous. It stops the run even while it is parked on a user action,
flips the row to cancelled, and reclaims the browser. Poll, or wait for the
checkout.status_changed webhook, to observe the terminal state.
A DELETE on an already-terminal checkout also returns 202 and does nothing. The 202
acknowledges the request, it does not assert an outcome.
404 Checkout not found if it does not exist or is not yours.
Mint a live-view token
200.
string
HS256 JWT pinned to this checkout id, valid 10 minutes.
string
Ready to drop into an
<iframe src>. Contains the token.string
ISO 8601. No refresh — mint a new token when it lapses.
404 Checkout not found if it is not yours. See
Live view.