AP2 MANDATES.

An AP2 mandate is a signed authorization that lets a specific AI agent spend USDC on your behalf — but only up to your defined budget, only in approved categories, and only until the expiry date you set.

What is a Mandate?

Instead of giving an agent your private key — which would let it spend any amount, any time — you sign a mandate. A mandate is a policy document stored in P402 that the router enforces on every request the agent makes.

The mandate defines:

  • Who is authorized: A specific agent DID — no other agent can use this mandate.
  • Budget cap: A maximum USD amount. The router blocks any request that would exceed it.
  • Allowed categories: Optional list of service categories (e.g. "inference", "search"). Unlisted categories are blocked.
  • Expiry: An ISO 8601 timestamp. Expired mandates are automatically rejected.

Intent Mandate

"Allow agent X to spend up to $50 on inference and search."

Use when an agent has broad autonomy within a task category. Good for research agents, coding agents, and data pipeline automation.

Payment Mandate

"Settle this specific service call for $0.05."

Use when an agent is executing a known, bounded task with a pre-agreed price. Auto-provisioned when you create a CDP session with a budget.

DID Format

Mandates use Decentralized Identifiers (DIDs) to identify both the user (grantor) and the agent (grantee). A DID is a string that uniquely identifies an entity without a central authority.

Format Example When to use
did:p402:tenant:{id} did:p402:tenant:t_abc123 Your P402 tenant account (user_did)
did:p402:agent:{name} did:p402:agent:my-research-agent An agent registered in P402 (agent_did)
did:key:z... did:key:zQ3shm... Cryptographic key-based identity for external agents
did:ethr:0x... did:ethr:0xAbc... Ethereum wallet address as identity

Find your tenant DID:

Your tenant DID is did:p402:tenant:{your_tenant_id}. Find your tenant ID at Dashboard → Settings → Account, or in the tenant_id field of any API response.

Auto-Provisioned Mandates (CDP Sessions)

The easiest way to use mandates. When you create a session with wallet_source: "cdp" and an agent_id, P402 automatically creates a payment mandate and links it to the session. Every auto-pay call through this session is enforced against the mandate — no separate API call needed.

bash — create a CDP session with automatic mandate

curl -X POST https://p402.io/api/v2/sessions \
  -H "Authorization: Bearer $P402_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{\n    "wallet_source": "cdp",\n    "agent_id": "my-research-agent",\n    "budget_usd": 10.00,\n    "expires_in_hours": 24\n  }'

json — response includes the auto-created mandate ID

{\n  "session_id": "ses_...",\n  "budget_usd": 10.00,\n  "expires_at": "2026-04-17T12:00:00.000Z",\n  "policies": {\n    "ap2_mandate_id": "mnd_abc123"   // Mandate auto-created and linked\n  },\n  "wallet": {\n    "address": "0x...",\n    "source": "cdp"\n  }\n}

Manual Mandate Creation

For non-CDP sessions or when you need custom constraints — like restricting to specific service categories or using a cryptographic key identity — create a mandate directly.

bash — POST /api/a2a/mandates

curl -X POST https://p402.io/api/a2a/mandates \
  -H "Authorization: Bearer $P402_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{\n    "mandate": {\n      "type": "intent",\n      "user_did": "did:p402:tenant:t_abc123",\n      "agent_did": "did:p402:agent:my-research-agent",\n      "constraints": {\n        "max_amount_usd": 50.00,\n        "allowed_categories": ["inference", "search", "data"],\n        "valid_until": "2026-12-31T23:59:59Z"\n      },\n      "signature": "0x..."\n    }\n  }'

json — response

{\n  "mandate_id": "mnd_xyz789",\n  "status": "active",\n  "type": "intent",\n  "user_did": "did:p402:tenant:t_abc123",\n  "agent_did": "did:p402:agent:my-research-agent",\n  "constraints": {\n    "max_amount_usd": 50.00,\n    "allowed_categories": ["inference", "search", "data"],\n    "valid_until": "2026-12-31T23:59:59Z"\n  },\n  "amount_spent_usd": 0.00,\n  "created_at": "2026-04-16T12:00:00.000Z"\n}

Allowed Categories

The allowed_categories constraint is an allowlist. If you omit it, the agent can spend in any category. If you include it, only the listed categories are permitted — any request categorized differently returns MANDATE_CATEGORY_DENIED.

Category Covers
inference LLM chat completions, text generation, summarization
search Web search, semantic search, vector retrieval
data Database queries, data API calls, structured data retrieval
compute Code execution, sandboxed computation, function calls
media Image generation, audio synthesis, video processing
storage File upload, object storage, IPFS pinning
agents Calls to other A2A agents in the Bazaar marketplace

Mandate Lifecycle

A mandate moves through these states. Status transitions are triggered automatically by the router — you don't need to update them manually.

Status Meaning Agent can spend?
active Mandate is valid and within budget. Yes, up to remaining budget
exhausted amount_spent_usd has reached max_amount_usd. No — create a new mandate
expired Current time is past valid_until. No — create a new mandate
revoked Manually revoked via DELETE /api/a2a/mandates/:id. No — permanent

Check mandate status

bash — GET /api/a2a/mandates/:id

curl https://p402.io/api/a2a/mandates/mnd_xyz789 \
  -H "Authorization: Bearer $P402_API_KEY"

json — response

{\n  "mandate_id": "mnd_xyz789",\n  "status": "active",\n  "type": "intent",\n  "constraints": {\n    "max_amount_usd": 50.00,\n    "allowed_categories": ["inference", "search", "data"],\n    "valid_until": "2026-12-31T23:59:59Z"\n  },\n  "amount_spent_usd": 12.34,    // How much the agent has spent so far\n  "remaining_usd": 37.66,       // Budget remaining\n  "created_at": "2026-04-16T12:00:00.000Z"\n}

How Agents Request Spending

An agent doesn't call the mandates API directly. Instead, it includes its agent_did and the mandate_id in the session or request. P402 verifies the mandate before processing the request and deducts from the budget after settlement.

The verification order is:

  1. Mandate exists
  2. Status is active
  3. valid_until is in the future
  4. amount_spent_usd + request_amount ≤ max_amount_usd
  5. Request category is in allowed_categories (if set)
  6. Signature valid against public_key (if set)

Backwards compatible:

Sessions without a mandate linked in their policies skip mandate verification entirely. Existing integrations that don't use mandates continue to work without any changes.

Revoke a Mandate

Revocation is immediate and permanent. Any in-flight request using this mandate will be rejected. Use this if you lose trust in an agent or need to stop spending immediately.

bash — DELETE /api/a2a/mandates/:id

curl -X DELETE https://p402.io/api/a2a/mandates/mnd_xyz789 \
  -H "Authorization: Bearer $P402_API_KEY"

# Response: 200 OK
{ "mandate_id": "mnd_xyz789", "status": "revoked" }

Error Codes

Code HTTP Action
MANDATE_NOT_FOUND 404 The mandate_id does not exist. Check the ID.
MANDATE_INACTIVE 403 Mandate is exhausted, expired, or revoked. Create a new one.
MANDATE_EXPIRED 403 valid_until has passed. Create a new mandate with a future expiry.
MANDATE_BUDGET_EXCEEDED 403 Request would exceed max_amount_usd. Increase budget or create a new mandate.
MANDATE_CATEGORY_DENIED 403 Request category is not in allowed_categories. Expand the allowlist or change the request.
MANDATE_SIGNATURE_INVALID 401 EIP-712 signature does not verify against public_key. Re-sign.