UCP, the open Universal Commerce Protocol, has two ways to hand a checkout from an agent back to the store. The plain one is a link: the buyer opens the checkout's continue_url and finishes on the store's site. The other is the Embedded transport: the agent's app opens the store's own checkout page inside itself, and the two exchange messages while the buyer finishes.
We built both sides: a test florist in UCP Sandbox whose checkout can be embedded, and hosting in UCP Playground. This walkthrough follows one real order through it, step by step, from the agent's checkout to six messages and a placed order. It is the companion to our walkthrough of UCP over A2A.
What is UCP's Embedded transport?
UCP's Embedded transport lets an app (the spec calls it the host) load a store's checkout page in an iframe or webview and exchange JSON-RPC messages with it. For checkout, the spec calls this the Embedded Checkout Protocol (ECP): the store's page tells the host when it is ready, when the checkout changes and when the order is placed, and the host can take over chosen steps, such as supplying the payment credential.
The store keeps its own page, rules and order. Embedded doesn't replace REST or MCP: the agent still builds the checkout over one of those, and Embedded is how the buyer finishes it.
The setup
- The store: Northlight Flowers on flowers-embedded.ucpsandbox.com, a test florist in UCP Sandbox whose profile offers REST, MCP and Embedded. Nothing is charged and nothing ships.
- The host and the agent: UCP Playground, running Claude Sonnet 5, with checkout set to "Store's checkout · Embedded".
- The task: buy one Bouquet of Red Roses, with free delivery to Sam Lee, 123 Market St, San Francisco.
The run is public, recorded on 30 October 2026: four agent calls, six messages from the store's page, two answers from Playground.

