The Agent Payments Protocol (AP2) lets an AI agent buy something on a user's behalf while every other party can check, from signed evidence, that the user really authorized that purchase. This article is the platform view. It covers where AP2 sits in a commerce stack, which services you build around it, the deployment models the documentation describes, and how to run it: keys, mandate lifecycle, evidence and failure handling.
A version note first, because AP2 is young and its vocabulary has already changed. This article follows the AP2 v0.2 documentation at ap2-protocol.org, as read on 2026-10-05. That version defines Checkout and Payment Mandates, each open or closed, encoded as SD-JWTs. An earlier version of this page described generic payment intents, rail selection and agent-negotiated refunds; none of that is AP2, which authorizes and evidences a purchase and leaves money movement to other systems. Pin the version you implement and re-read the docs before you ship.
Where AP2 sits in the stack
AP2 is easiest to understand as one layer in a stack. Each layer owns a distinct question, and most design mistakes come from asking a layer to answer someone else's question:
| Layer | Owns | Examples |
|---|---|---|
| Agent transport | how agents and services exchange messages | A2A, MCP tool calls, a UCP endpoint |
| AP2 | proof that the user authorized this checkout and this payment | mandates, verification rules, receipts |
| Commerce protocol | what is being bought and the order's life | catalog, checkout object, capture, refunds |
| Payment rail | moving money and its dispute rules | card networks and issuers, bank transfers |
The documentation describes AP2 as a security layer that sits inside commerce protocols, designed for compatibility with the Universal Commerce Protocol (UCP), and usable as an extension of A2A or alongside MCP. It is deliberately agnostic about the checkout object's contents, the payment instrument and dispute procedures. In practice that means your existing checkout, order and refund code stays. AP2 adds a verification step in front of it and an evidence trail behind it.
The primitives in brief
The protocol rests on a few primitives, covered in depth in the AP2 roles and mandates article and the SD-JWT tokens article. In summary: a Checkout Mandate authorizes what is bought and a Payment Mandate authorizes how it is paid. A closed mandate covers one finalized checkout; an open mandate records constraints, such as allowed merchants or an amount range, within which an agent may later create closed mandates itself. Both closed mandates carry the hash of the merchant-signed checkout, which binds the payment to exactly one basket. Each mandate names its schema in a vct claim such as mandate.payment.1, and the spec requires verifiers to match that string exactly, version suffix included. Verifiers always receive closed mandates, whichever mode produced them.
Five roles take part. The Shopping Agent builds the checkout. The Trusted Surface obtains consent and signs. The Merchant offers and completes the checkout. The Credential Provider supplies payment credentials, and the Merchant Payment Processor submits the payment. One company may play several roles. The Trusted Surface must be non-agentic.
The services you build
Turn the roles into services and the architecture becomes concrete. Whichever roles you play, you will build some of the following.
The Trusted Surface. This is the screen where the user sees what they are approving and signs it. The implementation guidance lists several forms: a component of the agent's own app, a standalone wallet, an issuer's app, or a platform or browser user agent. Whichever you choose, the rendering and signing path must be deterministic code. A model may draft the checkout, but it must never decide what the user is shown or what gets signed. If it did, a prompt-injected product page could change both. Render from the same bytes you sign, and keep the user's key on the device, ideally hardware-backed.
The Shopping Agent and its signing tool. In autonomous mode the agent signs closed mandates with its own key, which the user bound into the open mandates through a cnf claim. The documentation suggests tool calling, where deterministic code verifies the closed mandate before it is signed. That tool is the most security-critical code an agent operator writes; the next section shows one.
Verifiers at the Merchant, Credential Provider and Processor. Each checks the mandate it relies on: signature, exact vct, expiry, key binding, constraints and the checkout hash. Put the verifier in front of order creation or credential issuance, never after it. Fail closed: if the verifier is down, the purchase does not proceed.
Deployment models
The implementation considerations describe several ways to arrange the roles. Your choice decides who runs which verifier:
| Model | Shape | What changes |
|---|---|---|
| Merchant with a UCP endpoint | the agent talks to the merchant's commerce API directly | the merchant backend verifies the Checkout Mandate before creating the order |
| Merchant agent over A2A | a merchant-side agent talks to the shopping agent, then to the merchant backend | the merchant agent is agentic, so verification must happen in the deterministic backend, not in the merchant agent |
| Combined merchant and processor | one entity is both Merchant and MPP | one service can check the Checkout Mandate, the Payment Mandate and their binding together |
| Delegated verification | the merchant hands the Checkout Mandate to a technology provider | the merchant proceeds only if the provider's verification passes; the provider's availability becomes yours |
The merchant-agent model deserves care. An LLM on the merchant side can negotiate, answer questions and build carts, but anything it says about a mandate is a claim. The backend must re-verify the mandate itself and must sign the checkout from its own cart state, not from text the merchant agent produced. For how the parties' keys map onto these services, see AP2 multiparty architecture.
The agent's signing tool
In autonomous mode the agent turns an open mandate into a closed one and signs it. The model chooses what to buy; a deterministic tool decides whether the agent key may sign. The sketch below assumes the SD-JWT has already been parsed and its signature verified by a maintained library, and that registry is the lifecycle service described next. The vct strings, cnf, the constraint types payment.amount_range and payment.allowed_payees, and the closed fields are spec names; value shapes and other names are placeholders for your library's parsed claims.
from datetime import datetime, timezone
class Refused(Exception):
pass
def sign_closed_payment_mandate(open_token, open_pm, checkout, amount, payee, instrument,
registry, agent_key):
"""Deterministic gate: the LLM may call this tool, but cannot bypass its checks."""
now = datetime.now(timezone.utc).timestamp()
rec = registry.get(sha256_b64url(open_token)) # our own record, never the agent's word
if rec is None or rec.state != "active":
raise Refused("open mandate is not active")
if open_pm["vct"] != "mandate.payment.open.1": # exact match, version included
raise Refused("unexpected vct")
if now >= min(open_pm["exp"], rec.local_expiry): # our duration limit may be shorter
raise Refused("open mandate expired")
if jwk_thumbprint(open_pm["cnf"]["jwk"]) != agent_key.thumbprint:
raise Refused("open mandate is bound to a different agent key")
c = constraints_by_type(open_pm) # placeholder: type -> parsed value
if not c["payment.amount_range"].contains(amount):
raise Refused("amount outside the approved range")
if payee not in c["payment.allowed_payees"]:
raise Refused("payee not approved")
if checkout.payee != payee or checkout.total != amount:
raise Refused("mandate does not match the merchant-signed checkout")
reservation = registry.reserve(rec, amount) # atomic budget check-and-hold
try:
closed = {
"vct": "mandate.payment.1",
"transaction_id": checkout.hash, # binds payment to this exact basket
"payee": payee,
"payment_amount": amount,
"payment_instrument": instrument,
}
token = agent_key.sign_sd_jwt(closed) # key lives in a KMS/HSM, not in the prompt
except Exception:
registry.release(reservation)
raise
registry.record_use(rec, reservation, checkout.hash)
return tokenThree properties make this safe. The tool reads the mandate's state from its own registry, so a revoked or locally expired mandate cannot be revived by the model. The budget is reserved atomically before signing, so two concurrent purchases cannot both spend the last of it. And the private key is reachable only through this function, so a prompt injection can at worst propose a purchase that the checks then refuse. Cumulative limits enforced here protect the user only if the downstream Credential Provider enforces them too; treat the agent's own check as the first line, not the only one.
Managing the mandate lifecycle
The implementation guidance says the Shopping Agent should provide a way to manage active mandates, along with the tasks they are used in. It also stresses limiting how long mandates stay active and notifying the user even when the agent acts autonomously. Build that as a small service with an explicit state machine: active to exhausted, expired or revoked.
- Duration. Set a local expiry no later than the mandate's own
exp, and shorter for high-value or open-ended mandates. A weekly grocery mandate that silently lives for a year is a liability. - Notification. Notify the user of every autonomous purchase, from a deterministic service, with the merchant, amount and a link to stop the mandate.
- Revocation. The v0.2 pages I read do not define a revocation message. In practice, revoking means the agent's registry refuses further signing, and any verifier that keeps its own state, typically the Credential Provider, refuses further closed mandates under that parent. Check whether a newer version standardises this before you build your own.
Evidence and receipts
For dispute resolution, the documentation says to store the mandates' SD-JWTs with their disclosures, in compact serialization. Store the exact strings you received or produced. A re-serialized copy will not hash to the same value and cannot prove anything later. Alongside them, keep the merchant-signed checkout JWT, the receipts and your verifier's decision.
There are two receipts. The merchant returns a Checkout Receipt whose reference must match the hash of the closed Checkout Mandate. The Merchant Payment Processor produces a Payment Receipt that goes to the Shopping Agent, the Credential Provider and the network. A dispute then becomes a recomputation: hash the stored checkout, compare it with both mandates and the receipts, and check the signatures. AP2 leaves retention periods and dispute procedures to the rail and to regulation, so set retention with your compliance team. Keep personal data out of general logs: record hashes, vct values, key identifiers and decisions there, and keep full tokens in the access-controlled evidence store. The AP2 dispute architecture article walks through the evidence pipeline.
Worked example: one flight, end to end
Follow one human-present purchase through the services. A user asks their agent to book a business-class seat to Munich for next Tuesday.
- The Shopping Agent searches the airline's commerce API and builds a cart. The airline's backend, acting as Merchant, signs a checkout JWT for one seat at 1,800 euros.
- The agent asks the Credential Provider, here the user's wallet, for eligible instruments and proposes one.
- The Trusted Surface, the wallet app, renders the flight, price and instrument from the signed checkout. The user authenticates and approves, and the surface signs a closed Checkout Mandate and a closed Payment Mandate, both carrying the checkout hash.
- The Credential Provider verifies the Payment Mandate and issues a payment credential scoped to this checkout, so the agent never holds a reusable card number. See payment tokenization for agents.
- The airline's backend verifies the Checkout Mandate against its own cart, then starts the payment through its processor, which verifies the Payment Mandate and the binding and submits to the card network. Any issuer step-up, such as 3-D Secure, happens on the rail as it would today.
- The processor issues a Payment Receipt and the airline a Checkout Receipt. Every party writes its evidence.
The next day the user realises the date is wrong. The refund is a normal airline refund under the fare rules, processed by the commerce protocol and the rail. AP2 does not run it. Its contribution is evidence: the stored mandates show exactly which flight the user approved, which settles any argument about whether the agent bought the wrong thing.
Failure modes and trade-offs
The failures to design for, and the response to each:
- Verifier outage. Fail closed and tell the agent the purchase is pending. Never fall back to accepting unverified mandates.
- Version drift. A new
vctsuffix will be rejected by exact matching. Support old and new versions side by side during a migration, and alert on rejection rates byvct. - Agent key compromise. Rotate the key, mark every open mandate bound to it as revoked in the registry, and ask users to re-approve. Keep agent keys in a KMS or HSM, one per agent deployment.
- Cart changes after signing. If the price or stock changes, the merchant should reject and issue a new checkout, not quietly re-price. The user then approves again.
- Retries. Network timeouts make it unclear whether a payment happened. Make submission idempotent on the checkout hash; see AP2 payment flow.
The main trade-off: per-purchase approval is safest but adds friction, while open mandates remove friction and move risk into constraint design and budget tracking. Choose per use case.
What to do next
If you are adopting AP2, work through this list:
- Decide which roles you play, and pick a deployment model from the table above.
- Pin the AP2 version, record the exact
vctstrings you accept, and subscribe to spec changes. - Build or choose a non-agentic Trusted Surface that renders from the bytes it signs.
- Put a deterministic verifier in front of every order or credential issuance, failing closed.
- If you run agents autonomously, put the agent key behind a signing tool like the one above, backed by a KMS.
- Stand up the mandate lifecycle service with duration limits, notifications and revocation.
- Store compact SD-JWTs, disclosures, checkout JWTs and receipts in an evidence store with an agreed retention period.
- Run a game day: revoke a mandate mid-task, rotate an agent key and take a verifier offline.