AP2Verifiable intent, enforced on PayPal today

Agent payments, held to what your user signed.

Your user sets the bounds once, with a passkey. AgentGuard checks every payment an agent proposes against them before PayPal moves money, and issues a receipt that holds up in a dispute.

WebAuthn / Ed25519 / RFC 3161 timestamps / PayPal Orders v2

Mandate signed by passkeymdt_f1a6…cedf · ES256 · expires in 2h
  1. +0.0sAgent proposes $10.80, over your $5 ask-me lineheld
  2. +1.9sYou approve with your passkey
  3. +6.3sCaptured on PayPal · 3XR9…751R
VALID poi_2b2b…dba6 · 2 timestamps · anchored Verify
How it works

The agent proposes. The mandate decides.

Agents never hold PayPal credentials. Every payment goes through three steps, and money only moves after the last one.

01 · reserve

Reserve against a signed mandate

A deterministic policy engine checks payee, SKU, category, quantity, per-transaction and total limits against a mandate the user signed with their passkey. Budget is reserved atomically, so parallel agents cannot double-spend it.

propose_payment → reserved | needs_human | rejected

02 · verify

Verify with PayPal, and with the user

AgentGuard authorizes on the user's vaulted wallet, then reads PayPal's own view of amount, currency and payee. Anything above the user's threshold waits for a passkey approval bound to that exact order.

intent: AUTHORIZE · payee + amount re-checked

03 · capture

Capture, or void, and prove it

Captures run only while the mandate is still active, checked again under lock. Mismatches and revocations void instead. Each capture yields a Proof-of-Intent receipt with a hash-chained audit entry.

captured → receipt + RFC 3161 + anchor

Proof-of-Intent receipt

Evidence that holds up without trusting us.

The receipt bundles the user's passkey signature, the order PayPal reported, the approval and the capture. The issuer signs it, two independent timestamp authorities stamp it, and the audit log is anchored.

  • Verifies offline with the open-source @agentguard-dev/intent-verifier, or in your browser.
  • Retired issuer keys stay published, so old receipts keep verifying after rotation.
  • Submitted as seller evidence on a live PayPal sandbox dispute, which PayPal accepted for review. No outcome is claimed.

poi_2b2b34d8c4bfa2e098cadba6

SANDBOX
Issuer
agentguard-orcin.vercel.app
Mandate
mdt_f1a6ef71-d690-48be-a567-fe5de328cedf
Purpose
Buy API credits; ask me above 5 USD
PayPal order
8FF97705JB819194D
Authorization
1GV57061K52536844
Capture
3XR94817KX253751R · COMPLETED
Amount
10.80 USD
Approval
apr_01139ea1… · passkey
Timestamps
freetsa.org · sectigo
Anchor root
dc2022104f8eb5f3be03a833d114845820367f4bf3021762437d8987e0d07d57
AP2 compatibility

AP2 describes intent. AgentGuard enforces it on PayPal.

Every receipt carries an ap2 block: the same purchase as Agent Payments Protocol mandates, using AP2's schemas pinned to commit e1ea56d. The verifier re-derives them from the signed mandate.

AP2 roleAP2 objectBuilt fromFidelity
Intentopen_checkout_mandate
open_payment_mandate
Passkey-signed mandate: payees, SKUs, limits, windowexact categories and blocklists stay AgentGuard-only
Cartcheckout_mandateThe merchant-created PayPal order AgentGuard read and verifiedshape exact checkout_jwt is unsigned (alg none)
Paymentpayment_mandateDecision plus PayPal authorizationexact vault token never included
User authorityuser_authorizationWebAuthn assertion over sha256 of the AgentGuard mandatediffers not SD-JWT; same authority, different signed bytes
Security model

What we guarantee, and where it stops.

The server holds no key that can authorize spending. AI risk analysis is advisory: it can escalate a payment to a human, never approve a failing one.

Enforced

  • Mandates, revocations and approvals are signed by the user's passkey. The server re-verifies them on every proposal, approval and capture.
  • Approvals are single-use and bound to the exact proposal hash, PayPal order, amount and a five-minute nonce.
  • Payee allowlists are checked against the payee PayPal reports, not only what the agent claims.
  • Idempotency keys and PayPal-Request-Id on every money call. Recovery never captures against an inactive mandate.
  • Every decision lands in a hash-chained, append-only audit log, anchored with RFC 3161 timestamps.

Honest limits

  • The first passkey enrollment is bootstrapped by whoever holds the admin token. Deliver links over your own authenticated channel.
  • Admin recovery is time-locked for 72 hours and announced, so a takeover is visible as RECOVERY in receipts, not prevented.
  • An anchor proves history as of its time. Detecting a forked history needs someone outside to keep copies of the anchors.
  • A leaked issuer key stays valid for what it signed before retirement. Rotate promptly, together with the admin token.
  • AP2 objects are plain JSON claim sets, not SD-JWT, and the cart JWT is unsigned. Merchant authenticity comes from PayPal's API.
Quickstart

Install, run, verify in about five minutes.

Two packages on npm: a zero-dependency receipt verifier for merchants, and a guard for PayPal's Agent Toolkit for agent builders. The gateway runs on Node 22.6+ with a PayPal sandbox app; SQLite locally, Postgres and Vercel for deployment.

npm
# merchants: verify receipts offline (zero dependencies)
npm i @agentguard-dev/intent-verifier
# agent builders: guard PayPal's Agent Toolkit (AI SDK, OpenAI, MCP)
npm i @agentguard-dev/paypal-toolkit @paypal/agent-toolkit