Policies

Policies are the heart of AutopayOS. They define what agents can and cannot do — spending limits, merchant restrictions, velocity controls, time windows, and more. Every intent is evaluated against the active policy before approval.

Overview

Agent RequestPolicy EngineDecision

  • Approved — Request is within policy constraints
  • Denied — Request violates one or more policy rules

Policy structure

A policy is a JSON document with these sections:

JSON
{
  "version": "2.0",
  "spend": {
    "amount_cap": 100.00,
    "currency": "USD",
    "usage": "recurring",
    "categories": ["groceries", "household", "electronics"]
  },
  "merchant": {
    "allow_list": ["amazon.com", "walmart.com", "target.com"],
    "deny_list": ["casino.com", "gambling.com"]
  },
  "vendor": {
    "trust": "moderate"
  },
  "context": {
    "presence": "optional",
    "time_window": {
      "days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
      "hours": { "start": "09:00", "end": "18:00" },
      "timezone": "America/New_York"
    }
  },
  "risk": {
    "velocity_limit": {
      "max_transactions": 10,
      "max_amount": 500.00,
      "time_window": "24h"
    },
    "anomaly_flags": ["unusual_amount", "new_merchant", "rapid_succession"]
  },
  "rails": {
    "allow": ["STRIPE", "VISA_IC"],
    "priorities": ["STRIPE"],
    "fail_over": true
  }
}

Spend controls

Control how much agents can spend.

Amount cap

Maximum amount per transaction:

JSON
{
  "spend": {
    "amount_cap": 100.00,
    "currency": "USD"
  }
}
FieldTypeDescription
amount_capnumberMaximum amount per transaction
currencystringISO 4217 currency code
usagestringsingle (one-time) or recurring

Category restrictions

Limit purchases to specific merchant categories:

JSON
{
  "spend": {
    "categories": ["groceries", "household", "electronics"]
  }
}

Supported categories

CategoryMCC CodesDescription
groceries5411, 5422Grocery stores, meat markets
household5200, 5251Home improvement, hardware
electronics5732, 5734Electronics stores
restaurants5812, 5814Restaurants, fast food
travel4111, 4511Airlines, hotels, car rental
gas5541, 5542Gas stations
healthcare5912, 8011Pharmacies, doctors
entertainment7832, 7922Movies, theaters

MCC code mapping

You can also use MCC codes directly:

JSON
{
  "spend": {
    "mcc_codes": ["5411", "5422", "5912"]
  }
}

Merchant controls

Define which merchants agents can transact with.

Allowlist

Only allow specific merchants:

JSON
{
  "merchant": {
    "allow_list": [
      "amazon.com",
      "walmart.com",
      "*.target.com"
    ]
  }
}

Wildcards (*) are supported for subdomains.

Denylist

Block specific merchants:

JSON
{
  "merchant": {
    "deny_list": [
      "casino.com",
      "gambling.com",
      "*.bet"
    ]
  }
}

Geographic restrictions

Restrict by merchant location:

JSON
{
  "merchant": {
    "geo": {
      "allow_countries": ["US", "CA", "GB"],
      "deny_countries": ["RU", "CN"]
    }
  }
}

Velocity controls

Prevent excessive spending over time.

Transaction limits

JSON
{
  "risk": {
    "velocity_limit": {
      "max_transactions": 10,
      "time_window": "24h"
    }
  }
}

Amount limits

JSON
{
  "risk": {
    "velocity_limit": {
      "max_amount": 500.00,
      "time_window": "24h"
    }
  }
}

Combined limits

JSON
{
  "risk": {
    "velocity_limit": {
      "max_transactions": 10,
      "max_amount": 500.00,
      "time_window": "24h"
    }
  }
}

Time window options

WindowDescription
1hPer hour
24hPer day
7dPer week
30dPer month

Time controls

Restrict when agents can transact.

Business hours

JSON
{
  "context": {
    "time_window": {
      "hours": {
        "start": "09:00",
        "end": "18:00"
      },
      "timezone": "America/New_York"
    }
  }
}

Day restrictions

JSON
{
  "context": {
    "time_window": {
      "days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
      "hours": { "start": "09:00", "end": "17:00" }
    }
  }
}

Human Presence

Control when Human Presence verification is required.

Always require HP

JSON
{
  "context": {
    "presence": "required"
  }
}

HP threshold

JSON
{
  "context": {
    "presence": "optional",
    "presence_threshold": 500.00
  }
}

Transactions over $500 require HP verification.

HP for new merchants

JSON
{
  "context": {
    "presence": "optional",
    "presence_on_new_merchant": true
  }
}

Anomaly detection

Configure automatic anomaly detection.

JSON
{
  "risk": {
    "anomaly_flags": [
      "unusual_amount",
      "new_merchant", 
      "rapid_succession",
      "unusual_time",
      "geo_anomaly"
    ]
  }
}
FlagTrigger
unusual_amountAmount significantly higher than average
new_merchantFirst transaction with this merchant
rapid_successionMultiple transactions in quick succession
unusual_timeTransaction outside normal hours
geo_anomalyMerchant location unexpected

Payment rails

Configure which payment rails to use.

JSON
{
  "rails": {
    "allow": ["STRIPE", "VISA_IC"],
    "priorities": ["STRIPE", "VISA_IC"],
    "fail_over": true,
    "fail_open": false
  }
}
FieldDescription
allowWhich rails are allowed
prioritiesPreferred order
fail_overTry next rail on failure
fail_openAllow if all rails fail (dangerous)

Trust levels

Configure vendor trust levels:

