Your cart (0 items)

Let an AI assistant check out at Rad Home

Rad Home supports the Universal Commerce Protocol (UCP), an open standard that lets AI shopping assistants put an order together for you. The assistant chooses the products, options and quantities, adds your shipping address and method, and gets the exact total with shipping and tax. You then review the order and pay on this website: the assistant never sees or handles your card.

How it works

  1. Ask an AI assistant that supports UCP to buy something from Rad Home.
  2. The assistant prepares the checkout: the products, their options and quantities, your shipping address and a shipping method. It can tell you the total, including shipping and tax.
  3. It gives you a link to finish on this website. Opening the link puts the items in your cart and fills in the checkout form with the details the assistant has.
  4. If the email the assistant gave belongs to an account here, you're asked to sign in with its password first, so the order goes to your account. You can check out as a guest with a different email instead. If the email has no account yet, placing the order creates one and emails you a temporary password.
  5. Check everything, enter your payment details on the checkout page and place the order. You get the usual order confirmation by email.

Good to know

  • Opening the link replaces what is in your cart with the assistant's items. If you're signed in, that's the cart saved on your account.
  • The link works for 6 hours after the assistant last changed the order. If it has expired, ask the assistant for a new one.
  • Prices, stock and shipping are checked again when you open the link. If an item can no longer be bought, the cart page tells you which one.
  • Treat the link like a key: until the order is placed, the checkout is canceled or the link expires, anyone who has it can open the order, with its items and the name, email, phone number and address the assistant entered. Don't share it.
  • While the assistant's items are in your cart, we record the order as placed through an AI assistant, and from which assistant platform. If you remove them and shop on your own, it's an ordinary order.
  • Want an assistant that only browses the store, with no checkout? See AI connector (MCP).

For AI agents and developers

The store implements UCP version 2026-08-25 over REST: the checkout capability with the fulfillment extension (shipping). It lists no payment handlers. Buyers pay on this website through the checkout's continue_url, so the API never takes payment details. The API never says whether the buyer's email has an account here: when it does, the buyer signs in after opening continue_url.

Business profile

  • Service: dev.ucp.shopping, REST, endpoint https://www.radho.me/api/ucp.
  • Capabilities: dev.ucp.shopping.checkout and dev.ucp.shopping.fulfillment, both version 2026-08-25.
  • Payment handlers: none. Payment happens on this website.
  • Currency: US dollars. Every amount is an integer number of cents.

Headers

  • UCP-Agent: profile="https://your-platform.example/.well-known/ucp" is required on every call. The store reads that profile and caches it for 5 minutes. It must be served over HTTPS from a public address, answer 200 without a redirect within 10 seconds, be at most 256 KiB, use version 2026-08-25 and list dev.ucp.shopping.checkout. List dev.ucp.shopping.fulfillment too to send shipping addresses through the API. Without it, the buyer enters the address on this website.
  • Idempotency-Key is required on every POST and PUT. Use a new UUID for each new request. After a successful call, sending its key again with exactly the same request (the same method, path and body, byte for byte, from the same profile) returns the stored answer, kept for 24 hours; sending that key with any other request returns 409. To retry, resend the body unchanged. Answers with an HTTP error status are not stored, so retrying such a call with its key runs it again.
  • Content-Type: application/json for request bodies. A Request-Id header is echoed back.

Operations

Paths are relative to the endpoint, https://www.radho.me/api/ucp.

POST /checkout-sessions
Creates a checkout from line_items, plus optional buyer, context and fulfillment. Answers 201.
GET /checkout-sessions/{id}
The checkout, priced again with current prices, stock and shipping.
PUT /checkout-sessions/{id}
Replaces the whole checkout: send every line, the buyer, the context and the fulfillment again. Send back each line's id to keep it.
POST /checkout-sessions/{id}/complete
Places no order, because payment happens on the website. Returns the checkout as it stands.
POST /checkout-sessions/{id}/cancel
Cancels an open checkout. A completed one can't be canceled here.

A checkout belongs to the platform profile that created it. Calls that name another profile get not_found.

Item IDs

  • An item's id is the product's slug (the last part of its page address, such as blue-ceramic-vase) or its whole page address on this website. The store's MCP server returns both for every product (slug and url), and so does the sitemap.
  • For a product with options, add ?options= followed by one option ID from each option group, separated by commas, for example product-slug?options=12,15. Without it, the line gets the choice the product's page starts with, and an info message lists the product's options with their IDs.
  • In answers, item.id is in that form (the slug, plus the options chosen) once the store has matched the line to a product and its options; a line it could not match keeps the id you sent. item.price is the unit price in cents on a line that can be bought, and 0 on a line with an error.

