Intent Mandates
An Intent Mandate is a permission request from an agent to make purchases within specified constraints. It's the first step in the payment flow and determines whether the agent can proceed.
Overview
When an agent wants to shop, it first requests permission by creating an Intent Mandate:
- Agent submits request — "I want to spend up to $50 at amazon.com"
- Policy check — AutopayOS evaluates against spending rules
- Decision — Either approved (returns Intent VC) or denied (returns reason codes)
Create an Intent
<tabs> <tab title="TypeScript">const permission = await client.requestPermission({
agentDid: 'did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK',
principalDid: 'did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH',
amount: 100.00,
currency: 'USD',
merchantDomain: 'amazon.com',
merchantMcc: '5411', // Optional: Grocery stores
});
if (permission.allowed) {
console.log('Approved! Intent VC:', permission.intentVc);
} else {
console.log('Denied:', permission.reasonCodes);
}curl https://api.autopayos.com/ap2/intents \
-H "Authorization: Bearer $AUTOPAYOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentDid": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"principalDid": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
"request": {
"maxAmount": 100.00,
"currency": "USD",
"vendorHints": {
"domain": "amazon.com",
"mcc": "5411"
}
}
}'Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agentDid | string | Yes | DID of the agent making the request |
principalDid | string | Yes | DID of the human who authorized the agent |
amount | number | Yes | Maximum amount for the purchase |
currency | string | Yes | ISO 4217 currency code (USD, EUR, GBP) |
merchantDomain | string | No | Specific merchant domain |
merchantMcc | string | No | Merchant Category Code |
idempotencyKey | string | No | Prevent duplicate requests |
Response
Approved
{
"allowed": true,
"intentVc": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential", "IntentMandate"],
"issuer": "did:key:z6MkAutoPayOSIssuer...",
"issuanceDate": "2025-12-17T10:00:00Z",
"expirationDate": "2025-12-17T11:00:00Z",
"credentialSubject": {
"mandateId": "intent_abc123xyz",
"agentDid": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"principalDid": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
"maxAmount": 100.00,
"currency": "USD",
"merchantDomain": "amazon.com"
},
"proof": {
"type": "Ed25519Signature2020",
"created": "2025-12-17T10:00:00Z",
"verificationMethod": "did:key:z6MkAutoPayOSIssuer...#key-1",
"jws": "eyJhbGciOiJFZERTQSJ9..."
}
},
"attestation": {
"policyId": "pol_abc123",
"policyVersion": 3,
"policyHash": "sha256:def456789...",
"evaluatedAt": "2025-12-17T10:00:00Z"
},
"hpRequired": false
}Denied
{
"allowed": false,
"reasonCodes": [
"AMOUNT_OVER_CAP",
"MERCHANT_NOT_ALLOWED"
],
"details": {
"AMOUNT_OVER_CAP": {
"requested": 100.00,
"cap": 50.00
},
"MERCHANT_NOT_ALLOWED": {
"domain": "casino.com",
"allowed": ["amazon.com", "walmart.com"]
}
}
}Policy checks
When you create an intent, AutopayOS evaluates it against your policy:
| Check | Policy Field | Reason Code |
|---|---|---|
| Amount within cap | spend.amount_cap | AMOUNT_OVER_CAP |
| Currency allowed | spend.currency | CURRENCY_NOT_ALLOWED |
| Merchant on allowlist | merchant.allow_list | MERCHANT_NOT_ALLOWED |
| Merchant not on denylist | merchant.deny_list | MERCHANT_DENIED |
| MCC category allowed | spend.categories | MCC_DENIED |
| Within velocity limits | risk.velocity_limit | VELOCITY_EXCEEDED |
| Time window valid | context.time_window | TIME_WINDOW_VIOLATED |
| Geo restriction | context.geo | GEO_RESTRICTED |
| Agent not revoked | Agent status | AGENT_REVOKED |
Human Presence
If the policy requires Human Presence for this request, the response will indicate:
{
"allowed": true,
"hpRequired": true,
"hpChallenge": {
"challengeId": "hp_xyz789",
"expiresAt": "2025-12-17T10:05:00Z"
}
}The agent must obtain HP proof before proceeding:
if (permission.hpRequired) {
const hpProof = await getHumanPresenceProof(permission.hpChallenge);
const verification = await client.verifyCart({
intentVc: permission.intentVc,
cartVc: cartVc,
hpProof: hpProof, // Include HP proof
});
}See Human Presence guide for implementation details.
Intent lifecycle
| Status | Description | Next States |
|---|---|---|
| PENDING | Intent created, awaiting evaluation | ACTIVE, DENIED |
| ACTIVE | Intent approved and valid | USED, EXPIRED |
| USED | Cart verified with this intent | — |
| EXPIRED | Time limit exceeded | — |
| DENIED | Rejected by policy | — |
| Status | Description |
|---|---|
PENDING | Intent created but not yet evaluated |
ACTIVE | Intent approved and ready to use |
USED | Intent was used for a cart verification |
EXPIRED | Intent exceeded its time limit |
DENIED | Intent was rejected by policy |
Intent expiration
Intents have a limited lifetime to prevent stale permissions:
// Default: 1 hour
const permission = await client.requestPermission({
agentDid: '...',
principalDid: '...',
amount: 100,
currency: 'USD',
expiresIn: 3600, // 1 hour in seconds
});
console.log(permission.intentVc.expirationDate);
// "2025-12-17T11:00:00Z"If an intent expires, the agent must request a new one.
Query intents
List intents
curl "https://api.autopayos.com/ap2/intents?status=ACTIVE&limit=10" \
-H "Authorization: Bearer $AUTOPAYOS_API_KEY"Response:
{
"data": [
{
"mandateId": "intent_abc123",
"status": "ACTIVE",
"agentDid": "did:key:z6Mk...",
"maxAmount": 100.00,
"currency": "USD",
"createdAt": "2025-12-17T10:00:00Z",
"expiresAt": "2025-12-17T11:00:00Z"
}
],
"hasMore": false,
"cursor": null
}Get intent by ID
curl "https://api.autopayos.com/ap2/intents/intent_abc123" \
-H "Authorization: Bearer $AUTOPAYOS_API_KEY"Advanced: Natural language parsing
AutopayOS can parse natural language intent requests:
const result = await client.parseIntent({
text: "Buy groceries from Walmart, up to fifty dollars",
agentDid: 'did:key:z6Mk...',
});
console.log(result);
// {
// amount: 50.00,
// currency: 'USD',
// merchantDomain: 'walmart.com',
// categories: ['groceries']
// }Best practices
1. Request minimum permissions
Only request the amount and scope needed:
// Good: Specific amount and merchant
await client.requestPermission({
amount: 25.00,
merchantDomain: 'walmart.com',
});
// Bad: Overly broad
await client.requestPermission({
amount: 10000.00,
// No merchant restriction
});2. Handle denials gracefully
const permission = await client.requestPermission({ ... });
if (!permission.allowed) {
for (const code of permission.reasonCodes) {
switch (code) {
case 'AMOUNT_OVER_CAP':
// Suggest a lower amount
break;
case 'MERCHANT_NOT_ALLOWED':
// Suggest alternative merchants
break;
case 'VELOCITY_EXCEEDED':
// Wait and retry later
break;
}
}
}3. Use idempotency keys
Prevent duplicate intents from network retries:
const permission = await client.requestPermission({
...params,
idempotencyKey: `intent-${Date.now()}-${agentDid}`,
});Evidence events
Intent operations produce these evidence events:
| Event | Description |
|---|---|
INTENT_ISSUED | Intent was created and approved |
INTENT_DENIED | Intent was rejected by policy |
INTENT_EXPIRED | Intent exceeded time limit |
INTENT_USED | Intent was used for cart verification |
Query the evidence chain:
curl "https://api.autopayos.com/ap2/evidence/search?eventType=INTENT_ISSUED" \
-H "Authorization: Bearer $AUTOPAYOS_API_KEY"Next steps
- Cart verification — Validate shopping carts
- Policy configuration — Define spending rules
- Evidence chain — Query audit trail