Agent Payments Protocol (AP2)
Add AGP governance to Agent Payments Protocol (AP2) flows. Replace manual allowlists with dynamic policy and feed your dispute resolution chain with cryptographic evidence.
AP2 defines what an agent can pay for. AGP governs whether it should. Map AP2's three VDC types directly to AGP capability tokens and action envelopes — and replace V0.1's static allowlists with dynamic, policy-evaluated authorisation.
What is AP2?
Agent Payments Protocol (AP2) — released February 2026, led by Google with 60+ supporters including PayPal, Mastercard, Visa, and Adyen — defines three Verified Delegated Credentials (VDCs): IntentMandate (standing authorisation, human-not-present), CartMandate (human-present execution), and PaymentMandate (payment instrument delegation). AGP provides the governance layer those VDCs are missing.
The problem with AP2 V0.1 alone
AP2 V0.1 trusts agents via static allowlists — you pre-register agent IDs at the payment processor. That works in a lab. In production it means:
No per-action authorisation
Once an agent is on the allowlist it can execute any CartMandate for any amount. There is no mechanism to say "only laptops, under £2,000, for the London office."
No runtime revocation
Removing an agent from the allowlist requires a manual update propagated to every processor integration. A compromised agent keeps running until someone notices.
No audit chain
AP2 records that a payment happened. It does not record the policy that permitted it, who approved the IntentMandate, or the reasoning chain that led to the CartMandate. Dispute resolution has no evidence.
Concept mapping: AP2 → AGP
| AP2 concept | AGP equivalent | Notes |
|---|---|---|
IntentMandate | Capability token (standing) | Issued once by the authorising principal; scoped to category + spend ceiling; revocable at any time |
CartMandate | Action envelope | One envelope per execution; binds principal, amount, merchant, and timestamp; immutably logged |
PaymentMandate | Capability token (instrument) | Scoped to a payment method; constraints can restrict to specific merchants or currencies |
Static allowlist | Policy engine (dynamic) | Rules evaluated at request time; no re-registration needed when policy changes |
Processor trust assertion | Signed action envelope | Processor verifies the AGP signature rather than a list; works across all enrolled processors |
Dispute evidence | Immutable ledger + envelope chain | AGP provides the full reasoning chain: capability → decision → action → outcome |
Architecture
User / Authorising principal
│
│ issues IntentMandate (once)
▼
┌─────────────────────────────────┐
│ AGP Registry │
│ capability token (IntentMandate)│
│ constraints: { │
│ category: "office-supplies", │
│ max_amount: 2000, │
│ currency: "GBP" │
│ } │
└──────────────┬──────────────────┘
│
│ CartMandate execution request
▼
┌──────────────────────────────────────┐
│ AGP Decision Engine │
│ • validates capability token │
│ • checks item category + amount │
│ • evaluates policy rules │
│ • calls require_approval if needed │
└──────────────┬───────────────────────┘
│ issues action envelope (CartMandate)
▼
┌──────────────────────────────────────┐
│ Agent │
│ attaches: X-AGP-Action-ID header │
│ submits AP2 CartMandate to processor│
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Payment Processor │
│ verifies AGP signature (not list) │
│ records action_id for disputes │
└──────────────────────────────────────┘Step 1 — Issue the IntentMandate (capability token)
When a user grants an agent standing purchase authority, create an AGP capability token instead of adding the agent to an AP2 allowlist. The token carries the same information but is dynamically evaluated and instantly revocable.
# Python SDK — issue an IntentMandate-equivalent capability token
import agp
client = agp.Client(base_url="http://localhost:8000")
# Register the agent as a principal
agent = client.registry.register_principal(
principal_id="procurement-agent-01",
display_name="Procurement Agent",
principal_type="agent",
)
# Issue a standing capability token (IntentMandate equivalent)
token = client.registry.issue_capability_token(
principal_id="procurement-agent-01",
capabilities=["payment:execute", "cart:create"],
constraints={
"category": "office-supplies",
"max_amount": 2000,
"currency": "GBP",
"require_human_approval_above": 500,
},
ttl_seconds=86400, # 24 h; refresh daily
)
print(token.token_id) # tok_01J...Step 2 — Execute a CartMandate (action envelope)
Each CartMandate execution must be backed by an AGP action envelope — a signed, immutable record that the specific transaction was authorised at the specific moment. The agent obtains the envelope before submitting the AP2 CartMandate.
import agp
import requests
client = agp.Client(base_url="http://localhost:8000")
# --- AGP: obtain action envelope (CartMandate authorisation) ---
with client.task_session(
principal_id="procurement-agent-01",
task_description="Purchase ergonomic keyboard from OfficeWorld",
requested_outcome="CartMandate executed for £149 keyboard",
risk_level="medium",
metadata={
"merchant_id": "officeworld-uk",
"amount": 14900, # pence
"currency": "GBP",
"category": "office-supplies",
"ap2_vdc_type": "CartMandate",
},
) as session:
result = session.execute()
action_id = result.action_id # act_01J...
# --- AP2: submit CartMandate with AGP evidence ---
cart_mandate_response = requests.post(
"https://payment-processor.example.com/ap2/cart-mandates",
json={
"merchant_id": "officeworld-uk",
"items": [{"sku": "KB-ERGO-01", "quantity": 1, "unit_price": 14900}],
"currency": "GBP",
},
headers={
"Authorization": f"Bearer {agent_ap2_token}",
"X-AGP-Action-ID": action_id, # processor verifies this
"X-AGP-Principal": "procurement-agent-01",
},
)
print(cart_mandate_response.json()["cart_mandate_id"]) # cm_...Step 3 — Processor-side envelope verification
The payment processor verifies the AGP action envelope instead of checking a static allowlist. This works for any AGP-compatible governance server — no per-processor registration required.
# FastAPI middleware — verify AGP action envelope before processing AP2 CartMandate
from fastapi import FastAPI, Request, HTTPException
import httpx
app = FastAPI()
AGP_SERVER = "http://agp-server:8000"
async def verify_agp_envelope(action_id: str, expected: dict) -> dict:
async with httpx.AsyncClient() as c:
r = await c.get(f"{AGP_SERVER}/agp/action-envelopes/{action_id}")
r.raise_for_status()
envelope = r.json()
# Validate envelope fields match CartMandate payload
meta = envelope.get("metadata", {})
if meta.get("merchant_id") != expected["merchant_id"]:
raise HTTPException(403, "merchant_id mismatch")
if int(meta.get("amount", 0)) < expected["amount"]:
raise HTTPException(403, "amount exceeds authorised envelope")
if envelope["status"] != "approved":
raise HTTPException(403, "envelope not approved")
return envelope
@app.post("/ap2/cart-mandates")
async def create_cart_mandate(request: Request):
action_id = request.headers.get("X-AGP-Action-ID")
if not action_id:
raise HTTPException(400, "X-AGP-Action-ID required")
body = await request.json()
total = sum(i["quantity"] * i["unit_price"] for i in body["items"])
envelope = await verify_agp_envelope(action_id, {
"merchant_id": body["merchant_id"],
"amount": total,
})
# Proceed with CartMandate — envelope ID stored for dispute chain
return {
"cart_mandate_id": "cm_01J...",
"agp_action_id": action_id,
"agp_principal": envelope["principal_id"],
"status": "executed",
}High-value transactions — require human approval
For IntentMandate executions above a threshold (e.g., a standing order for recurring software subscriptions that suddenly spikes), AGP can gate on human approval before issuing the action envelope. The agent waits; the approver gets a push notification.
# High-value CartMandate — require_approval pattern
with client.task_session(
principal_id="procurement-agent-01",
task_description="Renew enterprise software licences — £18,500",
requested_outcome="PaymentMandate executed for annual SaaS renewal",
risk_level="high",
metadata={
"amount": 1850000, # pence — above require_human_approval_above threshold
"currency": "GBP",
"ap2_vdc_type": "PaymentMandate",
"vendor": "acme-saas",
},
require_approval=True,
approval_timeout_seconds=300,
) as session:
try:
result = session.execute()
# envelope issued only after human approves
action_id = result.action_id
except agp.ApprovalTimeoutError:
raise RuntimeError("Transaction not approved within 5 minutes")
except agp.ApprovalRejectedError:
raise RuntimeError("Transaction rejected by authorising principal")Revocation — AP2 without AGP vs with AGP
AP2 V0.1 revocation means removing the agent from every allowlist, coordinating across processors, and hoping nothing in flight completes before the change propagates. With AGP, revoke the capability token once — all inflight requests fail at the decision engine before any CartMandate reaches the processor.
# Revoke an agent's payment authority instantly
client.registry.revoke_capability_token(
token_id="tok_01J...",
reason="agent compromised — incident INC-2024-0881",
)
# Any subsequent task_session.execute() for this agent will raise:
# agp.CapabilityRevokedError: tok_01J... revoked at 2026-04-17T14:23:01Z
# No processor update required. No in-flight CartMandate will be issued.Cascade revocation.
If you're using A2A delegation (an orchestrator issues sub-tokens to worker agents), revoking the orchestrator token cascades to all worker tokens automatically. See the A2A integration guide.
Dispute resolution — evidence chain
AP2's dispute process asks: "Did the agent have authority?" With AGP you can prove it.
Every action envelope is immutably logged with: the capability token that permitted it,
the policy rules that evaluated it, any approval events, and the outcome. The processor
stores the X-AGP-Action-ID — disputes resolve by replaying the envelope.
# Retrieve the full evidence chain for a disputed CartMandate
import agp
client = agp.Client(base_url="http://localhost:8000")
# action_id stored by processor at CartMandate execution
evidence = client.ledger.get_envelope_chain("act_01J...")
for event in evidence.events:
print(f"[{event.timestamp}] {event.type}: {event.summary}")
# Output:
# [2026-04-17T09:00:00Z] CAPABILITY_ISSUED: tok_01J... (IntentMandate, £2,000 ceiling)
# [2026-04-17T14:10:32Z] TASK_CREATED: procurement-agent-01 → OfficeWorld £149
# [2026-04-17T14:10:33Z] POLICY_EVALUATED: office-supplies ✓, amount ✓, currency ✓
# [2026-04-17T14:10:33Z] ENVELOPE_ISSUED: act_01J... approved
# [2026-04-17T14:10:34Z] AP2_CART_MANDATE: cm_01J... executed at OfficeWorldLinking AP2 and UCP flows
When a UCP checkout session triggers an AP2 payment, both protocols share the same AGP
action envelope. The commerce-session.json schema links all three:
{
"agp_task_id": "tsk_01J...",
"agp_action_id": "act_01J...",
"ucp_checkout_id": "cs_01J...",
"ap2_cart_mandate_id":"cm_01J...",
"ap2_vdc_type": "CartMandate",
"principal_id": "procurement-agent-01",
"amount_pence": 14900,
"currency": "GBP",
"merchant_id": "officeworld-uk",
"created_at": "2026-04-17T14:10:32Z"
}UCP + AP2.
If you're implementing both UCP checkout sessions and AP2 payment mandates in the same
flow, start with the UCP integration guide — it covers the
checkout session lifecycle and the commerce-session.json schema in full. The AP2
action envelope and UCP checkout session share the same X-AGP-Action-ID.
Implementation checklist
Register agent principals in AGP registry
One registration per agent identity. Principal ID becomes the AP2 agent identifier — no separate AP2 allowlist entry needed.
Issue capability tokens for IntentMandates
For each standing payment authority a user grants, issue an AGP capability token with appropriate constraints (category, max_amount, currency, require_human_approval_above).
Obtain action envelope before every CartMandate
Call task_session.execute() first. Pass the returned action_id as X-AGP-Action-ID
on the AP2 CartMandate request.
Verify the envelope at the processor
Replace allowlist lookup with GET /agp/action-envelopes/{action_id}. Validate
merchant, amount, and status. Store action_id alongside the CartMandate record.
Wire revocation to capability tokens
Incident response: call revoke_capability_token(). All subsequent task sessions for
that agent fail before reaching the processor — no allowlist coordination.
Surface the ledger for dispute resolution
Give your compliance team access to ledger.get_envelope_chain(action_id). The full
chain — IntentMandate issuance → policy evaluation → CartMandate execution — is
available on demand.
AP2 V0.1 compatibility note.
AP2 V0.1 does not define a standard header for governance evidence. The
X-AGP-Action-ID header is an AGP convention. Coordinate with your payment processor
to ensure they store and verify it. As AP2 matures, governance header standardisation
is expected in a future revision.
Next steps
Agent-to-Agent (A2A)
Govern multi-agent workflows with AGP. Sub-token delegation, liability chains, and cascade revocation across orchestrators and worker agents — model and framework agnostic.
Universal Commerce Protocol (UCP)
Add AGP governance to Universal Commerce Protocol workflows. Govern which agents can initiate checkouts, enforce spend limits, and create an immutable audit trail for every agentic commerce transaction.