How we shipped AP2 v0.1 in production.
Three mandates, two DIDs, one dedup table, a 400-line verifier. A field report from one of the only production AP2 implementations on the open internet — covering the IntentMandate / CartMandate / PaymentMandate chain (with the IntentMandate and PaymentMandate signed by the payer and the CartMandate signed by the merchant), did:web key discovery via /.well-known/did.json, the ap2_mandates(account_id, mandate_hash) replay table keyed by sha256(jws), AES-256-GCM-sealed private JWKs in the signing_keys table, ES256 / P-256 across the board, and eight gaps the AP2 v0.1 spec leaves to the implementer including FX snapshotting, partial settlement, did:web SSRF hardening, optional kid headers, and the role-confusion footgun that breaks most third-party implementations.
- Three mandates, two signers — IntentMandate and PaymentMandate are issued by the payer DID, CartMandate is issued by the merchant DID. Mixing them up is the most common implementation error.
- did:web for key discovery — resolve did:web:example.com to https://example.com/.well-known/did.json, inline or JWKS service entry. No certificate authority, no key-ceremony quorum.
- Replay protection — sha256(jws) into ap2_mandates with UNIQUE (account_id, mandate_hash). Separate Idempotency-Key handling in middleware.
- Stack — Express 5 + TypeScript 5.9 + jose for JWS + Drizzle ORM + Postgres + AES-256-GCM via lib/crypto.ts + ES256 / P-256.
- Endpoint — POST /ap2/v1/payment for inbound payments; /.well-known/did.json, /.well-known/ap2-keys.json, /.well-known/jwks.json for key discovery.
- Eight spec gaps the implementer has to fill in — exp seconds vs ms, merchant-signs-cart role confusion, parent-by-inlined-JWS not by id, cross-currency FX (we reject rather than convert), optional kid, did:web SSRF hardening, partial settlement undefined, and the Go reference's central-signing-service assumption.
Frequently asked questions
- Is AP2 a payment rail or an authorization protocol?
- Authorization only. AP2 produces a signed mandate-chain that proves a payer authorized a specific cart at a specific merchant up to a specific cap. It does not move money. You still need a settlement layer underneath — cards, ACH, USDC, whatever — to actually charge the buyer. At AgentWallet we settle via Payouts.com's 17 fiat rails and Coinbase CDP for USDC on Base.
- How is AP2 different from x402 or ACP?
- AP2 is about WHO is allowed to spend, and HOW MUCH. x402 is about HOW the API server tells the agent the price (HTTP 402 Payment Required + USDC on Base). ACP (Stripe's Agentic Commerce Protocol) is about WHAT credential the agent presents at a merchant — a Shared Payment Token bound to one cart, one merchant. They compose: AP2 mandate authorizes the spend, ACP SPT presents the card credential, x402 handles the API micro-payment leg. AgentWallet implements all three in production.
- Do I need to be on Google Cloud to use AP2?
- No. AP2 is an open spec. The reference implementation Google published is in Go and assumes a particular signing-service architecture, but the protocol itself is just signed JWS objects keyed by did:web. We implemented it in TypeScript on Node 24 with the jose library. The full mandate-chain verification path is roughly 400 lines of code.
- Who else has shipped AP2 in production?
- As of May 2026, we're aware of AgentWallet, Stripe (limited pilots with Link), and Google's own first-party demo. Cobo wrote the canonical public explainer for AP2 but does not implement the protocol — they're a custody layer that holds keys agents use to sign. Crossmint, Ramp, Circle, and Coinbase Agentic Wallets have no AP2 implementation as of writing.
- What's the production endpoint?
- POST /ap2/v1/payment, taking a PaymentMandate JWS in the body. The handler verifies the full chain, deduplicates against ap2_mandates(account_id, mandate_hash), persists the receipt, and returns a verified-mandate handle. Settlement on the underlying rail (card / ACH / USDC) is a separate call against the same mandate handle. Public key discovery is at /.well-known/did.json, /.well-known/ap2-keys.json, and /.well-known/jwks.json.
- What's AgentWallet's AP2 implementation stack?
- Express 5 + TypeScript 5.9, jose for JWS (compactVerify / importJWK / SignJWT), Drizzle ORM + Postgres for the signing_keys and ap2_mandates tables, AES-256-GCM-sealed private JWKs in the column sealed_private_jwk, ES256 / P-256 across the board, WebAuthn-bound principals at the human end, did:web resolution via did:web:agentwallet.ai and per-agent subdomains.