How Veto works — discover, pay, deliver
The whole model in plain English. An agent discovers your checkout, pays you USDC over x402, and then YOU deliver the goods — triggered by a signed order.settled webhook that carries what was bought, the amount, the receipt, and the buyer's shipping details.
If you've used Stripe, the mental model is familiar: a customer pays, and you then fulfill the order — Stripe just tells you the payment happened. Veto is the same shape, with one twist: your customer is an AI agent, not a person clicking a button.
This page is the honest, end-to-end picture. Three parts: discover, pay, deliver. The third part — "after the agent pays, how does the buyer actually get what they paid for?" — is the one merchants ask about most, so it gets the most room.
The one-sentence version.
An agent discovers your checkout from a standard file on your domain, pays you USDC
over x402, and Veto then fires your server a signed order.settled webhook — and on that
webhook, you deliver the goods. Veto never holds the product (or the money); it governs
the spend and tells you exactly when, and to whom, to deliver.
1 · Discover — the agent finds your checkout
An agent has to find you before it can buy. There are three ways in, and you don't write any code for them:
Your own domain (one DNS record)
Point your domain at Veto and your checkout is described by a standard file at
https://yourdomain/.well-known/agentic-checkout.json. That single file is the bootstrap
URL every agent reads first — it advertises your catalog, your checkout URL, your rails,
your pay_to address, and a policy summary. One CNAME, no code. The recipe is in
Connect your site.
The Veto directory
Publish on the hosted platform and your merchant is listed in Veto's directory, so agents (and AI engines) can find you without knowing your URL in advance.
You hand over the link
Sometimes the simplest path: paste your checkout URL into an agent, a prompt, or a tool config. It's a normal URL.
The agent reads agentic-checkout.json, follows the next field through your catalog and
checkout, and never needs to read these docs at runtime — the wire protocol is
self-describing. (Full detail: Connect your site and the
Agentic Checkout Protocol.)
2 · Pay — the agent pays you, Veto governs the spend
When the agent decides to buy, it opens a checkout and Veto quotes a price. The agent pays in USDC over x402 — a gasless stablecoin payment. You do not touch any crypto plumbing, hold a wallet, or manage gas: you set your receiving address once, and settled USDC lands there.
Two things happen on every spend, and they're the heart of Veto:
- Veto governs. Before any money moves, Veto verifies the buyer's mandate and runs your acceptance policy. The rail only ever executes a spend the gate already approved. (This is the buyer-side promise too: the agent's own Veto policy decided it was allowed to spend here.)
- You get the money + a signed receipt. On a successful settle the agent receives an
order_id, a signed merchant receipt (an Ed25519 JWS anyone can verify offline), and a smallfulfillmentstub whosereferenceis the on-chain transaction hash.
Veto is non-custodial.
Veto never holds your funds. The USDC moves straight to your receiving address on Base. The only key Veto holds signs receipts, not value. See Set up Veto end-to-end for the full pay flow with real curls.
Here's the honest part that leads straight into delivery: the settle response does not contain your product. It confirms the payment and hands back a receipt — that's it. There is no in-band product delivery. So how does the buyer get what they paid for? That's part 3.
3 · Deliver — YOU ship it, Veto tells you when
This is the question every merchant asks: "the agent paid — now what?"
The honest answer: Veto does not deliver the product to the buyer. You do. Just like Stripe doesn't ship the sweater — your fulfillment system does, once Stripe tells you the charge succeeded. Veto works exactly the same way, using a Stripe-style webhook.
Here's the loop:
You tell Veto where to ping your server
In the dashboard, open Sale notifications and add your server's URL (or register it via
the API — see Webhooks). Veto gives you a signing secret
(whsec_…) for that endpoint, shown once. Keep it in your secret manager.
On every sale, Veto POSTs your server an order.settled event
The moment a checkout settles, Veto sends a signed POST to your URL. The body tells you
everything you need to fulfill: what was bought (the line items), the amount, the
receipt id, and the buyer's details — including a shipping address for physical
goods.
Your server verifies the signature, then delivers
You verify the delivery is really from Veto (one helper call — verifyWebhook), then run your
own fulfillment: ship the slippers, email the file, flip the "access granted" flag. The
delivery is yours; the webhook is just the trigger.
Veto governs · the rail executes · you deliver.
Veto's job ends at "the money is yours and here's who paid for what." Turning that into a delivered product — a parcel, a download, an unlocked account — is the merchant's job. The webhook is the seam between the two.
The event your server receives
It's a normal JSON POST. The envelope is stable; the part you care about is data.object,
the order:
{
"id": "evt_01J…",
"type": "order.settled",
"created": 1750000000,
"livemode": true,
"api_version": "v1",
"data": {
"object": {
"id": "ord_01J…",
"session_id": "sess_…",
"merchant_id": "mrch_01J…",
"agent_id": "11111111-1111-1111-1111-111111111111",
"total": { "currency": "USD", "subtotal": "39.00", "tax": "0.00", "total": "39.00" },
"items": [{ "sku": "slipper-cloud-9", "qty": 1 }],
"rail_name": "x402",
"settlement_ref": "0x…txHash…",
"receipt_id": "rcpt_01J…",
"mandate_type": "veto",
"trust_tier": "trusted",
"buyer": {
"name": "Ada Lovelace",
"shipping_address": {
"line1": "12 Mathematician's Way",
"city": "London",
"postal_code": "EC1A 1BB",
"country": "GB"
}
}
}
}
}settlement_ref is the on-chain transaction hash (your proof of payment). receipt_id
points at the signed merchant receipt. items is what to fulfill. buyer is who and where
to deliver — present when the checkout captured a shipping address. The full field list and
event types are in Webhooks.
A real webhook handler
A tiny Node endpoint that verifies the delivery and then fulfills. The only Veto-specific line
is verifyWebhook from @veto-protocol/checkout:
import { verifyWebhook } from '@veto-protocol/checkout';
export async function POST(req: Request) {
// 1. Read the RAW body — never JSON.parse → re-stringify before verifying.
const raw = await req.text();
const signature = req.headers.get('veto-signature'); // "t=…,v1=…"
// 2. Verify it really came from Veto (positional args; returns { ok, reason? }).
const result = verifyWebhook(raw, signature, process.env.VETO_WEBHOOK_SECRET!);
if (!result.ok) {
return new Response(`bad signature: ${result.reason}`, { status: 400 });
}
// 3. Dedupe — delivery is at-least-once, so the same event can arrive twice.
const eventId = req.headers.get('veto-event-id')!;
if (await alreadyHandled(eventId)) return new Response('ok'); // no-op replay
await markHandled(eventId);
// 4. Deliver. Fulfill on order.settled (the rail has confirmed the money).
const event = JSON.parse(raw);
if (event.type === 'order.settled') {
await fulfill(event.data.object); // <-- YOUR delivery logic (below)
}
// 5. Return 2xx fast (<10s) to ack. Anything else triggers a retry.
return new Response('ok', { status: 200 });
}Digital good — grant or return access
For a download, a license, or an account upgrade, fulfillment is a database write and a notification. There's no shipping address to read:
async function fulfill(order: any) {
// 'items' tells you what was bought; settlement_ref / receipt_id are your proof.
const link = await mintDownloadUrl(order.items, order.receipt_id);
// Deliver out of band: email it, expose it on your own /orders/:id API,
// or unlock the buyer's account. Veto carries no product bytes for you.
await emailBuyer(order.buyer, link);
}Physical good — ship using the buyer's address
For a parcel, read the shipping address straight off the event and hand it to your carrier or fulfillment provider:
async function fulfill(order: any) {
const ship = order.buyer?.shipping_address;
if (!ship) throw new Error('no shipping address on a physical order'); // 4xx → Veto retries
await createShipment({
to: { name: order.buyer.name, ...ship },
lines: order.items, // [{ sku, qty }]
reference: order.id, // ord_… — your idempotency anchor
});
}Be honest with yourself about delivery.
Veto guarantees you the money and a signed record of who paid for what. It does not
guarantee the parcel arrives — that's on your fulfillment, exactly as it is with Stripe. If
your fulfill() can fail (carrier down, file not ready), return a non-2xx and Veto will
retry the webhook on a backoff (7 attempts over ~36h), so a transient blip doesn't drop the
order. Make fulfill() idempotent — dedupe on the event id or order.id.
The whole arc, in one glance
| Step | Who does it | What moves |
|---|---|---|
| Discover | Agent reads yourdomain/.well-known/agentic-checkout.json | nothing — just discovery |
| Pay | Agent pays USDC over x402; Veto governs the spend | USDC → your receiving address |
| Receipt | Veto signs a merchant receipt | a verifiable record, to the agent |
| Notify | Veto POSTs order.settled to your server | the order: items + amount + receipt + buyer |
| Deliver | You — ship / email / grant access | the product → the buyer |
Webhooks — the full contract
Every event type, the exact order object, the Veto-Signature HMAC scheme, dedupe, and the
retry backoff.
Set up Veto end-to-end
Zero to a live agent purchase — keys, merchant, receiving address, publish, and the pay flow.
Connect your site
The one DNS record that makes yourdomain/.well-known/agentic-checkout.json resolve.
Verify receipts
Check the signed receipt offline against the merchant's public key.
Getting Started
Install the Veto Checkout SDK, stand up a checkout, and understand the path from zero to a published, agent-discoverable storefront.
Set up Veto end-to-end (hosted)
Zero to a live agent purchase on the hosted platform — sign in, mint keys, create a merchant, add a product, set your receiving address, publish, connect your site, and watch an AI agent buy it over x402.