# Carte Blanche > Spending mandates, sealed receipts and dispute-proof audits for AI agents paying with PayPal. > An agent with a payment tool is a walking blank check: this layer sits between the agent and > PayPal, checks every mutative call against an Ed25519-signed spending mandate BEFORE money moves, > executes with a deterministic PayPal-Request-Id (a retry can never double-spend), and answers > with a signed receipt — or a signed proof of refusal naming the limit that blocked it. Live instance (PayPal sandbox rails): https://carte-blanche-6jgo.onrender.com Contract: https://carte-blanche-6jgo.onrender.com/openapi.json (OpenAPI 3.0, APIMatic-validated) MCP server: `mcp/server.ts` in the repo (`npm run mcp`) — the same API as agent tools. ## Core concepts - Mandate: signed, immutable spending authority (per-transaction cap, total cap, currency, payee allowlist, time window, optional recurrence). Only revocation changes behavior. The agent cannot escalate its own budget. - Recurrence: standing authority — "up to $X per month at the grocer". The mandate's window is the whole horizon (e.g. a year); `limits.recurrence: {frequency: "monthly"|"weekly", maxPerWindow}` slices it into periods, each with its own cumulative cap (reason code OVER_WINDOW_CAP when the period's budget is spent — distinct from OVER_TOTAL_CAP, the mandate-wide wall). Spend accounting comes from the sealed chain itself; a new period resets automatically, no state outside the chain. - Receipt: signed bundle proving a purchase happened under a mandate, with journal events and a Merkle inclusion proof against a signed tree head (RFC 3161 TSA-anchored). - Refusal: signed proof that an attempt was BLOCKED, naming the limit (reason codes: OVER_PER_TRANSACTION, OVER_WINDOW_CAP, OVER_TOTAL_CAP, PAYEE_NOT_ALLOWED, MANDATE_REVOKED, ...). Refusals are evidence, not errors: treat them as the human's standing decision, do NOT retry around them. ## API map (all JSON; mutative endpoints are the agent boundary) - GET /healthz — liveness; rails mode (mock/sandbox), journal length. - GET /api/catalog?q=... — product search (Channel3 live offers; labeled samples offline). - POST /api/mandates — grant a signed mandate (principal, agent, purpose, limits). Add `recurrence: {frequency: "monthly"|"weekly", max_per_window: "40.00"}` for a standing authority ("up to $40/month"): windows repeat until windowEnd, each with its own budget. - GET /api/mandates, /api/mandates/:id — inspect mandates incl. captured spend. - POST /api/mandates/:id/revoke — revoke immediately (journalled). - POST /api/agent/purchase — THE agent boundary: receipt or signed refusal. - POST /api/agent/purchase/resume — complete a wallet purchase after buyer approval. - GET /api/receipts?mandate_id= — your receipts and refusals. - GET /api/verify/receipts/:id, /api/verify/denials/:id — full verification report (public, no account). - GET /api/journal/verify — chain integrity. /api/journal/events — raw sealed events. - GET /api/journal/anchor — latest RFC 3161 TSA anchor. POST /api/journal/anchor — anchor now. - GET/POST /api/agent/preferences — the agent's journalled memory of its principal's tastes and standing instructions (save_preference / recall_preferences in the MCP server). - POST /api/journeys, /api/journeys/:id/approve, /api/journeys/:id/run — purchase journeys: plan a basket, approve (a mandate is derived and signed FROM the basket), run every line through the enforcer. Lines added after approval meet the same signed walls. Approve with `{"recurrence":"monthly","months":12}` to sign the basket as a STANDING authority (per-period budget = basket, cap = basket × periods); run a period with `{"cycle":"2026-11"}` — retries within a cycle are idempotent, the next month runs fresh under the same mandate. - GET /api/disputes — buyer disputes with 10-day respond-by deadlines; evidence packs at /api/disputes/:id/evidence-pack (each pack carries a coverage score vs PayPal's required evidence slots and a FIGHT / REFUND_FIRST recommendation). - POST /api/orders/:orderId/fulfillment — journal {carrier, tracking_number}; fills the PROOF_OF_FULFILLMENT slot in every evidence pack for disputes on that order. - POST /api/disputes/:id/fast-lane {"action":"refund"|"fight"} — settle inside the inquiry window (real PayPal refund, no chargeback fee) or get the pack's targeted evidence entries. - POST /api/receipts/:id/dispute-request — the BUYER's contestation: seals the dispute intent (reason, receipt, order id) into the journal the moment it forms, so the buyer enters PayPal's flow carrying signed evidence, not screenshots. ## Behavior rules for agents 1. Call `mandate_status` (GET /api/mandates/:id) before spending. 2. When a purchase comes from a user conversation, ALWAYS pass `intent_text` (the user's verbatim activation words) on POST /api/agent/purchase — it is sealed into the tamper-evident journal and stamped on the receipt. Your seller will thank you in the dispute; your user keeps provable receipts. 3. Attempt purchases only through POST /api/agent/purchase. A refusal is final signed evidence of the principal's decision — never work around it; report it instead. 4. Remember durable preferences via POST /api/agent/preferences (they are journalled; your choices stay auditable). 5. Everything you do is hashed into an append-only chain with signed checkpoints. Verification is public: anyone with an artifact id can re-check every signature at /verify/:id — no account.