Integration Guide

Agent Payments Protocol (AP2) + AGP

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:

1
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."
2
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.
3
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 OfficeWorld

Linking 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

1
Register agent principals in AGP registry
One registration per agent identity. Principal ID becomes the AP2 agent identifier — no separate AP2 allowlist entry needed.
2
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).
3
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.
4
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.
5
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.
6
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

Integration

UCP — Universal Commerce Protocol

Govern checkout session creation. The complement to AP2's payment execution layer.

Integration

A2A — Agent-to-Agent

Delegate payment authority through multi-agent chains with cascade revocation.

Integration

MCP — Model Context Protocol

Govern tool calls from any MCP-compatible agent runtime.

Reference

AGP Implementation Profile

Full specification — conformance requirements, §17 auth, schema definitions.