How much should an agent be allowed to spend — and why "how much" is only half the answer.
Agents burn tokens, call paid APIs, trigger cloud spend and move money. Teams answer with limits: a per-transaction cap, a daily budget, a whitelist. That instinct is right — but a limit answers how much, while the harder question is whether this specific payment is allowed at all.
A spending limit is necessary, but it is not a complete authorization model. Limits constrain quantities; they don't judge intent:
Limits are the frame. Authorization is the judgment inside the frame.
payment request (amount, recipient, agent)
│
▼
authorize() ── evaluated in one place, atomically:
│ ├── per-transaction cap
│ ├── budget window (daily/monthly, cumulative, per agent)
│ ├── merchant / recipient policy (allow / block)
│ ├── approval threshold (over $X → human)
│ └── identity (which agent is spending)
▼
ALLOW (signed grant) / APPROVAL (human) / DENY
▼
executor verifies the grant — then, and only then, the rail executes
Because every check happens outside the agent loop, in one atomic decision, splitting and racing can't dodge it: the budget counter is updated in the same step as the decision, and execution requires the grant.
# policy.yaml
version: "2.1.0"
policy:
budget: { daily: 100, monthly: 1000 } # cumulative windows
transaction: { max: 50 } # per-payment cap
merchants:
allowed: [openai.com, mcdonalds.com] # recipient policy
blocked: [scam-vip.com]
approval: { over: 30 } # over $30 → human
agents:
shopping-agent:
budget: { daily: 50 }
transaction: { max: 25 }
from spendshield import SpendShield
shield = SpendShield(dry_run=False)
shield.load_policy("policy.yaml")
r = shield.authorize(agent="shopping-agent", amount=40, to="openai.com")
# $40 > $30 approval line → APPROVAL (human decides)
r = shield.authorize(agent="shopping-agent", amount=45, to="scam-vip.com")
# → DENY (blocked merchant), regardless of budget
| Request | Outcome |
|---|---|
| $20 → openai.com (under cap, under approval line, budget left) | ALLOW |
| $40 → openai.com (over approval line) | APPROVAL — human required |
| split attempt: 3 × $20 → openai.com | each ALLOW but budget consumed cumulatively — the third hits the daily window |
| $500 → scam-vip.com | DENY — blocked merchant (cap never even matters) |
SpendShield implements this model — limits as one part of a deterministic authorization decision, enforced with a signed single-use grant that execution must consume. Open source, MIT, Python + MCP. Try the interactive policy playground to feel the difference between a cap and a decision.
pip install spendshielduvx --from spendshield spendshield-mcp --policy policy.yaml