JSON
{
  "vendor": {
    "trust": "moderate"
  }
}
LevelDescription
strictOnly verified merchants, require signatures
moderateVerified preferred, signatures optional
permissiveAccept most merchants

Create a policy

<tabs> <tab title="TypeScript">
TypeScript
const policy = await client.createPolicy({
  name: 'Shopping Agent Policy',
  rules: {
    version: '2.0',
    spend: {
      amount_cap: 100.00,
      currency: 'USD',
      categories: ['groceries', 'household']
    },
    merchant: {
      allow_list: ['amazon.com', 'walmart.com']
    },
    risk: {
      velocity_limit: {
        max_transactions: 10,
        time_window: '24h'
      }
    }
  }
});

console.log('Policy created:', policy.id);
</tab> <tab title="cURL">
Bash
curl https://api.autopayos.com/ap2/admin/policies \
  -H "Authorization: Bearer $AUTOPAYOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Shopping Agent Policy",
    "rules": {
      "version": "2.0",
      "spend": {
        "amount_cap": 100.00,
        "currency": "USD",
        "categories": ["groceries", "household"]
      },
      "merchant": {
        "allow_list": ["amazon.com", "walmart.com"]
      }
    }
  }'
</tab> </tabs>

Update a policy

TypeScript
await client.updatePolicy(policyId, {
  rules: {
    ...existingRules,
    spend: {
      amount_cap: 200.00  // Increase limit
    }
  }
});

Policy versioning

Policies are versioned. Each update creates a new version:

JSON
{
  "id": "pol_abc123",
  "version": 3,
  "hash": "sha256:def456...",
  "createdAt": "2025-12-17T10:00:00Z"
}

When an intent is approved, it's bound to a specific policy version via the Policy Attestation:

JSON
{
  "attestation": {
    "policyId": "pol_abc123",
    "policyVersion": 3,
    "policyHash": "sha256:def456...",
    "evaluatedAt": "2025-12-17T10:00:00Z"
  }
}

Link policy to agent

Assign a policy to a specific agent:

TypeScript
await client.updateAgent(agentDid, {
  linkedPolicyId: policy.id
});

Or use the default policy for all agents:

TypeScript
await client.setDefaultPolicy(policy.id);

Policy evaluation

When an intent is created, the policy engine checks:

CheckPolicy FieldReason Code
Amountspend.amount_capAMOUNT_OVER_CAP
Currencyspend.currencyCURRENCY_NOT_ALLOWED
Categoriesspend.categoriesMCC_DENIED
Merchant allowlistmerchant.allow_listMERCHANT_NOT_ALLOWED
Merchant denylistmerchant.deny_listMERCHANT_DENIED
Velocity countrisk.velocity_limit.max_transactionsVELOCITY_EXCEEDED
Velocity amountrisk.velocity_limit.max_amountSPENDING_LIMIT_EXCEEDED
Time windowcontext.time_windowTIME_WINDOW_VIOLATED
Geographicmerchant.geoGEO_RESTRICTED
Anomalyrisk.anomaly_flagsANOMALY_DETECTED

Test a policy

Simulate policy evaluation without creating an intent:

TypeScript
const result = await client.simulatePolicy({
  policyId: 'pol_abc123',
  request: {
    amount: 150.00,
    currency: 'USD',
    merchantDomain: 'casino.com'
  }
});

console.log(result);
// {
//   allowed: false,
//   reasonCodes: ['AMOUNT_OVER_CAP', 'MERCHANT_DENIED']
// }

Example policies

Conservative (low risk)

JSON
{
  "spend": {
    "amount_cap": 50.00,
    "currency": "USD",
    "categories": ["groceries"]
  },
  "merchant": {
    "allow_list": ["walmart.com", "target.com"]
  },
  "context": {
    "presence": "required"
  },
  "risk": {
    "velocity_limit": {
      "max_transactions": 3,
      "max_amount": 100.00,
      "time_window": "24h"
    }
  }
}

Moderate (balanced)

JSON
{
  "spend": {
    "amount_cap": 200.00,
    "currency": "USD",
    "categories": ["groceries", "household", "electronics"]
  },
  "merchant": {
    "deny_list": ["casino.com", "gambling.com"]
  },
  "context": {
    "presence": "optional",
    "presence_threshold": 100.00
  },
  "risk": {
    "velocity_limit": {
      "max_transactions": 10,
      "max_amount": 500.00,
      "time_window": "24h"
    }
  }
}

Permissive (high trust)

JSON
{
  "spend": {
    "amount_cap": 1000.00,
    "currency": "USD"
  },
  "merchant": {
    "deny_list": ["casino.com"]
  },
  "context": {
    "presence": "optional",
    "presence_threshold": 500.00
  },
  "risk": {
    "velocity_limit": {
      "max_transactions": 50,
      "time_window": "24h"
    }
  }
}

Best practices

1. Start restrictive

Begin with conservative limits and loosen as needed:

JSON
// Start here
{ "spend": { "amount_cap": 50.00 } }

// Increase after trust is established
{ "spend": { "amount_cap": 100.00 } }

2. Use allowlists for high-value

For sensitive operations, use allowlists instead of denylists:

JSON
// Safer: Only allow known merchants
{ "merchant": { "allow_list": ["amazon.com"] } }

// Riskier: Block known bad, allow unknown
{ "merchant": { "deny_list": ["casino.com"] } }

3. Layer controls

Combine multiple controls for defense in depth:

JSON
{
  "spend": { "amount_cap": 100.00 },
  "merchant": { "allow_list": ["amazon.com"] },
  "risk": { "velocity_limit": { "max_transactions": 5 } },
  "context": { "presence_threshold": 50.00 }
}

Next steps