Step-by-step diagnosis and fixes for the most common UCP implementation issues. Start with the symptom that matches your problem.
Your /.well-known/ucp profile is not being found or is returning errors. Work through these checks:
dig your-store.com A
dig your-store.com AAAA
# Ensure the domain resolves to your server's IP
UCP requires HTTPS. Verify your SSL certificate is valid:
curl -I https://your-store.com/.well-known/ucp
# Expected: 200 OK
# Not: 301 redirect to HTTP, SSL error, or timeout
curl -I https://your-store.com/.well-known/ucp | grep Content-Type
# Expected: application/json
curl -I -X OPTIONS https://your-store.com/.well-known/ucp
# Must include:
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Methods: GET
# Access-Control-Allow-Headers: Content-Type, UCP-Agent
curl -s https://your-store.com/.well-known/ucp | jq .
# If jq fails, your JSON is malformed
curl -s https://your-store.com/.well-known/ucp | \
jq '{version: .ucp.version, services: (.ucp.services | keys), capabilities: (.ucp.capabilities | keys)}'
# Must have: version, at least one service, at least one capability
The file must be at /.well-known/ucp with NO file extension. Not ucp.json. Not /well-known/ucp. It must be /.well-known/ucp.
The agent and merchant cannot agree on capabilities. Here is how to diagnose:
# Merchant's version
curl -s https://merchant.com/.well-known/ucp | jq .ucp.version
# Agent's version (check your client config)
# If they don't match, check supported_versions:
curl -s https://merchant.com/.well-known/ucp | jq .ucp.supported_versions
Each capability is an array that can contain multiple versions. Ensure the agent and merchant have at least one version in common:
curl -s https://merchant.com/.well-known/ucp | \
jq '.ucp.capabilities["dev.ucp.shopping.checkout"][] | .version'
Spec URLs must match namespace authority. If they do not, platforms will reject the capability:
curl -s https://merchant.com/.well-known/ucp | \
jq '.ucp.capabilities["dev.ucp.shopping.checkout"][0].spec'
# Must start with https://ucp.dev/ for dev.ucp.* capabilities
If you are using extensions, their requires constraints must be satisfied:
# Fetch the extension schema and check requires
curl -s https://ucp.dev/.../discount.json | jq .requires
# Ensure your protocol version and capability versions meet the min/max
Checkout sessions are stuck or returning errors during completion:
GET https://your-store.com/api/ucp/checkout-sessions/{id}
# Check the status field:
# incomplete - missing required fields
# ready_for_complete - ready for payment
# requires_escalation - needs human input
# expired - timed out
You are missing required fields. Check:
buyer.email - requiredbuyer.first_name and buyer.last_name - requiredfulfillment.methods - at least one method requiredfulfillment.methods[0].selected_destination_id - must be setThis is normal, not an error. Present the continue_url to the buyer:
// In your agent's response to the user:
"I need you to verify your payment in your browser.
Please visit: {continue_url}
Once you're done, I'll check your order status."
The payment processor returned an error. Check:
Ensure your agent has subscribed to order lifecycle events. The subscription endpoint varies by implementation - check the merchant's OpenAPI spec.
# Can the merchant reach your webhook URL?
curl -X POST https://your-agent.com/webhooks/ucp \
-H "Content-Type: application/json" \
-d '{"test": true}'
# Should return 200 OK
Webhooks may be signed. If your signature validation fails, you might be silently dropping events:
// Verify webhook signature (implementation varies)
const signature = req.headers['ucp-signature'];
const payload = JSON.stringify(req.body);
if (!verifySignature(payload, signature, publicKey)) {
return res.status(401).send('Invalid signature');
}
If webhooks are unreliable, agents can fetch fresh order state on demand:
GET https://merchant.com/api/ucp/orders/{order_id}
# Use this when a webhook is missed or when the buyer asks "where's my order?"
# For each capability, check the schema URL
curl -I https://ucp.dev/2026-04-08/schemas/shopping/checkout.json
# Must return 200 OK
For extensions, each parent in extends must have a matching $defs key:
// If extends: ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"]
// Then $defs MUST have:
{
"$defs": {
"dev.ucp.shopping.checkout": { ... },
"dev.ucp.shopping.cart": { ... }
}
}
If an extension has requires, ensure the negotiated versions satisfy it:
// Extension requires:
"requires": {
"protocol": { "min": "2026-04-08" },
"capabilities": {
"dev.ucp.shopping.checkout": { "min": "2026-04-08" }
}
}
// Your negotiated versions must be >= min (and <= max if specified)
If agents get CORS errors when accessing your UCP endpoints:
# nginx
location /api/ucp/ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, UCP-Agent";
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://your_backend;
}
The UCP-Agent header is required for checkout operations. If it is not in Access-Control-Allow-Headers, preflight will fail.
Browser-based agents send OPTIONS requests before actual requests. Your server must respond with 204 No Content and the CORS headers.