Embedded: the store's checkout
With UCP's Embedded transport, the agent builds the checkout and the buyer finishes in the store's own checkout page, shown inside the agent's window. UCP Playground opens that page beside a run, or by hand in Inspect, and records every message between the page and the Playground.
How Embedded works
Embedded follows the Embedded Checkout Protocol (ECP), release 2026-08-25. It is found at two levels:
- The UCP profile lists a
dev.ucp.shoppingservice with"transport": "embedded"and a schema, but no endpoint. - Each checkout answer says whether this checkout can open embedded: an embedded service in
ucp.services, the embedded binding, withconfig.delegate, the actions the store lets the host handle. A checkout without the binding is redirect only.
"ucp": {
"version": "2026-08-25",
"services": {
"dev.ucp.shopping": [
{
"version": "2026-08-25",
"transport": "embedded",
"config": {
"delegate": [
"payment.credential",
"fulfillment.address_change",
"window.open",
"payment.instruments_change"
]
}
}
]
}
}The embedded binding in a checkout answer from our test store.
The host, here the Playground, loads the checkout's continue_url in a frame and adds ec_version and ec_delegate, the actions it wants to handle. The page answers in JSON-RPC messages over postMessage: ec.ready with the delegations it accepted, ec.start when the buyer can see the checkout, change notifications such as ec.payment.change, requests for a delegated action such as ec.payment.credential_request, and ec.complete with the order, or ec.error.
// The store's page
{ "jsonrpc": "2.0", "id": "ready_1", "method": "ec.ready",
"params": { "delegate": ["payment.credential", "fulfillment.address_change"] } }
// UCP Playground
{ "jsonrpc": "2.0", "id": "ready_1",
"result": { "ucp": { "version": "2026-08-25", "status": "success" } } } The handshake, as recorded on our test store: the page sends ec.ready and the Playground answers with the release.
Run one
On Runs, choose a store whose UCP profile lists Embedded. Runs says the store offers it and adds a Store's checkout row. Open the How the agent reaches the store pill and, under UCP · how checkout goes, pick Store's checkout · Embedded. The pill reads UCP · Embedded. It's never chosen for you: MCP stays the default. The choice is off, with the reason, when the store has no MCP or REST for the agent to build the checkout with.
- The agent builds the checkout over MCP (or REST): items, buyer, delivery. It never pays;
complete_checkoutis not sent. - The store's page opens when the checkout answer carries the embedded binding. It opens in the run's third pane, where a website run shows the store's page, with a browser bar that shows the host and the page's state. On a phone it opens as a full-height sheet from the chat. The chat says: "Your turn: finish in the store's checkout, on the right."
- You finish there, in the store's page. On our test stores, when the page asks for the payment credential, the Playground asks you first, in its own sheet over the page ("UCP Playground · not the store"): Send test card answers with the test token, and Cancel answers
abort_errorand leaves the page open. - The Playground confirms with the store. After
ec.completeit reads the checkout back withget_checkout. When the store answerscompletedwith an order, the run is Paid and the agent answers with what the store confirmed. The store's own confirmation page stays on screen.
A checkout without the binding is handed off at its continue_url, and the run says so.
What the Playground asks for: on our test stores, payment.credential and window.open; on any other store, window.open only, since the Playground holds no real card. The page is sandboxed (allow-scripts allow-forms allow-same-origin, credentialless), and every message's origin is checked against the continue_url. A store has to allow the Playground's origin in frame-ancestors for its page to show.
What you see
- Chat: the buyer's side only. One line hands over to the store's page, "Finished in the store's checkout." follows, and the paid card says it was finished in the store's checkout.
- Trace: the store's checkout as its own band, The store's checkout, embedded, with the host,
ec_versionand how many messages were answered. The load,ec.readyand its answer on one line,ec.start, each change, each delegated request with the Playground's answer and how long the buyer took,ec.completeorec.error, and the origin check after each. The confirm line reads likeget_checkout → completed · order … · matches ec.complete. - Discovery and negotiation: the embedded service and the delegation chain: what the store allows (
config.delegate), what the Playground asked (ec_delegate), and what the page accepted (ec.ready). - Info: how checkout went ("Embedded · the store's checkout · built over MCP") and a Store's checkout group: when the page loaded, the frame, what the Playground handled, the messages and the confirm.
Replays and shared runs show the recorded messages and the state the page reached, never a live store page.
Open it by hand in Inspect
In Inspect, a store that lists Embedded shows it on Start. Create a checkout (Checkout › create_checkout, then update_checkout); then, under Hand-off, choose open embedded checkout.
- The form: the checkout (answers that carry the binding), the
ec_delegateboxes from itsconfig.delegate(one the store doesn't allow can still be ticked, to test the page),ec_color_schemeandec_auth. - The request: the URL the Playground builds and the frame's flags, then Load, Reload and Close.
- Messages: a live log of every message, with Request, Response and Timing. Inspect never answers a delegated request for you (
ec.readyaside): each waits with a suggested answer to send, JSON you can edit, or Cancel · abort_error.
Every message is also added to the session's trace as it happens.
Try it on our test store
flowers-embedded.ucpsandbox.com is a Retail test store whose profile lists Embedded and whose checkout answers carry the binding. Its checkout page speaks ECP 2026-08-25 and allows the Playground to frame it. It takes test cards only and nothing ships.
With fulfillment.address_change ticked in Inspect, use Change address in the store's page: the address request arrives in the log and waits for your answer (Send address, edited JSON, or Cancel · abort_error).
What is recorded
Every message is kept with the run: its direction, method and id, the origin and whether it matched, the message itself, the Playground's answer and how long the buyer took, and the load (URL, delegations, frame flags). Messages from other origins are kept too, marked as ignored. See Transport failures for what a failed hand-off means.
Something wrong on this page?