This tutorial series takes you from zero to a working UCP implementation. You will create a discovery profile, expose your catalog, build carts, handle checkout, and manage orders. We will use REST transport for clarity, but the same concepts apply to MCP, A2A, and embedded.
Every UCP-enabled merchant needs a JSON profile at /.well-known/ucp. This is how agents discover what you support. Let us create one.
On your web server, create the directory for the well-known endpoint:
mkdir -p /var/www/your-store/.well-known
Create .well-known/ucp (no file extension) with your profile JSON:
{
"ucp": {
"version": "2026-04-08",
"services": {
"dev.ucp.shopping": [
{
"version": "2026-04-08",
"spec": "https://ucp.dev/2026-04-08/specification/overview",
"transport": "rest",
"endpoint": "https://your-store.com/api/ucp",
"schema": "https://ucp.dev/2026-04-08/services/shopping/rest.openapi.json"
}
]
},
"capabilities": {
"dev.ucp.shopping.checkout": [
{
"version": "2026-04-08",
"spec": "https://ucp.dev/2026-04-08/specification/checkout",
"schema": "https://ucp.dev/2026-04-08/schemas/shopping/checkout.json"
}
]
}
}
}
For nginx, ensure the file is served with the correct content type:
# nginx config
location = /.well-known/ucp {
default_type application/json;
add_header Access-Control-Allow-Origin *;
try_files $uri =404;
}
Agents will fetch your profile from different origins. You must set Access-Control-Allow-Origin or agents will be blocked by CORS.
curl -s https://your-store.com/.well-known/ucp | jq .ucp.version
# Expected: "2026-04-08"
curl -s https://your-store.com/.well-known/ucp | jq '.ucp.capabilities | keys'
# Expected: ["dev.ucp.shopping.checkout"]
Your store is now discoverable by UCP agents. Agents can fetch your profile and learn that you support shopping via REST with checkout capability. In the next steps, we will implement the actual endpoints.
Now agents know you exist, but they cannot see your products. Let us expose your catalog.
Your REST endpoint (declared as https://your-store.com/api/ucp in the profile) needs to handle catalog queries:
# Agent searches for products
GET https://your-store.com/api/ucp/catalog?q=wireless+headphones&limit=10
# Response:
{
"ucp": { "version": "2026-04-08" },
"results": [
{
"id": "item_123",
"title": "Premium Wireless Headphones",
"price": 19999,
"currency": "USD",
"description": "Noise-cancelling, 30h battery",
"seller": { "domain": "your-store.com" }
}
],
"total": 1,
"has_more": false
}
For per-product detail:
GET https://your-store.com/api/ucp/products/item_123
{
"ucp": { "version": "2026-04-08" },
"id": "item_123",
"title": "Premium Wireless Headphones",
"price": 19999,
"currency": "USD",
"variants": [
{ "id": "var_black", "title": "Black", "price": 19999 },
{ "id": "var_white", "title": "White", "price": 19999 }
]
}
Agents build carts across multiple conversation turns. Your API needs to accept line items and return estimated totals.
# Create a cart
POST https://your-store.com/api/ucp/carts
{
"line_items": [
{ "product_id": "item_123", "variant_id": "var_black", "quantity": 1 }
]
}
# Response:
{
"ucp": { "version": "2026-04-08" },
"id": "cart_abc123",
"line_items": [
{
"id": "li_1",
"item": { "id": "item_123", "title": "Premium Wireless Headphones" },
"variant_id": "var_black",
"quantity": 1,
"price": 19999
}
],
"totals": [
{ "type": "subtotal", "amount": 19999 },
{ "type": "estimated_tax", "amount": 1599 },
{ "type": "estimated_total", "amount": 21598 }
]
}
Update the cart as the buyer changes their mind:
PUT https://your-store.com/api/ucp/carts/cart_abc123
{
"line_items": [
{ "id": "li_1", "quantity": 2 }
]
}
When the buyer is ready, convert the cart to a checkout session and collect the remaining information.
# Create checkout from cart
POST https://your-store.com/api/ucp/checkout-sessions
{
"cart_id": "cart_abc123",
"line_items": [...]
}
# Response: status = "incomplete"
# Add buyer info and fulfillment
PUT https://your-store.com/api/ucp/checkout-sessions/chk_xyz
{
"buyer": {
"email": "customer@example.com",
"first_name": "Jane",
"last_name": "Doe"
},
"fulfillment": {
"methods": [{
"type": "shipping",
"destination": {
"street_address": "123 Main St",
"address_locality": "Austin",
"address_region": "TX",
"postal_code": "78701",
"address_country": "US"
}
}]
}
}
# Response: status = "ready_for_complete"
Complete the checkout by submitting payment:
POST https://your-store.com/api/ucp/checkout-sessions/chk_xyz/complete
{
"payment": {
"instrument": {
"type": "card",
"tokenized": "tok_xxx"
},
"credential": "ap2_mandate_xxx"
}
}
# Success:
{ "status": "completed", "order_id": "order_789" }
# Needs human input:
{
"status": "requires_escalation",
"continue_url": "https://your-store.com/checkout/chk_xyz/3ds"
}
All checkout operations require the agent to identify itself. Include the UCP-Agent header in REST requests:
UCP-Agent: profile="https://agent.example/profile"
After checkout, set up webhooks for order lifecycle events and an endpoint for on-demand order status:
# Agent asks "where's my order?"
GET https://your-store.com/api/ucp/orders/order_789
# Response:
{
"ucp": { "version": "2026-04-08" },
"id": "order_789",
"status": "fulfilled",
"fulfillment": {
"expectations": [{
"description": "Arrives in 2-3 business days",
"fulfillable_on": "now"
}],
"events": [{
"occurred_at": "2026-07-04T10:30:00Z",
"type": "shipped",
"tracking_number": "1Z999",
"tracking_url": "https://ups.com/track/1Z999"
}]
}
}
Some transactions need human input. UCP makes this graceful:
status: "requires_escalation" with a continue_urlcontinue_url should be a page on your site where the buyer completes the step{
"ucp": { "version": "2026-04-08" },
"id": "chk_xyz",
"status": "requires_escalation",
"continue_url": "https://your-store.com/checkout/chk_xyz/verify",
"messages": [{
"type": "info",
"code": "3ds_required",
"content": "Payment verification required. Please complete in your browser."
}]
}
You now have a working UCP implementation. Agents can discover your store, search your catalog, build carts, check out, and track orders. Next, check the platform guides for platform-specific optimizations, or the agent guides if you are building the agent side.