Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Agent Payments Protocol

Avatar for Mete Atamel Mete Atamel
November 05, 2026

Agent Payments Protocol

Avatar for Mete Atamel

Mete Atamel

November 05, 2026

More Decks by Mete Atamel

Other Decks in Technology

Transcript

  1. Agent Payments Protocol (AP2) Mete Atamel Developer Advocate @ Google

    @meteatamel atamel.dev speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/ap2
  2. Agent Payments Protocol (AP2) ap2-protocol.org AP2 is an open protocol,

    originated by Google, that secures agent-initiated payment transactions using cryptographically signed mandates and verifiable credentials
  3. Agent Protocols Stack MCP gives agents tools & A2A enables

    agent-to-agent communication UCP standardizes merchant discovery & checkout; AP2 secures payments A2UI & AG-UI bring agents into rich, interactive user interfaces
  4. AP2 vs. UCP UCP is the commerce layer: merchant discovery,

    catalog, cart, and checkout AP2 is the trust & payment layer: cryptographically signed mandates (SD-JWT)
  5. Four Challenges AP2 Solves 1. Authorization & Auditability: Cryptographic proof

    the user gave the agent permission to buy 2. Authenticity of Intent: Ensure the agent’s request reflects true user intent, not prompt injection 3. Agent Errors & Hallucinations: Hard bounds on merchants, SKUs, and max payment amounts 4. Accountability: Non-repudiable cryptographic audit trail for dispute resolution
  6. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

    11. 12. 13. 14. 15. 16. 17. 18. // AP2 v0.2 Mandate & Receipt Credentials (SD-JWT & KB-SD-JWT) { "checkout_domain": { "OpenCheckoutMandate": "mandate.checkout.open.1", // User -> Agent "CheckoutMandate": "mandate.checkout.1", // -> Merchant "CheckoutReceipt": "Signed ES256 JWT (reference = sha256(leaf))" }, "payment_domain": { "OpenPaymentMandate": "mandate.payment.open.1", // User -> Agent "PaymentMandate": "mandate.payment.1", // -> CP / MPP "PaymentReceipt": "Signed ES256 JWT (reference = sha256(leaf))" }, "cryptographic_primitives": { "SD-JWT": "Selective Disclosure JWT signed on Trusted Surface", "KB-SD-JWT": "Key-Binding SD-JWT chaining Agent key via cnf claim", "hash_bind": "SHA-256 base64url digest binding Mandate to Checkout" } }
  7. AP2 Roles • Shopping Agent (SA): AI surface that discovers

    products, builds carts, and chains mandates • Trusted Surface (TS): Secure, non-agentic UI that renders terms and signs with User key • Merchant (M) & Payment Processor (MPP): Verifies CheckoutMandate and settles payment • Credential Provider (CP) & Network (NI): Verifies PaymentMandate and issues tokens
  8. Trust Boundary Agentic (LLM-Driven): Only the Shopping Agent is agentic;

    it holds its own ephemeral signing key (Key_SA) and never touches the user’s private key Non-Agentic (Deterministic): Trusted Surface MUST be non-agentic (renders deterministic templates directly from protocol parameters) Deterministic Verification: All mandate chain & constraint checks MUST execute in deterministic code, never delegated to an LLM
  9. Two Operating Modes • Human Present (Direct): User is present

    at checkout, reviews finalized cart & price on a Trusted Surface, and directly signs Closed Mandates (CheckoutMandate & PaymentMandate) • Human Not Present (Autonomous): User pre-authorizes Open Mandates with constraints (budget, merchants, SKUs) and binds the Agent’s public key (cnf); Agent later chains Closed Mandates (KB-SD-JWT)
  10. Mandate Flow Buy White Shoes (<= $150) from RunFast Store

    1. User signs OpenCheckoutMandate (SD-JWT) 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. open_checkout_token = client.create( payloads=[OpenCheckoutMandate( constraints=[ AllowedMerchants(allowed=[merchant]), LineItems(items=[LineItemRequirements( id="req-running-shoes", quantity=1, acceptable_items=[ MandateItem(id="SKU-SHOE-WHITE", title="White Shoes") ])]), ], cnf={"jwk": agent_pub_dict}, iat=now, exp=now + 3600, )], issuer_key=user_jwk)
  11. Mandate Flow 2. User signs OpenPaymentMandate (SD-JWT) 1. 2. 3.

    4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. open_checkout_hash = compute_sha256_b64url(open_checkout_token) open_payment_token = client.create( payloads=[OpenPaymentMandate( constraints=[ AmountRange(currency="USD", min=0, max=15000), # Max $150.00 AllowedPayees(allowed=[merchant]), PaymentReference( conditional_transaction_id=open_checkout_hash ), ], cnf={"jwk": agent_pub_dict}, # Bind Shopping Agent public key iat=now, exp=now + 3600, )], issuer_key=user_jwk, )
  12. Mandate Flow 3. Merchant creates signed UCP Checkout JWT 1.

    2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. checkout = Checkout( id="chk_98765", merchant=merchant, status=Status.ready_for_complete, currency="USD", totals=[Total(type="total", amount=12000)], line_items=[LineItem( id="li_1", quantity=1, totals=[Total(type="total", amount=12000)], item=Item(id="SKU-SHOE-WHITE", title="White Shoes", price=12000), )], links=[Link(type="terms_of_service", url="https://runfast.io/tos")], ) checkout_jwt = create_jwt( header={"alg": "ES256", "typ": "JWT", "kid": "merchant-key-1"}, payload=json.loads(checkout.model_dump_json(exclude_none=True)), private_key=merchant_jwk, ) checkout_hash = compute_sha256_b64url(checkout_jwt)
  13. Mandate Flow 4. Open Mandate constraints bound what the agent

    can approve: • checkout.allowed_merchants — Restricts allowed merchants • checkout.line_items — Acceptable SKUs & quantities (max-flow) • payment.amount_range — Min/max transaction amount (in cents) • payment.allowed_payees — Restricts which payees receive funds • payment.budget & agent_recurrence — Caps spend & frequency • payment.reference & execution_date — Links open checkout & dates
  14. Mandate Flow 5. Agent chains Closed Mandates (KB-SD-JWT) 1. 2.

    3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. # Agent binds checkout_hash and signs KB-SD-JWT with agent_jwk checkout_chain_token = client.present( holder_key=agent_jwk, mandate_token=open_checkout_token, payloads=[CheckoutMandate( checkout_jwt=checkout_jwt, checkout_hash=checkout_hash, iat=now, exp=now + 3600)], aud="merchant", nonce="nonce_chk_98765", ) payment_chain_token = client.present( holder_key=agent_jwk, mandate_token=open_payment_token, payloads=[PaymentMandate( transaction_id=checkout_hash, payee=merchant, payment_amount=Amount(amount=12000, currency="USD"), payment_instrument=card_instrument, iat=now, exp=now + 3600)], aud="credential-provider", nonce="nonce_pay_98765", )
  15. Mandate Flow 6. Deterministic verification & Receipts 1. 2. 3.

    4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. # Merchant & CP verify SD-JWT/KB-SD-JWT signatures and constraints chk_payloads = client.verify( token=checkout_chain_token, key_or_provider=lambda _: user_pub_jwk, expected_aud="merchant", expected_nonce="nonce_chk_98765") chk_violations = CheckoutMandateChain.parse(chk_payloads).verify( expected_checkout_hash=checkout_hash, checkout_jwt=checkout_jwt) pay_payloads = client.verify( token=payment_chain_token, key_or_provider=lambda _: user_pub_jwk, expected_aud="credential-provider", expected_nonce="nonce_pay_98765") pay_violations = PaymentMandateChain.parse(pay_payloads).verify( expected_transaction_id=checkout_hash, expected_open_checkout_hash=open_checkout_hash) assert not chk_violations and not pay_violations
  16. Verify & Enforce AP2 Mandates 1. Verify Checkout Mandate Chain

    deterministically 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. from ap2.sdk.checkout_mandate_chain import CheckoutMandateChain from ap2.sdk.mandate import MandateClient client = MandateClient() checkout_payloads = client.verify( token=checkout_chain_token, key_or_provider=lambda _tok: user_pub_jwk, expected_aud="merchant", expected_nonce="nonce_chk_98765", ) checkout_chain = CheckoutMandateChain.parse(checkout_payloads) violations = checkout_chain.verify( expected_checkout_hash=checkout_hash, checkout_jwt=checkout_jwt) assert not violations # 0 constraint violations
  17. Verify & Enforce AP2 Mandates 2. Verify Payment Mandate Chain

    deterministically 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. from ap2.sdk.payment_mandate_chain import PaymentMandateChain payment_payloads = client.verify( token=payment_chain_token, key_or_provider=lambda _tok: user_pub_jwk, expected_aud="credential-provider", expected_nonce="nonce_pay_98765", ) payment_chain = PaymentMandateChain.parse(payment_payloads) violations = payment_chain.verify( expected_transaction_id=checkout_hash, expected_open_checkout_hash=open_checkout_hash, ) assert not violations # Amount $120 <= $150 & PaymentReference valid
  18. Verify & Enforce AP2 Mandates 3. Issue & verify cryptographic

    Receipts 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. from ap2.sdk.receipt_wrapper import ReceiptClient receipt_client = ReceiptClient() closed_leaf = client.get_closed_mandate_jwt(checkout_chain_token) checkout_ref = compute_sha256_b64url(closed_leaf) chk_receipt = receipt_client.create_checkout_receipt( merchant=merchant.website, reference=checkout_ref, order_id="ord_1001") chk_receipt_jwt = create_jwt( header={"alg": "ES256", "typ": "JWT"}, payload=json.loads(chk_receipt.model_dump_json(exclude_none=True)), private_key=merchant_jwk) res = receipt_client.verify_receipt( chk_receipt_jwt, merchant_pub_jwk, lambda r: r == checkout_ref, False)
  19. Verify & Enforce AP2 Mandates 4. Guardrail check: reject out-of-budget

    purchase 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. # Agent attempts to pay $250.00 against $150.00 OpenPaymentMandate over_budget_token = client.present( holder_key=agent_jwk, mandate_token=open_payment_token, payloads=[PaymentMandate( transaction_id="tx_over_budget", payee=merchant, payment_amount=Amount(amount=25000, currency="USD"), # $250.00! payment_instrument=card_instrument, iat=now, exp=now + 3600)], aud="credential-provider", nonce="nonce_over_budget", ) payloads = client.verify( over_budget_token, lambda _: user_pub_jwk, expected_aud="credential-provider", expected_nonce="nonce_over_budget") violations = PaymentMandateChain.parse(payloads).verify( "tx_over_budget", expected_open_checkout_hash=open_checkout_hash) # -> Violations: ['Amount 25000 exceeds maximum 15000']
  20. AP2 Hello World & ADK Scenarios $ uv run python

    mandate_demo.py === 1. Generating Keys for User (Trusted Surface), Agent, and Merchant === === 2. User Signs Open Mandates on Trusted Surface (SD-JWT) === Intent: 'Buy white running shoes (SKU-SHOE-WHITE) from RunFast Store (<= $150)' === 3. Merchant Creates Signed UCP Checkout ($120.00) === Merchant Checkout JWT Hash: L23UciCkCgTu8fZgvU8o7awhJ6XqrYbfibTOjWrU9C0 === 4. Shopping Agent Signs Closed Mandates (KB-SD-JWT Delegation Chain) === Agent appended closed KB-SD-JWT hop to both open mandates. === 5. Deterministic Verification & Receipt Issuance === [Merchant] Checkout Mandate Chain verified! Constraints satisfied (0 violations). [Payment Processor] Payment Mandate Chain verified! Amount $120.00 <= $150.00 max. [Shopping Agent] Checkout & Payment Receipts verified: {'verified': True} === 6. Guardrail Demo: Agent Tries to Exceed Budget ($250.00 > $150.00) === [Payment Processor] Rejected! Violations: ['Amount 25000 exceeds maximum 15000']
  21. Thank you! Mete Atamel Developer Advocate at Google @meteatamel atamel.dev

    speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/ap2