Add agentic checkout to your site
The stack-agnostic way to make your store discoverable and buyable by AI agents — the discovery anchor, the manifest, and mounting the protocol on Node, Next.js, Express, or Hono (or any framework).
To make your store buyable by an AI agent, you expose one discovery URL and four
endpoints behind it. An agent starts at the discovery anchor, reads what you sell and where
to pay, and follows the next field in every response — it never reads these docs at runtime.
This guide is the connect path: where the URLs live, and how to mount them on whatever you
already run.
The contract: one anchor, four endpoints
GET /.well-known/agentic-checkout.json ← the discovery anchor (start here)
GET /agent/catalog ← your product list
POST /agent/checkout ← create a session, get a payment instruction
POST /agent/checkout/{id}/settle ← submit payment, run the acceptance gate
GET /agent/checkout/{id} ← poll session stateThe anchor advertises everything else — the catalog URL, the checkout URL, your supported
rails, your receiving address (pay_to), and a summary of your
policy so a well-behaved agent can self-screen before it even tries:
{
"protocol": "veto-checkout/0.1",
"merchant": { "id": "acme", "name": "Acme Corp", "domain": "shop.acme.example" },
"catalog_url": "/agent/catalog",
"checkout_url": "/agent/checkout",
"rails": ["x402", "mock"],
"pay_to": { "x402": { "chain": "base", "address": "0x…", "asset": "USDC" } },
"mandate": { "accepted": ["veto", "ap2", "acp", "none"], "preferred": "veto" },
"policy_summary": { "require_mandate_over_usd": 20, "max_per_transaction_usd": 500, "min_reputation_tier": "standard" }
}You don't hand-write this. createCheckout() builds it from your config, and the adapter
serves it at the right path. The URLs inside are relative, so the same manifest works
whether you mount at a root domain, a subdomain, or a path prefix.
Two ways to connect
Hosted — point a CNAME
Create your merchant in the control plane and route a subdomain to Veto. No code on your servers.
Self-hosted — mount the SDK
Add the SDK to the app you already run and expose the five routes from your own domain.
See self-host vs hosted for the full tradeoff.
Hosted: point DNS at Veto
In hosted mode you never run the protocol. Create the merchant, then make the discovery anchor resolve under your brand.
Create the merchant
curl -X POST https://api.veto-checkout.com/v1/merchants \
-H "Authorization: Bearer veto_test_…" -H "Content-Type: application/json" \
-d '{ "slug": "acme", "name": "Acme", "domain": "shop.acme.example",
"receiving": { "x402": { "chain": "base-sepolia", "address": "0xYourAddr…", "asset": "USDC" } } }'Add your catalog and policy (policies & trust), then
POST /v1/publish.
Route the discovery anchor
Your merchant is immediately live at the Veto-served forms:
https://acme.veto-checkout.com/.well-known/agentic-checkout.json (subdomain)
https://api.veto-checkout.com/m/acme/.well-known/agentic-checkout.json (path)To serve it under your own brand, add a CNAME so shop.acme.example resolves to your
Veto subdomain. Agents that discover you at your domain get a manifest whose relative URLs
resolve back to the same host — discovery "just works" on either form.
Verify discovery
curl https://acme.veto-checkout.com/.well-known/agentic-checkout.jsonYou should see your merchant block, catalog/checkout URLs, pay_to, and policy_summary.
The matching receipt JWKS is served at /.well-known/jwks.json.
Self-hosted: mount the SDK on your stack
createCheckout(config) returns a handle whose methods map 1:1 to the five endpoints. An
HTTP adapter wires those methods to your framework. The core has zero runtime
dependencies; framework adapters resolve your framework lazily, so nothing is imported until
you call it.
First, build the handle once (shared by every adapter below):
import { createCheckout, BALANCED } from '@veto-protocol/checkout';
export const checkout = createCheckout({
merchant: { id: 'acme', name: 'Acme', domain: 'shop.acme.example' },
catalog: [
{ sku: 'rpt-001', name: 'Market Report', price: { amount: '5.00', currency: 'USD' }, available: true },
],
receiving: { x402: { chain: 'base', address: process.env.VETO_X402_ADDRESS!, asset: 'USDC' } },
policy: BALANCED(),
receiptSigningKeySeedB64: process.env.VETO_RECEIPT_SEED,
});Then pick your adapter.
The zero-dependency node:http adapter — nothing to install:
import { createNodeServer } from '@veto-protocol/checkout/node';
import { checkout } from './checkout.ts';
createNodeServer(checkout).listen(8787, () => {
console.log('manifest at http://localhost:8787/.well-known/agentic-checkout.json');
});A single catch-all route handler. Place it at app/agent/[...veto]/route.ts:
import { vetoCheckoutNext } from '@veto-protocol/checkout/next';
import { checkout } from '@/checkout';
export const { GET, POST } = vetoCheckoutNext(checkout);Serve the discovery anchor and JWKS at the well-known paths (a tiny route or a rewrite to the
handler), so GET /.well-known/agentic-checkout.json resolves on your domain.
vetoCheckoutRouter mounts all five routes (and parses JSON for you):
import express from 'express';
import { vetoCheckoutRouter } from '@veto-protocol/checkout/express';
import { checkout } from './checkout.ts';
const app = express();
app.use(vetoCheckoutRouter(checkout)); // mount at the domain root
app.listen(8787);Express is an optional peer dependency — installed in your app, never bundled by the SDK.
vetoCheckoutHono returns a sub-app you mount at the root:
import { Hono } from 'hono';
import { vetoCheckoutHono } from '@veto-protocol/checkout/hono';
import { checkout } from './checkout.ts';
const app = new Hono();
app.route('/', vetoCheckoutHono(checkout)); // all 5 routes + /.well-known/jwks.json
export default app;Every adapter funnels through one pure function. If your framework isn't listed, call it directly — normalize your request in, serialize the response out:
import { handleRequest } from '@veto-protocol/checkout/handler';
import { checkout } from './checkout.ts';
// In your framework's handler:
const { status, headers, body } = await handleRequest(checkout, {
method: req.method,
path: url.pathname, // leading-slash path only, no query/host
body: await req.json().catch(() => undefined),
headers: Object.fromEntries(req.headers),
});
// → reply with `status`, `headers`, and JSON.stringify(body)handleRequest routes all five endpoints, emits the { reason_codes, error_human } error
shape, and uses HTTP status codes semantically — so routing semantics live in exactly one
place no matter which framework wraps it.
Confirm an agent can buy
Whichever path you chose, the proof is the same: discover, quote, settle. With the SDK's CLI:
veto discover http://localhost:8787
veto quote http://localhost:8787 --sku rpt-001 --qty 1
veto pay http://localhost:8787 --session <id> --mockA successful pay returns an accepted status, an order_id, and a signed receipt. That
receipt is the proof the sale happened — anyone can verify it offline.
Next steps
Test mode & sandbox
Run the whole flow with fake funds before you touch a real chain.
Go live with x402 + CDP
Wire real USDC settlement and move from testnet to mainnet.
Policies & trust
Set caps, mandates, and reputation floors before you open the doors.
Webhooks
Get a signed event every time an order is accepted, held, or rejected.
Set up Veto with your coding agent (MCP)
Hand the whole hosted setup to your coding agent. The @veto-protocol/mcp-merchant MCP server gives Claude Code, Cursor, or any MCP client the tools to create a merchant, add products, set receiving, and publish — you just describe your shop in plain English.
Test mode & sandbox
Build and exercise a full checkout with zero real money — the offline mock rail, hosted test keys and mode isolation, the deterministic mock facilitator, and a sandbox loop that mirrors production exactly.