Skip to content
/docs
Meow Gateway

Spend Capsule

A signed, single-use, TTL-bounded authorization that binds an agent's intent to an exact beneficiary, rail, amount, and policy.

A Spend Capsule is a JWS (Ed25519) wrapping a canonical JSON payload. It's what an agent presents to Meow via the gateway, without a valid capsule the call is denied before a single upstream byte flies.

Shape

{
  "version": "veto.capsule/1",
  "capsule_id": "cap_01hy2z...",
  "issuer": "meow-gateway:self-host",
  "entity_id": "ent_acme_llc",
  "agent_id": "agent_finance_bot",
  "tool": "meow.pay",
  "rail_allowlist": ["ach", "wire"],
  "counterparty_hash": "sha256:7ee7f2426cda71548a0fae87c291ff42469358bcb65ff2a0ffaf763d15bae5f4",
  "amount_ceiling": { "currency": "USD", "amount": "5000.00" },
  "invoice_hash": "sha256:9f2...",
  "workflow_id": "wf_01hy2z...",
  "policy_sha256": "ac3f...",
  "issued_at": "2026-04-21T14:03:00Z",
  "expires_at": "2026-04-21T14:18:00Z",
  "nonce": "8b2d4e...",
  "max_uses": 1
}

Drift axes

Every field in the payload is a drift anchor. At consume time the gateway verifies the runtime request matches the signed payload byte-for-byte on all axes:

AxisCheck
toolExact string match (e.g. meow.pay signed → cannot consume as meow.card.create)
counterparty_hashRecomputed from runtime beneficiary; must equal signed
railMust be in rail_allowlist; runtime pick is recorded in the receipt
amountRuntime ≤ amount_ceiling; currency exact match
invoice_hashRuntime ≥ signed (bind-once, consume-once)
expires_atnow < expires_at with a ±30s skew tolerance
nonceUnique per (organization, entity); replay = deny

Miss any axis → deny + chain a deny receipt with the specific mismatch code.

Why Ed25519 + JCS

Ed25519, 32-byte pubkey, 64-byte sig, deterministic (no nonce reuse foot-guns), widely-implemented in every target language. The @veto/spend-capsule-protocol package exposes matching signCapsule / verifyCapsule helpers in TS and Python so cross-language auditors read the same JWS.

JCS (RFC 8785), canonical JSON serialization. Two verifiers on two runtimes will serialize the same payload to the same bytes, so the hash is stable. JCS handles key sorting, number normalization, and UTF-8 encoding.

Lifetime

  1. Mint (POST /v1/capsules), gateway validates inputs against the active policy pack, reserves a budget lease, claims any required HITL approval, signs the payload, writes a receipt.
  2. Consume (POST /meow/consume or /meow/mcp), gateway verifies signature + all drift axes, atomically marks the capsule consumed, chains a decision receipt, forwards to Meow.
  3. Settle (webhook from Meow), gateway transitions the workflow to settled / failed.

A capsule is single-use. Multi-use was considered and removed, the consume path cannot atomically decrement a counter while also enforcing nonce replay. Re-minting is cheap; re-using is forbidden.

Idempotency

Pass an Idempotency-Key header on mint. Retries with the same key AND the same canonical request body return the exact original JWS. A retry with the same key but a different body returns 400 idempotency_key_reused_with_different_payload, prevents a "retry" from silently authorizing a different payment.

Where to go next