Docs/AP2 Mandates

>_ DOCS / REFERENCE

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.

FormatExampleWhen to use
did:p402:tenant:{id}did:p402:tenant:t_abc123Your P402 tenant account (user_did)
did:p402:agent:{name}did:p402:agent:my-research-agentAn 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 '{
    "wallet_source": "cdp",
    "agent_id": "my-research-agent",
    "budget_usd": 10.00,
    "expires_in_hours": 24
  }'
json — response includes the auto-created mandate ID
{
  "session_id": "ses_...",
  "budget_usd": 10.00,
  "expires_at": "2026-04-17T12:00:00.000Z",
  "policies": {
    "ap2_mandate_id": "mnd_abc123"   // Mandate auto-created and linked
  },
  "wallet": {
    "address": "0x...",
    "source": "cdp"
  }
}

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 '{
    "mandate": {
      "type": "intent",
      "user_did": "did:p402:tenant:t_abc123",
      "agent_did": "did:p402:agent:my-research-agent",
      "constraints": {
        "max_amount_usd": 50.00,
        "allowed_categories": ["inference", "search", "data"],
        "valid_until": "2026-12-31T23:59:59Z"
      },
      "signature": "0x..."
    }
  }'
json — response
{
  "mandate_id": "mnd_xyz789",
  "status": "active",
  "type": "intent",
  "user_did": "did:p402:tenant:t_abc123",
  "agent_did": "did:p402:agent:my-research-agent",
  "constraints": {
    "max_amount_usd": 50.00,
    "allowed_categories": ["inference", "search", "data"],
    "valid_until": "2026-12-31T23:59:59Z"
  },
  "amount_spent_usd": 0.00,
  "created_at": "2026-04-16T12:00:00.000Z"
}

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.

CategoryCovers
inferenceLLM chat completions, text generation, summarization
searchWeb search, semantic search, vector retrieval
dataDatabase queries, data API calls, structured data retrieval
computeCode execution, sandboxed computation, function calls
mediaImage generation, audio synthesis, video processing
storageFile upload, object storage, IPFS pinning
agentsCalls 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.

StatusMeaningAgent can spend?
activeMandate is valid and within budget.Yes, up to remaining budget
exhaustedamount_spent_usd has reached max_amount_usd.No — create a new mandate
expiredCurrent time is past valid_until.No — create a new mandate
revokedManually 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
{
  "mandate_id": "mnd_xyz789",
  "status": "active",
  "type": "intent",
  "constraints": {
    "max_amount_usd": 50.00,
    "allowed_categories": ["inference", "search", "data"],
    "valid_until": "2026-12-31T23:59:59Z"
  },
  "amount_spent_usd": 12.34,    // How much the agent has spent so far
  "remaining_usd": 37.66,       // Budget remaining
  "created_at": "2026-04-16T12:00:00.000Z"
}

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
MANDATE_NOT_FOUND
2
Status is active
MANDATE_INACTIVE
3
valid_until is in the future
MANDATE_EXPIRED
4
amount_spent_usd + request_amount ≤ max_amount_usd
MANDATE_BUDGET_EXCEEDED
5
Request category is in allowed_categories (if set)
MANDATE_CATEGORY_DENIED
6
Signature valid against public_key (if set)
MANDATE_SIGNATURE_INVALID

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

CodeHTTPAction
MANDATE_NOT_FOUND404The mandate_id does not exist. Check the ID.
MANDATE_INACTIVE403Mandate is exhausted, expired, or revoked. Create a new one.
MANDATE_EXPIRED403valid_until has passed. Create a new mandate with a future expiry.
MANDATE_BUDGET_EXCEEDED403Request would exceed max_amount_usd. Increase budget or create a new mandate.
MANDATE_CATEGORY_DENIED403Request category is not in allowed_categories. Expand the allowlist or change the request.
MANDATE_SIGNATURE_INVALID401EIP-712 signature does not verify against public_key. Re-sign.