Skip to content

Selling to AI agents

Every store on Tuppence is open to shopping agents from the moment it sells online. An agent finds the store, searches it, builds a basket and gets it paid for, through whichever protocol it speaks. Every door runs the same catalogue, basket and checkout as the store’s website: the same prices, tax, delivery dates and policies. No language model runs on any of them; answers are data.

AgentStore (Tuppence)Personagent.json, /.well-known/ucpsearch, basket, checkoutcontinue_urlopens the link and payscomplete with a tokenorder confirmation

A store lives on its own domain, or under its slug on the shared address (https://shop.tuppence.ai/<slug>). The test store is the same path under /test/<slug>: it answers anyone, says livemode: false, and is never indexed or listed.

DoorWhere (on the store’s address)For
Guide/agent.json, /llms.txtAny agent: the doors, headers, tiers, limits and the catalogue in outline
UCP profile/.well-known/ucpUCP platforms: services, capabilities, payment handler, signing keys
UCP over REST/ucp/v1/… (catalog/search, carts, checkout-sessions, orders/{id})UCP platforms
UCP over MCPPOST /ucp/mcp (search_catalog, create_cart, create_checkout, …)UCP platforms that prefer MCP. meta["ucp-agent"].profile on every call
Store MCPPOST /mcp (search_products, create_cart, checkout_link, complete_checkout, …)Any MCP client, such as a person’s own assistant
ACP/acp/v1/checkout_sessionsPartner agents with a key from Tuppence

On the shared address the UCP profile is https://shop.tuppence.ai/<slug>/.well-known/ucp, not the root: the store’s agent.json links it.

Start with the guide. It names every door with its full address, the headers and the limits:

Terminal window
curl -sS https://shop.tuppence.ai/harbour-gifts/agent.json

The UCP profile lists the services, the capabilities, the payment handler (when the store takes native checkout) and the key the store signs its order updates with:

Terminal window
curl -sS https://shop.tuppence.ai/harbour-gifts/.well-known/ucp

The strongest valid identity a request carries wins. One that is presented and does not verify is refused (401), never treated as anonymous.

HeaderTier it can give
Signature-Input, Signature, Signature-Agent (Web Bot Auth, RFC 9421, Ed25519 or ECDSA)verified when the key comes from a network directory or a listed assistant’s own directory, declared otherwise
UCP-Agent: profile="https://…" with an RFC 9421 signature by a key in that profileverified for a listed platform, declared otherwise
Tuppence-Agent with Tuppence-Agent-Proof (a Tuppence agent)verified
Tuppence-Agent: Name/1.0 (+https://…) alone, or a known assistant’s user agentdeclared
Nothinganonymous

A signed write (a basket, a checkout, a completion) needs a fresh nonce; a reused one is 401 agent_signature_replayed. @authority is the store’s address as the shopper sees it.

TierMayBudget
anonymousBrowse every door, build baskets, start checkouts. Ends in a checkout link10 reads and 1 write a second per address
declaredThe same, on its own budget0.5 a second per name and address
verifiedAll of that, and pay by itself within the business’s limits5 a second per identity

Every store also has a budget of 20 agent requests a second. Over a budget is 429 with Retry-After.

Search, products and baskets are priced by the store, VAT in outside the US and before tax in the US (tax_included). Never send a price or total: each step prices again.

Over the store MCP, any client can search. This one declares its name, so it gets its own budget:

Terminal window
curl -sS https://shop.tuppence.ai/harbour-gifts/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Tuppence-Agent: GiftFinder/1.0 (+https://giftfinder.example/agent)" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": "search_products", "arguments": {"query": "candle"}}}'

Over UCP the same search is POST /ucp/v1/catalog/search with {"query": "candle"} and your platform’s UCP-Agent: profile="https://…" header. The store fetches that profile to agree the capabilities, so it must be on a public https address.

The checkout link (every tier). Every basket and checkout answers with continue_url: the store’s checkout with the basket, address and delivery filled in. Hand it to the person. They see any change in price before Pay, and pay with their own wallet.

Here an agent finds a candle, puts two in a basket for the person’s address and asks the store MCP for the link:

const STORE = "https://shop.tuppence.ai/harbour-gifts";
const call = async (name, args) => {
const res = await fetch(`${STORE}/mcp`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json, text/event-stream",
"Tuppence-Agent": "GiftFinder/1.0 (+https://giftfinder.example/agent)",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: { name, arguments: args },
}),
});
const { result } = await res.json();
const answer = JSON.parse(result.content[0].text);
if (result.isError) throw new Error(JSON.stringify(answer));
return answer;
};
const { products } = await call("search_products", { query: "candle" });
const address = { country: "GB", postcode: "TR11 3HH" };
const cart = await call("create_cart", {
lines: [{ variant: products[0].variants[0].id, quantity: 2 }],
address,
});
const linked = await call("checkout_link", {
cart_token: cart.cart_token,
buyer: { email: "sam.shopper@example.test", name: "Sam Shopper" },
address: { ...address, name: "Sam Shopper", line1: "1 Harbour Row", city: "Falmouth" },
delivery: cart.delivery_options[0].key,
});
console.log(`2 × ${products[0].title}. Pay here: ${linked.checkout_url}`);

Native checkout (verified agents). Where the business’s store has a network ID (the UCP profile’s payment handler and the store MCP’s complete_checkout name it), a verified agent completes a ready checkout with a Shared Payment Token that the person’s agent platform granted to that network ID, as the credential of the instrument it selects (the type the profile’s payment handler names). The token must cover the total in the checkout’s currency. A declined card leaves the checkout ready for another token.

These baskets always go to the person, whatever the tier: age-restricted products, digital products, subscriptions, bookings, gift cards and products in a drop. So does an order above the business’s limit for that assistant (£250 or $250 unless the business changes it), and every order while the business is new to Tuppence.

Agent orders are store orders with source: agent and agent_details (the assistant, tier, protocol, and whether the person paid through the link).

Terminal window
curl -sS "https://api.tuppence.ai/v1/orders?source=agent&limit=10" \
-H "Authorization: Bearer $TUPPENCE_SECRET_KEY"

A UCP platform reads the orders it placed at /ucp/v1/orders/{id} (or get_order over MCP) and gets the whole order at its profile’s webhook_url on every change, signed.

CodeWhen
invalid_agent_signature (401)A signature that does not verify, a key not found, or created outside 30 seconds
agent_signature_replayed (401)A write’s nonce was used before
agent_not_allowed (403)The business switched this assistant off, or hid the product from it
agent_blocked (403)This identity is paused here (three declined payments in an hour) or limited to reads everywhere. Retry-After says when it ends
requires_escalationThe person must pay: the reason and continue_url
native_checkout_unavailable, store_not_eligiblePay through the checkout link
buyer_email_requiredA native completion without the person’s email: the confirmation goes to them
shared_token_expired, shared_token_over_limit, shared_token_currency_mismatch, shared_token_deactivated, shared_token_wrong_seller (402)The token can’t pay this checkout. Each names the limit and the total
acp_partner_required (401)An ACP call without a partner key

Docs assistant

Open as a page
Loading the assistant…