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.

Test stores only. Every payment uses a test instrument, no money moves and nothing ships. Never send a real card or real personal data.

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 } ] }

Start with initialize and tools/list. The server is stateless: no session to keep between calls.

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 idCardResult
instr_1Visa •••• 1234Approves
instr_2Mastercard •••• 5678Approves
instr_failVisa •••• 0000Always 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.

ToolCapabilityChanges state
create_checkoutdev.ucp.shopping.checkoutYes
get_checkoutdev.ucp.shopping.checkoutNo
update_checkoutdev.ucp.shopping.checkoutYes
complete_checkoutdev.ucp.shopping.checkoutYes, pays
cancel_checkoutdev.ucp.shopping.checkoutYes
create_cartdev.ucp.shopping.cartYes
get_cartdev.ucp.shopping.cartNo
update_cartdev.ucp.shopping.cartYes
cancel_cartdev.ucp.shopping.cartYes
search_catalogdev.ucp.shopping.catalog.searchNo
lookup_catalogdev.ucp.shopping.catalog.lookupNo
get_productdev.ucp.shopping.catalog.lookupNo
get_orderdev.ucp.shopping.orderNo
search_locationsdev.ucp.common.location.searchNo
lookup_locationsdev.ucp.common.location.lookupNo

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.

ActionData part
create_checkoutThe fields of a UCP checkout create request, such as line_items, buyer and fulfillment. add_to_checkout does the same.
update_checkoutThe fields of a UCP checkout update request: the full line items, as over REST.
get_checkoutNothing else.
cancel_checkoutNothing else.
complete_checkoutWith 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 } }
DelegationWhat your page does
payment.credentialPay 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_changeThe 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_changeThe 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.openLinks 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.

StoreVersionSinceWhat changed
Northlight Flowersv5 current26 Oct 2026Clearer return terms: 48 hours for fresh flowers, 30 days for vases and accessories, wedding pieces final sale
v423 Oct 2026Account linking (UCP identity linking); order reads stay open
v317 Oct 2026Five shops, location search and lookup, pickup at checkout
v28 Oct 2026Five shops, location search and lookup, pickup at checkout
v12 Oct 2026Five shops, location search and lookup, pickup at checkout
Harbour Row Staysv2 current5 Oct 2026Catalogue or settings updated
v12 Oct 2026First recorded version
Kettle Street Noodlesv3 current29 Oct 2026Catalogue or settings updated
v25 Oct 2026Catalogue or settings updated
v12 Oct 2026First recorded version

Next

Updated 30 Oct 2026 · spec release 2026-08-25Suggest an edit