Self-host vs hosted
The two ways to run Veto Checkout — mount the zero-dependency SDK on your own domain, or use the hosted control plane. What each owns, what's identical across both, and how to choose.
There are two ways to run Veto Checkout, and they speak the same wire protocol. An agent that can buy from a self-hosted merchant can buy from a hosted one with no change — discovery, the self-describing payment instruction, the acceptance gate, and the signed receipt are identical. The difference is who runs the moving parts.
Self-hosted (the SDK)
createCheckout() on your own domain. Zero runtime deps, non-custodial, you own
storage, keys, and uptime.
Hosted (the control plane)
Veto serves your manifest, stores config and orders, runs webhooks and reputation, and injects the live settlement facilitator.
What's identical across both
Whichever you pick, these never change — they're the protocol, not the deployment:
- The endpoints.
GET /.well-known/agentic-checkout.json,GET /agent/catalog,POST /agent/checkout,POST /agent/checkout/{id}/settle,GET /agent/checkout/{id}. - The acceptance gate. Verify mandate → replay check → reputation → policy verdict → capture → receipt. Same ordering, same reason codes.
- Non-custodial settlement. Funds move agent → your receiving address. Veto never holds money in either mode.
- The receipt. An Ed25519-signed merchant receipt that anyone can verify offline.
Self-hosted: mount the SDK
You import @veto-protocol/checkout, call createCheckout(config), and mount the handle on
your domain with one of the HTTP adapters. It has zero runtime dependencies and runs on
Node ≥ 22.6.
import { createCheckout, BALANCED } from '@veto-protocol/checkout';
import { createNodeServer } from '@veto-protocol/checkout/node';
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,
});
createNodeServer(checkout).listen(8787);You own:
| Concern | What you do |
|---|---|
| Storage | The default MemoryStore is single-process. Swap in a SessionStore over Redis/Postgres for durability and scale — see going to production. |
| Keys | You hold the receipt-signing seed (receiptSigningKeySeedB64) and publish the public JWK at /.well-known/jwks.json. |
| Settlement | For real on-chain capture you inject an x402 facilitator. The core stays zero-dependency; the facilitator owns the viem/CDP code. |
| Reputation (optional) | Point reputation.baseUrl at the hosted reputation service, or run fully local (every agent reads as standard). |
| Reporting (optional) | Wire onEvent to mirror terminal outcomes anywhere — including the hosted ingest API. |
Choose self-hosted when you want full control, data residency, no third-party in the request path, or you're embedding checkout inside an existing service.
Hosted: the control plane
You create your merchant, catalog, receiving, and policy through the REST API (or the
dashboard), and Veto serves the protocol for you under a
<slug>.veto-checkout.com subdomain (or a /m/<id> path). The same SDK runs server-side; you
just don't operate it.
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" } }
}'The control plane owns:
| Concern | What it does |
|---|---|
| Manifest + JWKS serving | Builds and caches /.well-known/agentic-checkout.json and serves your receipt JWKS (R4). |
| Config storage | Catalog, receiving, and immutable, versioned policies, with a validate + publish gate. |
| Keys | Mints your Ed25519 receipt key, stores the seed AES-GCM-encrypted at rest, and never returns it. |
| Live settlement | Injects the real CDP x402 facilitator for veto_live_ keys; veto_test_ keys settle on a deterministic mock — see test mode. |
| Reputation | Hosted agent scoring updated on every terminal outcome. |
| Webhooks | Signed, retried event delivery to your endpoints — see webhooks. |
| Orders | A queryable record of every accepted order for the dashboard's Activity view. |
Choose hosted when you want to ship fast, skip running storage and key management, and get webhooks, reputation, and the dashboard out of the box.
A spectrum, not a wall
These aren't mutually exclusive. The clean seam is CheckoutConfig.onEvent (and the matching
reputation config): a self-hosted SDK can post terminal outcomes to the hosted ingest API to
get hosted reputation and orders while still owning its own request path and storage.
createCheckout({
// … your self-hosted config …
reputation: { baseUrl: 'https://api.veto-checkout.com', apiKey: process.env.VETO_API_KEY },
onEvent: (event) => reportToHostedIngest(event), // best-effort; never blocks settle
});The protocol is the contract; everything else is a dial you can turn from "all mine" to "all managed."
Choosing
Guides
Task-focused walkthroughs — choose a deployment model, connect your store, run in sandbox, go live on-chain, set policy, and harden for production.
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.