Meow Gateway
Error code catalog
Every code the gateway can emit, what it means, and what to do next.
Every non-2xx carries the Stripe-tier envelope:
{
"error": {
"type": "invalid_request",
"code": "capsule_payload_invalid",
"message": "amount.amount: Invalid",
"param": "amount.amount",
"doc_url": "https://docs.veto.so/docs/meow/errors/capsule_payload_invalid",
"hint": "Must match ^\\d+(\\.\\d{1,2})?$ for USD.",
"request_id": "req_01hy2z..."
}
}
| code | HTTP | What it means | Fix |
|---|
capsule_expired | 403 | now ≥ expires_at on the signed payload (+ skew tolerance) | Re-mint; raise ttl_seconds if legit |
capsule_signature_invalid | 403 | JWS signature does not verify against current or prev JWKS | Check the kid in the JWS header matches an active key; run GET /.well-known/veto-keys.json |
capsule_kid_unknown | 403 | Header kid not present in JWKS | Rotate consumer's cached JWKS; confirm the signer's kid is in the overlap window |
capsule_replayed | 409 | nonce already claimed for this entity | Mint a fresh capsule; each is single-use |
capsule_consumed | 409 | Atomic CAS found the capsule already in consumed status | Mint fresh |
capsule_payload_invalid | 400 | Zod validation failed on the request body | Inspect error.param + error.message |
| code | HTTP | What it means |
|---|
beneficiary_hash_mismatch | 403 | Runtime beneficiary hash ≠ signed hash. Invoice-swap attack or operator edited the payee between mint and consume |
beneficiary_held | 403 | Operator marked this counterparty held (compromised) |
beneficiary_quarantine_active | 403 | First-time payee; HITL approval required before any consume |
amount_exceeds_capsule_ceiling | 403 | Runtime amount > amount_ceiling.amount |
amount_exceeds_entity_cap | 403 | Policy pack's budgets.entity_{24h,7d,30d}_usd would be breached |
rail_not_in_allowlist | 403 | Runtime rail not in the signed rail_allowlist |
invalid_amount_precision | 400 | Currency-aware precision check (USD=2dp, USDC=6dp) |
| code | HTTP | What it means |
|---|
policy_eval_error | 500 | Internal; compiled pack raised during eval — file a bug |
policy_pack_not_found | 404 | Requested pack_id (or version) does not exist in storage |
policy_pack_hash_mismatch | 409 | Pack's sha256 changed under a live capsule; the chain flags this at consume |
| code | HTTP | Envelope adds | What it means |
|---|
approval_required | 409 | approval_id, payload_hash | Policy requires a human. Poll /v1/approvals/:id; re-mint with approval_ref once state=approved |
approval_denied | 403 | — | Operator denied. Terminal; do not retry |
approval_expired | 409 | — | Approval TTL elapsed. Raise fresh via re-mint |
approval_already_resolved | 409 | — | Idempotent replay of a resolve call — already approved or already denied |
approval_already_consumed | 409 | — | Single-use approval was already claimed by a prior mint |
approval_payload_mismatch | 409 | payload_hash | Approval was raised for different drift inputs than this mint |
dual_control_required | 409 | approval_id | Amount ≥ dual_control_threshold_usd; TWO distinct approvers must resolve |
| code | HTTP | What it means |
|---|
meow_upstream_error | 502 | Meow returned non-2xx. Inspect error.message for the raw code |
meow_timeout | 504 | Request to Meow exceeded timeout |
meow_rate_limited | 429 | Meow's rate limit. Back off per Retry-After |
| code | HTTP | What it means |
|---|
invoice_already_consumed | 409 | A capsule already bound this invoice_hash; can't consume twice (prevents double-pay) |
invoice_hash_missing | 400 | Pack requires an invoice reference; none supplied |
invoice_parse_failed | 502 | The parser errored; the row is in parse_failed state |
| code | HTTP | What it means |
|---|
workflow_invalid_transition | 409 | Requested state change violates the edge map. error.hint lists legal next states |
workflow_not_found | 404 | The referenced workflow_id does not exist |
workflow_already_exists | 409 | Create attempted with a non-unique (org, entity, workflow_id) |
| code | HTTP | What it means |
|---|
idempotency_key_reused_with_different_payload | 400 | Same Idempotency-Key seen, different canonical body. Prevents silent payment-substitution on retry |
| code | HTTP | What it means |
|---|
not_implemented | 501 | Spec'd endpoint not yet shipped in this block |
route_not_found | 404 | Path doesn't match any registered route |
signing_key_missing | 503 | No Ed25519 key configured. Run veto-meow-gateway keygen |
invalid_json | 400 | Body was not valid JSON |
payload_too_large | 413 | Body > 512KB (or 1MB for invoice ingest) |
internal_server_error | 500 | Unhandled exception; check logs with error.request_id |