VetoVetoDocs
Getting Started

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 small fulfillment stub whose reference is 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:

order.settled — the POST body Veto sends you
{
  "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:

webhook.ts — verify, then deliver
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:

fulfill() — a digital good
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:

fulfill() — a physical good
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

StepWho does itWhat moves
DiscoverAgent reads yourdomain/.well-known/agentic-checkout.jsonnothing — just discovery
PayAgent pays USDC over x402; Veto governs the spendUSDC → your receiving address
ReceiptVeto signs a merchant receipta verifiable record, to the agent
NotifyVeto POSTs order.settled to your serverthe order: items + amount + receipt + buyer
DeliverYou — ship / email / grant accessthe product → the buyer