The flow

  1. Create the checkout with the items. Its status is incomplete while something is missing, and each error message's path points at what to fix.
  2. Update it with the buyer's shipping address. The answer lists the shipping options for that address with their prices, under fulfillment.methods[0].groups[0].options.
  3. Update it again with selected_option_id. When nothing is left to fix, the status is requires_escalation, with a payment_required message (severity requires_buyer_input) and the final totals.
  4. Give the buyer the continue_url (it starts with https://www.radho.me/ucp/checkout/). They review and pay on this website.
  5. Once they have placed the order, GET returns status completed with an order object (id, label and permalink_url) and the order's actual lines and totals.

Create a checkout:

curl -X POST https://www.radho.me/api/ucp/checkout-sessions \
  -H "Content-Type: application/json" \
  -H 'UCP-Agent: profile="https://your-platform.example/.well-known/ucp"' \
  -H "Idempotency-Key: 6f1e2d3c-4b5a-4968-8776-5a4b3c2d1e0f" \
  -d '{"line_items": [{"item": {"id": "product-slug"}, "quantity": 1}],
       "buyer": {"first_name": "Ada", "last_name": "Lovelace", "email": "ada@example.com"}}'

Then add the shipping address and choose a shipping option (PUT /checkout-sessions/{id} with a new Idempotency-Key). selected_option_id must be one of the IDs the store listed under fulfillment.methods[0].groups[0].options for that address, so send the address first if you don't have them yet. ship_1 below is only an example: the number is the store's ID for the shipping method, not its position.

{
  "line_items": [{"id": "li_1", "item": {"id": "product-slug"}, "quantity": 1}],
  "buyer": {"first_name": "Ada", "last_name": "Lovelace", "email": "ada@example.com"},
  "fulfillment": {"methods": [{
    "type": "shipping",
    "destinations": [{"id": "home", "street_address": "1 Main St", "address_locality": "Albany",
                      "address_region": "NY", "postal_code": "12207", "address_country": "US"}],
    "selected_destination_id": "home",
    "groups": [{"id": "group_1", "selected_option_id": "ship_1"}]
  }]}
}

The answer, shortened:

{
  "id": "chk_…",
  "status": "requires_escalation",
  "currency": "USD",
  "line_items": [{"id": "li_1", "item": {"id": "product-slug", "title": "…", "price": 2999}, "quantity": 1, "totals": […]}],
  "totals": [{"type": "subtotal", "amount": 2999}, {"type": "fulfillment", "amount": 1995},
             {"type": "tax", "amount": 300}, {"type": "total", "amount": 5294}],
  "messages": [{"type": "error", "code": "payment_required", "severity": "requires_buyer_input", "content": "…"}],
  "continue_url": "https://www.radho.me/ucp/checkout/chk_…",
  "expires_at": "…",
  "fulfillment": {"methods": [{"id": "shipping_1", "type": "shipping", "line_item_ids": ["li_1"],
    "selected_destination_id": "home", "destinations": […],
    "groups": [{"id": "group_1", "selected_option_id": "ship_1",
                "options": [{"id": "ship_1", "title": "…", "totals": [{"type": "total", "amount": 1995}]}]}]}]}
}

