Reason codes
The complete, stable rejection vocabulary. Every rejection (and several accepts) carries one or more of these SCREAMING_SNAKE_CASE codes — a stable API surface an agent keys off.
Reason codes are the lingua franca of every decision. An agent can't read prose docs at
runtime, so every rejection (and several accepts) carries one or more of these
SCREAMING_SNAKE_CASE codes. They are a stable API surface — never renamed or
repurposed, only added.
import { REASON, humanReason, humanReasons } from '@veto-protocol/checkout';
humanReason('OVER_PER_TX_CAP'); // "The amount exceeds the per-transaction limit."Happy path
| Code | Human |
|---|---|
OK | Acceptable. |
Mandate — presence & shape
| Code | Human |
|---|---|
MANDATE_REQUIRED | A signed authorization mandate is required for this amount. |
MANDATE_TYPE_UNSUPPORTED | That mandate type is not supported yet. |
MANDATE_MALFORMED | The presented mandate could not be parsed. |
Mandate — cryptographic & binding
| Code | Human |
|---|---|
MANDATE_INVALID_SIGNATURE | The signature did not verify against a trusted issuer. |
MANDATE_EXPIRED | The mandate has expired. |
MANDATE_AMOUNT_INSUFFICIENT | The authorized amount is less than the cart total. |
MANDATE_MERCHANT_MISMATCH | The mandate authorized a different merchant. |
MANDATE_NOT_APPROVED | The mandate decision was not an approval. |
MANDATE_REPLAY | This authorization was already used. |
MANDATE_UNBOUND | The mandate is not bound to this merchant or cart. |
MANDATE_CART_MISMATCH | The mandate authorized different cart contents. |
Reputation
| Code | Human |
|---|---|
REPUTATION_TOO_LOW | The agent reputation is below the required minimum. |
AGENT_BLOCKED | This agent is blocked by the merchant. |
Dollar caps & velocity
| Code | Human |
|---|---|
OVER_PER_TX_CAP | The amount exceeds the per-transaction limit. |
OVER_DAILY_CAP | This would exceed the agent daily spend limit. |
RATE_LIMITED_HOURLY | Too many transactions from this agent this hour. |
RATE_LIMITED_DAILY | Too many transactions from this agent today. |
Intent
| Code | Human |
|---|---|
INTENT_FORBIDDEN_KEYWORD | The stated intent contains a forbidden term. |
INTENT_MISMATCH | The stated intent does not match the cart contents. |
Rails & settlement
| Code | Human |
|---|---|
RAIL_NOT_ALLOWED | The selected payment rail is not accepted here. |
RAIL_NOT_IMPLEMENTED | That payment rail is not implemented. |
PAYMENT_INVALID | The payment proof was invalid or could not be captured. |
PAYMENT_EXPIRED | The payment quote expired before settlement. |
x402 on-chain (real facilitator only)
| Code | Human |
|---|---|
SIGNER_MISMATCH | The signature did not recover to the stated payer address. |
UNSUPPORTED_CHAIN | The chain/network is not supported by the facilitator. |
INSUFFICIENT_FUNDS | The payer wallet did not have enough USDC. |
NONCE_ALREADY_USED | This payment authorization was already settled on-chain. |
SETTLEMENT_FAILED | On-chain settlement failed; verify the chain before retrying. |
Review routing
| Code | Human |
|---|---|
HOLD_FOR_REVIEW | Held for human review. |
MANDATE_REQUIRED_HOLD | Held: this amount needs a mandate, which was not provided. |
Session lifecycle
| Code | Human |
|---|---|
SESSION_NOT_FOUND | No checkout session with that id. |
SESSION_ALREADY_SETTLED | This checkout session was already settled. |
SESSION_EXPIRED | The checkout session has expired; start a new one. |
SESSION_INVALID_STATE | The session is not in a settleable state. |
POLICY_ERROR | The acceptance policy could not be evaluated; held to be safe. |
Request, cart & idempotency
| Code | Human |
|---|---|
IDEMPOTENCY_KEY_REUSED | This idempotency key was already used with a different request. |
BAD_REQUEST | The request was malformed. |
BAD_JSON | The request body was not valid JSON. |
PAYLOAD_TOO_LARGE | The request body was too large. |
AGENT_ID_REQUIRED | An agent id is required. |
ITEMS_REQUIRED | At least one line item is required. |
SKU_UNKNOWN | One of the requested SKUs is not in the catalog. |
SKU_UNAVAILABLE | One of the requested SKUs is not available. |
QTY_INVALID | A requested quantity was not a positive whole number. |
CART_EMPTY | The resulting cart was empty. |
CURRENCY_MISMATCH | The line items are priced in more than one currency. |
PRICE_MALFORMED | A catalog price was not a valid amount. |
CART_BUILD_FAILED | The cart could not be assembled (catch-all). |
QUOTE_FAILED | A payment quote could not be produced for this rail. |
Internal / fail-safe
These fire when something unexpected throws. The gate fails safe — it never silently accepts — so a verification or policy error becomes a hold/reject, not an allow.
| Code | Human |
|---|---|
MANDATE_VERIFY_ERROR | The mandate could not be verified due to an internal error. |
RECEIPT_ISSUE_FAILED | Settlement succeeded but the receipt could not be issued. |
NETWORK_ERROR | A network error occurred while contacting the merchant. |
TOOL_ERROR | An MCP tool wrapper threw while invoking the merchant. |
Receipts
| Code | Human |
|---|---|
RECEIPT_MALFORMED | The receipt could not be parsed. |
RECEIPT_INVALID_SIGNATURE | The receipt signature did not verify. |
RECEIPT_WRONG_TYPE | The receipt was not the expected type. |
RECEIPT_WRONG_ISSUER | The receipt was issued by an unexpected party. |
RECEIPT_KEY_UNRESOLVED | The receipt signing key could not be resolved. |
Every code here is exported from @veto-protocol/checkout as REASON.<CODE>, with a human
gloss via humanReason(code). Unknown codes fall back to the raw code string, so a
rail-specific code still renders something rather than undefined. Codes are append-only
— never renamed or repurposed — so you can safely key logic off the string.