VetoVetoDocs
Concepts

Policy

The local acceptance rules the gate evaluates — caps, rate limits, reputation floor, mandate requirement, intent, and review routing — plus the STRICT / BALANCED / OPEN presets.

Your acceptance policy is a plain object the gate evaluates locally on every settle. Every field is optional; safe defaults fill the rest. Start from a preset, then tune.

The presets

Adopt a posture wholesale, then override individual fields. The presets are functions so each call yields a fresh, isolated copy.

The recommended default. Small spends clear on reputation alone; larger spends need a mandate; generous-but-bounded caps and rates; only the weakest tier is sent to review.

{
  requireMandateOverUsd: 20,
  minReputationTier: 'standard',
  maxPerTransactionUsd: 500,
  maxPerAgentPerDayUsd: 2000,
  ratePerAgentPerHour: 60,
  ratePerAgentPerDay: 300,
  holdForReviewBelowTier: 'cautious',
}

High-value / sensitive merchants. Always wants a mandate, a trusted reputation, modest caps, tight velocity, and routes anything not clearly trusted to human review.

{
  requireMandateOverUsd: 0,        // a mandate is required for ANY spend
  minReputationTier: 'trusted',
  maxPerTransactionUsd: 100,
  maxPerAgentPerDayUsd: 250,
  ratePerAgentPerHour: 10,
  ratePerAgentPerDay: 50,
  holdForReviewBelowTier: 'trusted',
}

Maximum reach, minimum friction — e.g. cheap digital goods or content APIs where the paid-up-front rail is the guarantee and a bad sale costs almost nothing.

{
  requireMandateOverUsd: Infinity, // effectively never require a mandate
  minReputationTier: 'risky',      // accept the lowest tier
  maxPerTransactionUsd: 10000,
  holdForReviewBelowTier: 'cautious', // nothing is below cautious → never holds
}

Adopt and tweak

import { createCheckout, BALANCED } from '@veto-protocol/checkout';

const checkout = createCheckout({
  merchant: { id: 'acme', name: 'Acme', domain: 'shop.acme.example' },
  catalog: [/* … */],
  receiving: { x402: { chain: 'base', address: '0x…', asset: 'USDC' } },
  policy: {
    ...BALANCED(),
    maxPerTransactionUsd: 250,
    blockedAgents: ['00000000-0000-0000-0000-000000000000'],
    forbiddenIntentKeywords: ['gift card', 'crypto'],
  },
});

The fields

FieldEffectFires
requireMandateOverUsdAbove this total, a valid mandate is required.MANDATE_REQUIRED
minReputationTierReputation floor (riskyelite).REPUTATION_TOO_LOW
maxPerTransactionUsdHard per-transaction cap.OVER_PER_TX_CAP
maxPerAgentPerDayUsdRolling 24h spend cap per agent.OVER_DAILY_CAP
ratePerAgentPerHourMax transactions / agent / hour.RATE_LIMITED_HOURLY
ratePerAgentPerDayMax transactions / agent / day.RATE_LIMITED_DAILY
blockedAgentsExplicit blocklist of agent ids.AGENT_BLOCKED
allowedRailsRestrict which rails may settle.RAIL_NOT_ALLOWED
forbiddenIntentKeywordsReject if intent text contains a term.INTENT_FORBIDDEN_KEYWORD
requiredIntentMatchRequire intent↔cart match (needs an intentMatcher).INTENT_MISMATCH
holdForReviewBelowTierRoute below-tier requests to human review.HOLD_FOR_REVIEW

requiredIntentMatch: true holds every transaction unless you also wire an intentMatcher into your config — otherwise there is no score to compare. The presets deliberately leave it off for this reason.

See the full policy fields reference and the complete reason-code vocabulary.