VetoVetoDocs
Getting Started

Quickstart

Go from npx to an AI agent completing a sandbox checkout in about five minutes — one scaffold command, three run commands, four scenarios, zero install.

The fastest path is the scaffolder: it writes a runnable merchant + buyer agent that complete a sandbox checkout end to end on the offline mock rail — no chain, no wallet, no real money. The only prerequisite is Node ≥ 22.6.

The 5-minute path

Scaffold a project

npx @veto-protocol/create-checkout my-shop --template quickstart

This writes a small project — and nothing else runs yet, so it's safe:

  • merchant.ts — a digital-goods shop standing up createCheckout(...) on the node:http adapter, test mode / mock rail, with a 3-item catalog and a BALANCED() policy.
  • buyer.ts — a buyer agent that discovers the manifest and runs four canonical scenarios.
  • scripts/setup-keys.ts — mints the test Veto issuer keypair the demo trusts.

The scaffolder is itself zero-dependency — it only writes files and prints the run command last. quickstart is the only template today.

Mint the test issuer keypair

cd my-shop
node --experimental-strip-types scripts/setup-keys.ts

This writes a test veto issuer keypair into keys/ (gitignored). The merchant trusts the public JWKS; the buyer agent signs mandates with the matching private seed — exactly mirroring reality, where a merchant only ever holds the public key.

Start the merchant

node --experimental-strip-types merchant.ts &

The shop comes up on http://localhost:8787 in test mode / mock rail and prints its discovery URL (/.well-known/agentic-checkout.json) and receipt JWKS URL (/.well-known/jwks.json).

Run the buyer agent

node --experimental-strip-types buyer.ts

The agent is given only the merchant origin. It discovers the manifest and follows the self-describing responses through four scenarios:

#ScenarioExpected
aSmall purchase ($5), no mandateaccepted on reputation alone
bLarge purchase ($40), no mandateholdMANDATE_REQUIRED_HOLD
cLarge purchase ($120) with a minted veto mandateaccepted (premium) + verified receipt
dReplay the same mandaterejectedMANDATE_REPLAY

Expect the final line: Summary: 4/4 scenarios passed.

That is a real AI agent completing a sandbox checkout — discovery, the acceptance gate, signed mutual receipts, and replay defense — with no chain and no install. The rest of this page builds the same thing from scratch so you understand every moving part.


From scratch

This is the whole integration by hand: one config object, one server, and an order settled end to end on the mock rail.

1. Install

npm install @veto-protocol/checkout

The SDK has zero runtime dependencies and targets Node ≥ 22.6. The examples below run as-is with node --experimental-strip-types server.ts — no build step.

2. Stand up a checkout

createCheckout(config) returns a handle whose methods map 1:1 to the wire endpoints. createNodeServer mounts them on node:http.

server.ts
import { createCheckout, BALANCED } from '@veto-protocol/checkout';
import { createNodeServer } from '@veto-protocol/checkout/http';

const checkout = createCheckout({
  merchant: { id: 'acme', name: 'Acme Corp', domain: 'shop.acme.example' },
  catalog: [
    {
      sku: 'widget',
      name: 'Blue Widget',
      price: { amount: '12.50', currency: 'USD' },
      available: true,
    },
  ],
  // Non-custodial: YOUR receiving address. (mock shown; add x402 for real settlement.)
  receiving: { x402: { chain: 'base', address: '0xYourAddress', asset: 'USDC' } },
  policy: BALANCED(), // STRICT() | BALANCED() | OPEN(), then tweak fields
  // rails defaults to [x402, mock] when omitted.
});

createNodeServer(checkout).listen(8080);
console.log('Veto Checkout listening on :8080');

That is a fully self-describing agentic checkout on port 8080.

3. Discover, create, settle

An agent only needs the manifest URL; every response carries the literal next call.

# 1) Discover — the single bootstrap URL
curl http://localhost:8080/.well-known/agentic-checkout.json

# 2) Create a session (returns a self-describing `payment_required`)
curl -X POST http://localhost:8080/agent/checkout \
  -H 'content-type: application/json' \
  -d '{
    "agent_id": "11111111-1111-1111-1111-111111111111",
    "items": [{ "sku": "widget", "qty": 1 }],
    "rail": "mock"
  }'

# 3) Settle — run the acceptance gate, then capture
curl -X POST http://localhost:8080/agent/checkout/<SESSION_ID>/settle \
  -H 'content-type: application/json' \
  -d '{ "payment": { "mock": true } }'

You can also drive the handle directly, without any HTTP:

buy.ts
const created = await checkout.createSession({
  agent_id: '11111111-1111-1111-1111-111111111111',
  items: [{ sku: 'widget', qty: 1 }],
  rail: 'mock',
});

const settled = await checkout.settle(created.body.session_id, {
  payment: { mock: true },
});

console.log(settled.status, settled.body);
// → 'accepted' { status: 'accepted', order_id, receipt, fulfillment }

4. The result

On allow, the settle returns 200 with an order_id, a signed receipt, and your fulfillment:

{
  "status": "accepted",
  "order_id": "…",
  "receipt": "<compact JWS merchant receipt>",
  "fulfillment": { "/* merchant-defined */": "download url, license key, …" }
}

The receipt is an Ed25519-signed compact JWS. Anyone can verify it offline.

Already on a framework?

The SDK ships adapters for node:http, Express, Hono, and Next.js. See SDK → HTTP adapters.