Addresses, shipping and tax

  • A shipping address needs street_address, address_locality (the city), postal_code and address_country: an ISO 3166-1 code such as US (three-letter codes and English country names work too). In the United States and Canada it also needs address_region: the state or province, as a two-letter code such as NY or ON, or its name.
  • Shipping options and tax are the ones the checkout page uses for the same address. The address must be in a country the store sells to.
  • For an order with nothing to ship, send context.address_country (and address_region) to include tax in the totals.
  • Until the store knows a country it sells to (from the shipping address, or from context.address_country for an order with nothing to ship), the totals leave out shipping and tax, and a warning with code estimate says so. While the address has shipping options and none is chosen (or the chosen one isn't offered), the totals leave out shipping, and the error on selected_option_id says why. A US or Canadian address without a usable address_region gets only the country's tax, with an error on address_region.

Statuses

  • incomplete: the agent can fix something through the API. See the error messages.
  • requires_escalation: the buyer finishes on the website through continue_url.
  • completed: the buyer placed the order. The checkout includes order.
  • canceled: canceled through the API.

Messages

Problems with the order come back as messages on a successful answer: HTTP 201 for a create, 200 for every other call. Each path is a JSONPath to the field concerned.

missing
A required address field, the shipping address or the shipping option is missing.
not_found
The item ID can't be read, or names no active product of this store. A slug with a malformed ?options= list gets not_found too: anything but distinct option ID numbers, more than 20 IDs, or options= given twice. On a call for a checkout, it means there is no such checkout for your platform profile (or it has expired): the answer is HTTP 200 with no checkout in it, ucp.status error and this message. Create a new checkout.
invalid
Well-formed option IDs don't name exactly one option from each of the product's groups, an address field is too long, the country or the state or province isn't one the store recognizes, selected_destination_id names no destination, or the shipping option isn't offered for the address.
item_unavailable
The product can't be bought through the API: it is priced on request, a subscription, needs details entered on its page, sold on another website, has no price, or isn't available (the store doesn't stock it, or that combination of its options). The message says what the buyer can do instead: buy a subscription or a product that needs details on the product's page, ask the store about one priced on request or without a price, or buy one sold elsewhere on that website.
out_of_stock
Not enough stock for the quantity. The message says how many are left.
invalid_quantity
The quantity must be from 1 to 100,000.
duplicate_item
The same product with the same options is in two lines. Change the first line's quantity instead.
address_undeliverable
The store doesn't sell to that country.
payment_required
Nothing is left to fix through the API: send the buyer to continue_url.
checkout_completed, checkout_canceled
The checkout is closed and can't change.
backorder (warning)
The item is on pre-order and ships when it is back in stock.
estimate (warning)
The totals leave out shipping and tax until the store knows a country it sells to (see Addresses, shipping and tax).
default_options (info)
A product with options got its page's starting choice. Lists the options and their IDs.

Errors

Problems with the call itself come back with an HTTP 4xx or 5xx status and a body of {"code": ..., "content": ...}, except 429 (below). Two come back instead as an HTTP 200 error response, with ucp.status error, the problem in messages and no checkout: capabilities_incompatible, at the end of this list, and not_found for a checkout that doesn't exist (see Messages).

400 invalid_profile_url
No UCP-Agent profile, or it isn't an absolute https URL of at most 500 characters.
400 idempotency_key_required, invalid_idempotency_key
A POST or PUT without an Idempotency-Key, or with one that isn't 16 to 255 printable ASCII characters.
400 invalid_request
The body isn't valid JSON for a checkout, has no line_items or more than 50, has a line without an item.id, with an item.id over 700 characters or with a quantity below 1, or has a fulfillment ID over 255 characters.
409 idempotency_conflict, request_in_progress
The key was used for a different request, or a request with the same key is still running.
413 request_too_large
The body is over 64 KiB.
422 profile_malformed, version_unsupported
The platform profile isn't a UCP profile, or isn't version 2026-08-25.
424 profile_unreachable
The store couldn't read the platform profile.
429
Too many requests. Wait for the number of seconds in Retry-After, which is always sent. This answer has no code: its body is {"status": 429, "title": "Too many requests", "retryAfter": seconds} (application/problem+json).
500 internal_error
The store couldn't process the request. Retry later with the same key.
503 idempotency_unavailable
The store can't check the key right now. Retry later with the same key.
200 capabilities_incompatible
The platform profile doesn't list dev.ucp.shopping.checkout version 2026-08-25. Sent as an error response with ucp.status error.

Limits

  • Up to 50 lines per checkout, quantities from 1 to 100,000, and request bodies up to 64 KiB.
  • A checkout expires 6 hours after it was last created or updated. A completed one can still be read for 30 days.
  • Every request counts toward the per-client limit this whole website has: bursts of up to 150 requests, then 5 a second.
  • Creates, updates, completes and cancels are limited per client to 40 at once, then 20 a minute.
  • Reading a platform profile the store hasn't cached is limited per client to 10 at once, then 10 every 10 minutes. Keep using the same profile URL.

Not supported

  • Payment through the API: payment handlers, payment tokens and AP2 mandates. The buyer pays on this website.
  • Coupon codes and discounts through the API. The buyer can still use them on this website.
  • Order tracking (GET /orders and order webhooks), and the cart, catalog and identity-linking capabilities.
  • Checkout over MCP, A2A or the embedded protocol, and request signatures. Checkout calls are not authenticated, so keep each checkout's id and continue_url private.