Quickstart: shop a sandbox store with your agent
Point any UCP agent at Northlight Flowers, search, build a checkout and pay with a test card. No account needed. About ten minutes.
1. Read the store's profile
Every store publishes its UCP profile at /.well-known/ucp: the spec version it speaks, its transports and the capabilities it declares.
curl https://flowers.ucpsandbox.com/.well-known/ucp
Northlight Flowers declares catalog search, catalog lookup, cart, checkout, order, discount, fulfillment, buyer consent, location search, location lookup, identity linking, on spec release 2026-08-25, over REST and MCP.
2. Say who your agent is
Every UCP operation needs your platform's profile URL. Over REST it goes in the UCP-Agent header; over MCP, in meta.ucp-agent.profile. Without it the store answers 400 invalid_profile_url. Send an idempotency key on writes so a retry never makes a second checkout: the Idempotency-Key header over REST, meta.idempotency-key over MCP.
UCP-Agent: profile="https://agent.example.com/.well-known/ucp" Idempotency-Key: 3f6c1c52-7d0e-4b43-9a55-2b1f0c9e8d11
You can also sign your requests with HTTP Message Signatures, either Web Bot Auth or UCP request signatures. The store checks the signature against the keys you publish and records whether it verified. Unsigned requests are still answered.
3. Search and build a checkout
Pick the way your agent talks to stores. All three reach the same catalogue, cart and checkout, so a browser and an agent never disagree.
POST https://flowers.ucpsandbox.com/mcp
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "search_catalog", "arguments": {
"meta": { "ucp-agent": { "profile": "https://agent.example.com/.well-known/ucp" } },
"catalog": { "query": "birthday bouquet" }
} } }
// then create_checkout with
// "meta": { "ucp-agent": { ... }, "idempotency-key": "3f6c1c52-7d0e-4b43-9a55-2b1f0c9e8d11" },
// "checkout": { "line_items": [ { "item": { "id": "bouquet_ranunculus" }, "quantity": 1 } ] }
curl -X POST https://flowers.ucpsandbox.com/ucp/catalog/search \
-H 'UCP-Agent: profile="https://agent.example.com/.well-known/ucp"' \
-H 'Content-Type: application/json' \
-d '{ "query": "birthday bouquet" }'
curl -X POST https://flowers.ucpsandbox.com/ucp/checkout-sessions \
-H 'UCP-Agent: profile="https://agent.example.com/.well-known/ucp"' \
-H 'Idempotency-Key: 3f6c1c52-7d0e-4b43-9a55-2b1f0c9e8d11' \
-H 'Content-Type: application/json' \
-d '{ "line_items": [ { "item": { "id": "bouquet_ranunculus" }, "quantity": 1 } ] }'
// Open any store page in a browser with WebMCP (Chrome origin trial):
// https://flowers.ucpsandbox.com/
// The page registers these tools with navigator.modelContext:
// search_catalog, lookup_catalog, create_checkout, get_checkout, update_checkout, complete_checkout, get_order, search_locations, lookup_locations
// For test harnesses it also exposes document.modelContext with
// getTools() and executeTool(tool, args).
// e.g. call search_catalog with { "query": "birthday bouquet" }
Start with initialize and tools/list. The server is stateless: no session to keep between calls.
The full contract is at /ucp/openapi.json. A checkout comes back with its status, totals, what is still missing, and a continue_url for the buyer.
WebMCP tools act on the same cart and checkout as UCP. WebMCP is a separate standard, not a UCP transport; we offer both so you can compare them.
4. Pay with a test instrument
Complete the checkout with one of the store's saved test instruments. Use the declining one to see how your agent handles a refusal.
| Instrument id | Card | Result |
|---|---|---|
| instr_1 | Visa •••• 1234 | Approves |
| instr_2 | Mastercard •••• 5678 | Approves |
| instr_fail | Visa •••• 0000 | Always declines |
Discount codes: 10OFF (10%), WELCOME20 (20%), FIXED500 ($5.00 off) . Delivery: std-ship, exp-ship-us, exp-ship-intl . Pickup: pickup from the store's shops, found with search_locations.
MCP tools on Northlight Flowers
A store lists only the tools for the capabilities it declares. Each tool takes meta with your profile, and returns the UCP object for that capability.
| Tool | Capability | Changes state |
|---|---|---|
| create_checkout | dev.ucp.shopping.checkout | Yes |
| get_checkout | dev.ucp.shopping.checkout | No |
| update_checkout | dev.ucp.shopping.checkout | Yes |
| complete_checkout | dev.ucp.shopping.checkout | Yes, pays |
| cancel_checkout | dev.ucp.shopping.checkout | Yes |
| create_cart | dev.ucp.shopping.cart | Yes |
| get_cart | dev.ucp.shopping.cart | No |
| update_cart | dev.ucp.shopping.cart | Yes |
| cancel_cart | dev.ucp.shopping.cart | Yes |
| search_catalog | dev.ucp.shopping.catalog.search | No |
| lookup_catalog | dev.ucp.shopping.catalog.lookup | No |
| get_product | dev.ucp.shopping.catalog.lookup | No |
| get_order | dev.ucp.shopping.order | No |
| search_locations | dev.ucp.common.location.search | No |
| lookup_locations | dev.ucp.common.location.lookup | No |
Checkout over A2A
flowers-a2a.ucpsandbox.com is Northlight Flowers with UCP's A2A transport added to its profile: the dev.ucp.shopping service lists "transport": "a2a" with the store's agent card as its endpoint. REST and MCP stay. The A2A binding covers checkout only, so search and look up products over REST or MCP, then check out over A2A.
curl https://flowers-a2a.ucpsandbox.com/.well-known/agent-card.json
# A2A 0.3
curl -X POST https://flowers-a2a.ucpsandbox.com/ucp/a2a \
-H 'UCP-Agent: profile="https://agent.example.com/.well-known/ucp"' \
-H 'X-A2A-Extensions: https://ucp.dev/2026-08-25/specification/reference' \
-H 'Content-Type: application/json' \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "message/send",
"params": { "message": { "role": "user", "kind": "message",
"messageId": "3f6c1c52-7d0e-4b43-9a55-2b1f0c9e8d11",
"parts": [ { "kind": "data", "data": { "action": "create_checkout",
"line_items": [ { "item": { "id": "bouquet_ranunculus" }, "quantity": 1 } ] } } ] } } }'
# A2A 1.0: -H 'A2A-Version: 1.0' -H 'A2A-Extensions: https://ucp.dev/2026-08-25/specification/reference',
# method SendMessage, role ROLE_USER, parts { "data": { ... } }
The agent card at /.well-known/agent-card.json serves A2A 0.3 and 1.0. Without an A2A-Version header the store speaks 0.3: method message/send, parts with kind. With A2A-Version: 1.0: method SendMessage, parts by member name, and the reply under result.message. Activate the UCP extension https://ucp.dev/2026-08-25/specification/reference on every request with X-A2A-Extensions (0.3) or A2A-Extensions (1.0); without it the store answers the A2A error for a required extension. Send UCP-Agent as over REST.
| Action | Data part |
|---|---|
| create_checkout | The fields of a UCP checkout create request, such as line_items, buyer and fulfillment. add_to_checkout does the same. |
| update_checkout | The fields of a UCP checkout update request: the full line items, as over REST. |
| get_checkout | Nothing else. |
| cancel_checkout | Nothing else. |
| complete_checkout | With a second data part: a2a.ucp.checkout.payment (the payment, with its instruments) and, optionally, a2a.ucp.checkout.signals. |
Each action is {"action": "…"} in a data part. Every reply is an agent message whose data part a2a.ucp.checkout holds the checkout exactly as REST returns it, errors and messages included; once completed, it carries order.id and order.permalink_url. The store creates the contextId on your first message; pass it back on the next. One checkout per context, and a new one can start once it is completed or canceled. A repeated messageId in the same context gets the earlier reply. A text message gets back the list of actions.
These action names are our interpretation. UCP's A2A binding shows add_to_checkout and complete_checkout only as examples and does not define a list of actions.
Embedded checkout
flowers-embedded.ucpsandbox.com is Northlight Flowers with UCP's Embedded transport (the Embedded Checkout Protocol, release 2026-08-25) added to its profile: the dev.ucp.shopping service lists "transport": "embedded" with its schema and no endpoint. REST and MCP stay. Every open checkout response carries the per-session binding in ucp.services with config.delegate, and a continue_url to the store's own checkout page. Load that page in an iframe with the ec_* parameters and it speaks ECP to your page with postMessage; without them it is the ordinary checkout page.
<!-- 1. Create a checkout on flowers-embedded.ucpsandbox.com (REST, MCP): the response carries
ucp.services["dev.ucp.shopping"] = [{ "transport": "embedded", "config": { "delegate": [...] } }]
and a continue_url. 2. Load it in your page: -->
<iframe sandbox="allow-scripts allow-forms allow-same-origin" credentialless
src="https://flowers-embedded.ucpsandbox.com/checkout/{id}?ec_version=2026-08-25&ec_delegate=payment.credential,window.open"></iframe>
// 3. Answer the handshake, then the delegated requests (origin = the continue_url's):
// ← { "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" } } }
// ← ec.start { checkout } … the buyer presses Pay …
// ← { "id": "payment_credential_request_2", "method": "ec.payment.credential_request", "params": { "checkout": { … } } }
// → { "id": "payment_credential_request_2", "result": { "ucp": { … "status": "success" },
// "checkout": { "payment": { "instruments": [ { "id": "instr_1", "handler_id": "mock_payment_handler",
// "type": "card", "selected": true, "credential": { "type": "token", "token": "success_token" } } ] } } } }
// ← ec.complete { checkout with order { id, permalink_url } }
| Delegation | What your page does |
|---|---|
| payment.credential | Pay sends ec.payment.credential_request with the checkout. Answer with checkout.payment.instruments, the selected one carrying a credential; the store completes the checkout with it as REST does: {"type": "token", "token": "success_token"} succeeds, fail_token declines. Answer abort_error if the buyer cancels; the page stays open. |
| fulfillment.address_change | The page shows the address with a Change button and sends ec.fulfillment.address_change_request. Answer with checkout.fulfillment.methods, the chosen destination selected. |
| payment.instruments_change | The page shows your payment method with a Change button and sends ec.payment.instruments_change_request. Answer with checkout.payment.instruments. You can also send them in the ec.ready result. |
| window.open | Links on the page (the returns and delivery policies) send ec.window.open_request with the URL instead of opening. |
Parameters: ec_version must be 2026-08-25 (anything else gets one ec.error with not_supported_error, unrecoverable, and the continue_url); ec_delegate is a comma list, and the page accepts the delegations you ask for that the binding allows, listing them in ec.ready; ec_color_scheme is light or dark and overrides the system setting; ec_auth is accepted and not needed. After the handshake the page sends ec.start with the checkout, then the ec.*.change notifications as the buyer edits it, each with the whole checkout, and ec.complete with the order. A MessagePort in the ec.ready result moves the session onto that port.
Only https://ucpplayground.com, http://127.0.0.1:8016, http://localhost:8016 may frame the page (Content-Security-Policy: frame-ancestors), and the page talks only to that origin. To test your error handling: add ec_test=origin_denied and the page answers with ec.error security_error; any other ec_version gets not_supported_error; and a checkout with White Orchid in it is redirect-only, so it carries no embedded binding and the page refuses to embed.
Store versions
A reference store gets a new version number whenever its catalogue or settings change. Copies and experiment stores follow their reference store, so quote the version when you publish a result. The console shows the version an experiment started on and warns if it changed part-way.
| Store | Version | Since | What changed |
|---|---|---|---|
| Northlight Flowers | v5 current | 26 Oct 2026 | Clearer return terms: 48 hours for fresh flowers, 30 days for vases and accessories, wedding pieces final sale |
| v4 | 23 Oct 2026 | Account linking (UCP identity linking); order reads stay open | |
| v3 | 17 Oct 2026 | Five shops, location search and lookup, pickup at checkout | |
| v2 | 8 Oct 2026 | Five shops, location search and lookup, pickup at checkout | |
| v1 | 2 Oct 2026 | Five shops, location search and lookup, pickup at checkout | |
| Harbour Row Stays | v2 current | 5 Oct 2026 | Catalogue or settings updated |
| v1 | 2 Oct 2026 | First recorded version | |
| Kettle Street Noodles | v3 current | 29 Oct 2026 | Catalogue or settings updated |
| v2 | 5 Oct 2026 | Catalogue or settings updated | |
| v1 | 2 Oct 2026 | First recorded version |