Building AI Shopping Agents That Actually Buy: A UCP Developer Guide
A technical guide to building AI shopping agents with real purchase capability using UCP. Covers discovery, cart management, checkout, payment token exchange, authentication, and error handling.
From Assistant to Buyer: What UCP Unlocks for Agent Developers
Most AI shopping assistants today are elaborate search engines. They can find products, compare options, summarize reviews, and then hand the user off to a browser tab to actually complete the purchase. That handoff is where conversion dies.
UCP eliminates the handoff. The Universal Commerce Protocol gives an AI agent a structured, authenticated pathway to discover merchants, query catalogs, manage carts, and complete checkout without ever leaving the agent context. This guide walks through the full technical architecture for building an agent that can actually buy things.
If you are looking for a quick overview of which platforms and SDKs currently support UCP, the UCPList directory is a good starting point. This post goes deeper: how to wire it all together.
The Architecture of a UCP-Enabled Agent
A UCP shopping agent has four distinct layers:
Discovery layer finds merchants and retrieves their capability manifests. Catalog layer queries product data, availability, and pricing. Cart layer manages session state across multiple merchants. Checkout layer handles identity, payment token exchange, and order confirmation.
Each layer has a defined API surface in the UCP spec. The layers are independent: you can have a robust discovery and catalog integration without implementing checkout, which is a reasonable starting point for agents that need approval workflows before purchasing.
Discovery: Finding Merchants via .well-known/ucp
Every UCP-enabled merchant publishes a manifest at /.well-known/ucp. This file describes what the merchant supports: which UCP spec version they implement, which product categories they carry, checkout capabilities, and supported payment methods.
import { z } from 'zod';
const response = await fetch('https://shop.example.com/.well-known/ucp');
if (!response.ok) throw new Error(`Discovery failed: ${response.status}`);
const profile = z.record(z.string(), z.unknown()).parse(await response.json());
console.log(profile.ucp); // Inspect advertised versions, services, and capabilities.
// Discover curated candidates using the UCPList directory API.
const merchants = await fetch(
'https://ucplist.ai/api/v2/listings?category=merchants&ucpStatus=live'
).then(result => result.json());The official @ucp-js/sdk npm package and ucp-sdk PyPI package provide generated schemas/models for payload validation. Neither package is a universal network client. Bind your transport to the services advertised by each merchant profile.
Querying the Catalog
Once you have a merchant's manifest, catalog queries follow a consistent GraphQL-style schema regardless of the underlying platform. This is one of UCP's core value propositions: a Shopify merchant and a custom commerce backend look identical to your agent at the catalog layer.
const catalog = await client.catalog(manifest);
const results = await catalog.search({
query: 'noise canceling headphones',
filters: {
priceMax: 35000, // cents
inStock: true,
},
limit: 10,
});
for (const product of results.items) {
console.log(product.id, product.title, product.price.amount);
console.log(product.variants); // sizes, colors, etc.
console.log(product.availability.quantity);
}Price values in UCP are always integers in the smallest currency unit (cents for USD) to avoid floating-point ambiguity. Your agent should store and compare prices this way throughout the session.
Cart Management
UCP carts are server-side sessions scoped to a merchant. Creating a cart returns a cartId that you will use through to checkout. Carts have a TTL (typically 30 minutes, but specified in the manifest) and must be refreshed if the user's session extends.
const cartSession = await client.cart.create(manifest, {
agentId: 'your-agent-id',
sessionId: 'user-session-abc123',
});
// Add items
await cartSession.addItem({
productId: product.id,
variantId: product.variants[0].id,
quantity: 1,
});
// Check current state (prices may have changed)
const cart = await cartSession.get();
console.log(cart.items, cart.subtotal, cart.estimatedTax);
// Handle the case where something changed since you queried the catalog
if (cart.warnings.length > 0) {
for (const warning of cart.warnings) {
// warning.type: "PRICE_CHANGED" | "OUT_OF_STOCK" | "QUANTITY_REDUCED"
console.log(warning.type, warning.productId, warning.details);
}
}Always re-check the cart state before presenting a purchase confirmation to the user. Prices and availability change between catalog query and checkout.
Checkout and Payment Token Exchange
The checkout flow separates identity verification from payment. Your agent first initiates checkout to get a checkout session, then exchanges a consumer-authorized payment token to complete the purchase.
// Initiate checkout
const checkoutSession = await client.checkout.initiate(cartSession, {
shippingAddress: {
line1: '123 Main St',
city: 'San Francisco',
state: 'CA',
postalCode: '94105',
country: 'US',
},
shippingMethod: 'standard',
});
// Exchange a payment token
// The consumer pre-authorizes a payment token with your agent platform
// This token is scoped: it can only be used for this checkout session
const order = await client.checkout.complete(checkoutSession, {
paymentToken: consumerPaymentToken,
confirmationRequired: false, // set true for high-value purchases
});
console.log(order.orderId, order.status, order.estimatedDelivery);The payment token model is critical to understand. Consumers do not give agents their raw payment credentials. Instead, they pre-authorize a scoped token through their wallet provider (Stripe, Apple Pay, etc.) that specifies: maximum spend amount, permitted merchant categories, and an expiry window. Your agent receives this token and can only use it within those constraints.
Agent Authentication and Scoped Tokens
Agents authenticate to the UCP network with two credentials: an agent identity token (who you are) and a consumer authorization token (permission to act on behalf of a specific user). Both are JWTs with standard UCP claims.
const ucpClient = new UCPClient({
agentId: process.env.UCP_AGENT_ID,
agentSecret: process.env.UCP_AGENT_SECRET,
});
// Attach consumer authorization for a specific session
const sessionClient = ucpClient.withConsumerAuth(consumerAuthToken);
// All subsequent calls carry both credentials
const manifest = await sessionClient.discover('https://shop.example.com');Scoped tokens are the right pattern for production agents. An agent with broad payment authority is a liability. Issue tokens scoped to a purchase category and a spend limit, and expire them aggressively.
MCP Integration
If a merchant advertises an MCP transport in its UCP business profile, configure your MCP client with that advertised endpoint. Tool names and authentication come from the merchant service; UCP does not publish a universal MCP server package.
{
"mcpServers": {
"merchant": {
"url": "https://merchant.example/api/ucp/mcp"
}
}
}Exact remote-MCP configuration varies by client. Discover the endpoint first, review the tools it exposes, and apply the authentication requirements declared by that merchant. You can see current integrations in the agent integrations category on UCPList.
Error Handling Patterns
Three failure modes deserve explicit handling in any production agent:
Out of stock after add: The cart API will return a QUANTITY_REDUCED or OUT_OF_STOCK warning on cart.get(). Surface this to the user before confirming purchase, never silently drop the item.
Price change: If a product's price increases between catalog query and checkout, the cart will reflect the new price. Always show the user the final cart total, not the originally displayed price.
Payment failure: The checkout.complete() call will throw a UCPPaymentError with a code property. Codes include INSUFFICIENT_FUNDS, TOKEN_EXPIRED, MERCHANT_DECLINED, and NETWORK_ERROR. Each requires a different recovery path. TOKEN_EXPIRED means you need a new consumer authorization; the others may be retriable or require user intervention.
try {
const order = await client.checkout.complete(checkoutSession, { paymentToken });
} catch (err) {
if (err instanceof UCPPaymentError) {
switch (err.code) {
case 'TOKEN_EXPIRED':
return requestNewConsumerAuth(userId);
case 'MERCHANT_DECLINED':
return notifyUser('The merchant declined this order. Please contact support.');
case 'NETWORK_ERROR':
return retryWithBackoff(() => client.checkout.complete(checkoutSession, { paymentToken }));
}
}
throw err;
}Testing Your Integration
The official UCP conformance repository provides language-agnostic integration tests for a running merchant server. Clone it and configure its fixtures before going to production:
git clone https://github.com/Universal-Commerce-Protocol/conformance
cd conformance && uv sync
# Follow the README to provide SERVER_URL, SIMULATION_SECRET, and fixture paths.The test suite covers: discovery, catalog pagination, cart creation, item add/remove, price change simulation, checkout initiation, payment token exchange, and order confirmation. A passing conformance suite means your agent handles the protocol correctly. It does not mean your UX is good, so test that separately.
Best Practices
Always confirm before purchasing. Show the user a summary with merchant, items, total, and shipping before calling checkout.complete(). Even automated reorder agents should log what they purchased and surface it to the user immediately.
Respect rate limits. Merchant manifests include rate limit headers. Your catalog search layer should cache results aggressively (5-10 minutes for most product data) and back off on 429 responses.
Handle partial cart failures gracefully. If a user wants items from three merchants and one fails, complete the other two and report the failure clearly. Do not abort the entire session.
Keep consumer auth tokens short-lived. 15-30 minute expiry windows are appropriate for interactive sessions. Background reorder agents should use single-use tokens.
The full spec reference and SDK documentation live at ucp.dev. For a browsable list of UCP-enabled merchants to test against, the UCPList merchant directory is the most current source available.
Read next
A practical guide for connecting LangChain, CrewAI, AutoGen, and other AI agent frameworks to UCP merchant endpoints. Includes code examples for each framework.
Headless commerce stores separate the front end from the commerce engine. Here is how UCP fits into headless stacks and what changes when your agent talks to a headless merchant.
An honest guide to the UCP developer tools worth knowing in 2026, from specs and conformance tests to directories and SDKs.