Step 1: the profile says the store supports Embedded
Every UCP store publishes a profile at /.well-known/ucp. A store that supports Embedded adds a shopping service with a transport of embedded. Unlike REST and MCP it has no endpoint, because there is nothing to call:
"dev.ucp.shopping": [
{ "version": "2026-08-25", "transport": "rest", "endpoint": "https://flowers-embedded.ucpsandbox.com/ucp" },
{ "version": "2026-08-25", "transport": "mcp", "endpoint": "https://flowers-embedded.ucpsandbox.com/mcp" },
{ "version": "2026-08-25", "transport": "embedded",
"schema": "https://ucp.dev/2026-08-25/services/shopping/embedded.openrpc.json" }
]
That is the first of two levels: the profile says the store supports it, and each checkout says whether it applies this time.
Step 2: the agent builds the checkout over MCP
The agent works as it would without Embedded: search_catalog, then create_checkout and update_checkout on the store's MCP service for the buyer, the address and free delivery. The checkout came back ready_for_complete.
The difference is in each answer. Beside the continue_url, the checkout carries an embedded binding in ucp.services, whose config.delegate lists the steps the store will let the host take over:
{
"id": "01a18e12-78dd-7263-acba-36eb8b3d8ebe",
"status": "ready_for_complete",
"continue_url": "https://flowers-embedded.ucpsandbox.com/checkout/01a18e12-78dd-7263-acba-36eb8b3d8ebe",
"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"] }
}]
}
}
}
A business MUST include this binding to make a checkout embeddable. Without it, even when the profile lists embedded, the checkout supports redirect only; our test store does this for checkouts containing a White Orchid. Playground's agent never pays: it stops here and hands over.
Step 3: load the store's page with ec_ parameters
The host opens the continue_url in an iframe with parameters prefixed ec_. Only ec_version is required, and it must match the checkout's version; ec_delegate, ec_color_scheme and ec_auth are optional. Playground loaded:
https://flowers-embedded.ucpsandbox.com/checkout/01a18e12-78dd-7263-acba-36eb8b3d8ebe
?ec_version=2026-08-25
&ec_delegate=payment.credential,window.open
&ec_color_scheme=light
Playground asked for two of the four delegations: the payment credential, and opening links (the store's policies) in a new tab. Address and card choice stayed on the store's page, which opens in the run's third pane, beside the chat and the trace.

Step 4: the handshake
The store's page speaks first with ec.ready, a request the host must answer, listing the delegations it accepted:
{ "jsonrpc": "2.0", "id": "ready_1", "method": "ec.ready",
"params": { "delegate": ["payment.credential", "window.open"] } }
{ "jsonrpc": "2.0", "id": "ready_1",
"result": { "ucp": { "version": "2026-08-25", "status": "success" } } }
Delegation narrows at every stage. The spec writes it as a chain:
config.delegate ⊇ ec_delegate ⊇ ec.ready delegate
The store allows, the host asks, the page confirms. An accepted delegation is binding: the page MUST send the request at that step, wait for the answer, and show no UI of its own for it. Playground's Discovery and negotiation tab shows the chain per delegation.

The answer may also carry a MessagePort (a private channel) or an auth credential; Playground sends neither and stays on postMessage.
Step 5: the page reports what the buyer does
Next comes ec.start, a notification with the full checkout. Every edit then produces an ec.*.change notification carrying the whole checkout, not just the changed fields. In this run the agent had already set the address and free delivery, so the buyer typed an email and the recipient's name into the store's page, which sent ec.buyer.change with the checkout's new buyer (email, first and last name), then picked a saved test card, which sent ec.payment.change. Notifications get no answer, and the host never has to poll.

Step 6: the store asks the host for the payment credential
The buyer pressed Pay. With payment.credential delegated, the page asked the host and waited:
{ "jsonrpc": "2.0", "id": "payment_credential_request_2",
"method": "ec.payment.credential_request",
"params": { "checkout": { "id": "01a18e12-…", "status": "ready_for_complete",
"payment": { "instruments": [ { "id": "instr_1", "handler_id": "mock_payment_handler", "type": "card",
"display": { "brand": "Visa", "last_digits": "1234" }, "selected": true } ] } } } }
The host MUST NOT silently release a token based solely on the message. Playground showed its own sheet over the store's page, naming the store and the amount:

The buyer chose "Send test card" about seven seconds later, and only then did Playground answer, with the selected instrument and its credential (the shared run redacts the token):
{ "jsonrpc": "2.0", "id": "payment_credential_request_2",
"result": {
"ucp": { "version": "2026-08-25", "status": "success" },
"checkout": { "payment": { "instruments": [ {
"id": "instr_1", "handler_id": "mock_payment_handler", "type": "card",
"display": { "brand": "Visa", "last_digits": "1234" }, "selected": true,
"credential": { "type": "token", "token": "success_token" } } ] } } } }
Cancel answers abort_error, which is recoverable: the page stays open. Playground sends test cards to test stores only; on a real store it asks to handle links only, so payment stays on the store's page.
Step 7: the order, and a second check
The store completed the checkout on its own server and sent ec.complete with the order:
{ "jsonrpc": "2.0", "method": "ec.complete",
"params": { "checkout": { "id": "01a18e12-…", "status": "completed",
"order": { "id": "01a18e13-3b85-73de-929f-261b944ba225", "label": "NF-TAIK5P",
"permalink_url": "https://flowers-embedded.ucpsandbox.com/orders/01a18e13-3b85-73de-929f-261b944ba225" } } } }
The store's page showed its own confirmation, "Order placed", inside the frame. Playground doesn't take the page's word for it: it called get_checkout over MCP, got completed with the same order, and only then let the agent confirm to the buyer. The trace shows the exchange as one band, each message with its origin checked.

The security rules both sides follow
Putting one company's page inside another's app needs rules on both sides:
- The store MUST send a
Content-Security-Policy: frame-ancestorsheader naming the hosts allowed to embed it. Our test store lists Playground (and local development addresses) and nothing else, so no other site can frame it. - The host sandboxes the iframe (
allow-scripts allow-forms allow-same-origin) and SHOULD load itcredentialless, a fresh context with none of the buyer's existing cookies or sessions, so the store can't link the visit to other activity. - Both check the origin of every message. Playground accepts messages only from the
continue_url's origin.
The run's Info tab records the URL, the frame and the result.

What the specification leaves open
Building both sides turned up three places where implementers must choose. None stopped the order, but two implementations could choose differently:
- What
config.delegatemeans. The spec describes it as "the intersection of what the host requested viaec_delegateand what the business allows", but the host only sendsec_delegatelater, when it loads the page. The chain in the same section reads it as what the business allows. We built to the chain. - How the page learns the host's origin. The host checks messages against the
continue_url's origin, but the spec doesn't say how the page finds the host's origin to address and check its own. Our test store reads it from the browser and talks only to origins in itsframe-ancestorslist. - Redirects inside the frame. Payment redirects and 3-D Secure are allowed, but the recommended sandbox has no popups or top-level navigation. Host and store "MAY negotiate additional capabilities", with no field to do it.
What it means for stores
- Both levels are needed:
embeddedin the profile, and the binding in each checkout. Without the binding, hosts fall back to the link. - Allow only delegations you will honour. Accepted in
ec.ready, they bind your page to wait for the host. - Set
frame-ancestors. It is a MUST, and it decides which apps can show your checkout.
About 11,000 verified stores in our directory already list embedded in their profiles, almost all on Shopify (transports, 30 October). We haven't tested whether their checkouts load in Playground's frame; frame-ancestors decides that, store by store.
What it means for agent builders
- Build the checkout over REST or MCP, then look for the binding before handing over.
- Ask only for delegations you can handle, and answer every request: a cancel is
abort_error, not silence. - Never release a credential without the buyer's confirmation, and confirm the order with
get_checkout.
Try it
Start a run at flowers-embedded.ucpsandbox.com and pick "Store's checkout · Embedded" in the UCP pill, or open it by hand from Inspect's Hand-off tool. To host the page yourself, the Sandbox quickstart has the iframe, every delegation and switches for testing errors.
What we'll watch
- Whether the three open points above get settled in the spec, and the open pull request that marks the embedded auth fields required (#802).
- Which hosts start embedding store checkouts, and which stores allow them in
frame-ancestors. - How many stores that declare
embeddedalso send the binding in their checkouts.
FAQ
What is UCP's Embedded transport?
It lets an app show a store's own checkout page in an iframe or webview and exchange JSON-RPC messages with it, so the buyer finishes in the store's page. For checkout it is called the Embedded Checkout Protocol (ECP). It doesn't replace REST or MCP, which still build the checkout.
How does an app know a checkout can be embedded?
At two levels: the store's UCP profile lists a shopping service with "transport": "embedded", and each checkout response carries an embedded binding in ucp.services with config.delegate. Without the binding, that checkout supports redirect only.
What is delegation in embedded checkout?
The host taking over a step the store's page would otherwise handle, such as the payment credential, the address or links. The store allows a set, the host asks with ec_delegate, and the page confirms in ec.ready.
Can the host pay without asking the buyer?
No. The spec says the host must not release a payment token based only on the store's request; it must show its own confirmation first.
Can I test it without a real store?
Yes. Northlight Flowers on flowers-embedded.ucpsandbox.com is a test store that supports Embedded, and nothing is charged.
About UCP Checker
UCP Checker is the independent validation and observability layer for the Universal Commerce Protocol. We crawl, validate and grade every public UCP manifest we can find, run the merchant directory, the UCP Score, live adoption stats, the vertical guides and the authority-bound vendor map, and track the spec as it evolves so you don't have to — measured from two vantages (what a business declares, and what actually happens when an agent transacts), the same way for everyone, without picking winners.
Sources
- Embedded Protocol (EP). UCP specification, release 2026-08-25.
- Checkout Capability - EP Binding (the Embedded Checkout Protocol). UCP specification, release 2026-08-25.
- Embedded shopping service (OpenRPC). UCP, release 2026-08-25.
- Payment Request API. W3C Recommendation.
- Run in the store's embedded checkout. UCP Playground, 30 October 2026.
Related coverage: How UCP over A2A works, step by step · How UCP works: a real agent buys flowers · UCP transports · What is /.well-known/ucp?
