Policy Engine
How PINTI evaluates spend intents against your policies.
Mental Model
Every spend intent goes through an evaluation pipeline. Think of it as a series of gates — the first failure short-circuits the evaluation:
Policy exists?
→ Merchant not blocked?
→ Category not blocked?
→ Merchant in whitelist? (if set)
→ Category in whitelist? (if set)
→ Under single-transaction limit?
→ Under daily limit?
→ Under monthly limit?
→ Under velocity limits?
→ Under approval threshold?
→ ALLOW ✓Evaluation Pipeline
| # | Check | Decision | Reason |
|---|---|---|---|
| 1 | Policy exists and is active | DENY | NO_ACTIVE_POLICY |
| 2 | Merchant not in blockedMerchants | DENY | BLOCKED_MERCHANT |
| 3 | Category not in blockedCategories | DENY | BLOCKED_CATEGORY |
| 4 | Merchant in allowedMerchants (if set) | DENY | MERCHANT_NOT_ALLOWED |
| 5 | Category in allowedCategories (if set) | DENY | CATEGORY_NOT_ALLOWED |
| 6 | Amount ≤ maxSingleAmount | DENY | EXCEEDS_SINGLE_LIMIT |
| 7 | (dailyTotal + amount) ≤ dailyLimit | DENY | EXCEEDS_DAILY_LIMIT |
| 8 | (monthlyTotal + amount) ≤ monthlyLimit | DENY | EXCEEDS_MONTHLY_LIMIT |
| 9 | Transactions/min ≤ maxTransactionsPerMinute | DENY | VELOCITY_LIMIT_MINUTE |
| 10 | Transactions/hour ≤ maxTransactionsPerHour | DENY | VELOCITY_LIMIT_HOUR |
| 11 | Amount ≤ requireApprovalOver | REQUIRE_APPROVAL | REQUIRES_APPROVAL |
| 12 | All checks passed | ALLOW | OK |
Note
When the decision is REQUIRE_APPROVAL, the spend is paused until a human approves or rejects it. See Approvals & Callbacks for the full notification and callback flow.
Note
Daily and monthly totals only count ALLOW decisions. Denied or pending-approval requests don't count toward your limits.
Policy Fields
| Field | Type | Description |
|---|---|---|
name | string | Human-readable policy name |
unit | string | Currency unit (e.g. "USD"). Must match the intent. |
maxSingleAmount | number | null | Max allowed per single transaction (minor units). Null = no limit. |
dailyLimit | number | null | Max total spend per day (UTC). Null = no limit. |
monthlyLimit | number | null | Max total spend per month (UTC). Null = no limit. |
requireApprovalOver | number | null | Amounts above this trigger human approval. Null = never require. |
maxTransactionsPerMinute | number | null | Max transactions per minute. Null = no limit. |
maxTransactionsPerHour | number | null | Max transactions per hour. Null = no limit. |
allowedMerchants | string[] | Whitelist. Empty = allow all. Non-empty = only these merchants. |
blockedMerchants | string[] | Blocklist. These merchants are always denied. |
allowedCategories | string[] | Whitelist. Empty = allow all. Non-empty = only these categories. |
blockedCategories | string[] | Blocklist. These categories are always denied. |
isActive | boolean | Toggle policy on/off without deleting. |
Policy Resolution
PINTI uses the first active policy in your workspace. If no active policy exists, all spend intents are denied with NO_ACTIVE_POLICY.
Tip
Only one policy can be active at a time per workspace. Toggle policies on/off from the Dashboard to switch between rule sets.
Best Practices
Start Simple
Begin with one policy with conservative limits. Increase limits as you build confidence in your agent's behavior.
Example Policy Sets
Basic daily limit
$50/day limit, no single-transaction cap
{
"name": "Conservative",
"dailyLimit": 5000,
"unit": "USD"
}OpenAI-only with approval threshold
Only OpenAI, approve over $50
{
"name": "OpenAI Only",
"allowedMerchants": ["openai.com"],
"requireApprovalOver": 5000,
"dailyLimit": 50000,
"unit": "USD"
}Blocked merchant list with velocity limits
Block bad merchants, max 10 tx/min
{
"name": "Production Policy",
"blockedMerchants": ["evil.com", "scam.io"],
"maxSingleAmount": 20000,
"dailyLimit": 100000,
"monthlyLimit": 1000000,
"maxTransactionsPerMinute": 10,
"maxTransactionsPerHour": 100,
"requireApprovalOver": 10000,
"unit": "USD"
}Note
All amount fields are in minor currency units (e.g. 5000 = $50.00 in cents).