ai newsSo., 25. Okt.
EilmeldungTexas verklagt OpenAI: Staat fordert behördliche Prüfung neuer KI-Modelle vor der Freigabe

What's the Best Way to Give AI Agents Controlled Spending Authority?

A Marketing AI agent's second ad purchase of the day, $150, landed as pending_human in front of the store owner about one second after its first $220 ad purchase cleared automatically. Same agent, same API key, same rule, the only difference was that the two spends together crossed a $300-a-day lin

dev.toSonntag 15:00 · 25.10.7 Min. Lesezeit
Bild: dev.to

A Marketing AI agent's second ad purchase of the day, $150, landed as pending_human in front of the store owner about one second after its first $220 ad purchase cleared automatically. Same agent, same API key, same rule, the only difference was that the two spends together crossed a $300-a-day line.

Disclosure: I work on Pink Agentic AI Payments at PinkWallet.

Short answer: give each AI agent the same kind of layered spending authority you'd give an employee with a corporate card: a monthly budget, a per-payment cap, a daily window that catches cumulative drift, an approval band for anything bigger, and a hard block on categories that should never be self-service. Pink Agentic AI Payments checks every one of those limits on the server, before a payment executes, so the limits hold even if the agent's own context has no memory of what it already spent today. Here's that model, run live against a sandbox.

Companies don't give employees unlimited corporate cards, and they don't rely on employees self-reporting their own spending either. They give a card with a limit, a manager who sees the statement, and a policy for what's never reimbursable. The same five levers work for an AI agent, and they map directly onto this rule engine:

curl -s -X POST "https://agentic-sandbox.pinkwallet.com/v1/sandbox/workspaces?utm_source=devto&utm_medium=article&utm_campaign=ai-agent-spending-authority" \ -H 'content-type: application/json' \ -d '{"company":"Bramble & Finch Roasters","email":"dev@brambleandfinch.example","template":"coffee"}' Enter fullscreen mode Exit fullscreen mode Trimmed real response:

{ "workspace_id": "9vmbv7tb8n", "name": "Bramble & Finch Roasters", "template": "coffee", "agents": [ { "id": "a_purch", "name": "Purchasing AI", "monthly_budget": 4000, "per_payment_cap": 500 }, { "id": "a_inv", "name": "Inventory AI", "monthly_budget": 2500, "per_payment_cap": 300 }, { "id": "a_mkt", "name": "Marketing AI", "monthly_budget": 3000, "per_payment_cap": 300 }, { "id": "a_pay", "name": "Payroll & Bills AI", "monthly_budget": 32000, "per_payment_cap": 15000 } ], "rules": 11 } Enter fullscreen mode Exit fullscreen mode Four agents, four different budgets and caps, sized for what each one actually does. Payroll's agent can move far more per payment than marketing's, because its job is to pay a fixed vendor on schedule, not to improvise.

curl -s -X PATCH "https://agentic-sandbox.pinkwallet.com/v1/admin/agents/a_mkt" \ -H "authorization: Bearer $ADMIN " -H 'content-type: application/json' \ -d '{"monthly":3500,"perPay":300}' Enter fullscreen mode Exit fullscreen mode { "id": "a_mkt", "name": "Marketing AI", "dept": "Marketing", "owner": "owner", "monthly": 3500, "perPay": 300, "spentMonth": 2060, "spentToday": 220, "status": "active" } Enter fullscreen mode Exit fullscreen mode monthly and perPay are the only two fields this PATCH call takes. monthly is checked before every payment. perPay is shown to the agent as single_payment_cap, but in the current sandbox the engine doesn't enforce that field on its own, so a hard per-payment limit is an amount rule with window tx. There's no hourly budget in this engine, only transaction, daily, and monthly windows, and the daily one is where most real drift gets caught.

The rule table for this agent has a matched pair:

Neither rule looks at a single payment in isolation. The day window is the agent's total spend for the day across all its payments, including this one, compared against the line. The payee scope ( cat:ads ) decides whether the rule applies at all; it doesn't narrow the running total to ads only.

curl -s -X POST "https://agentic-sandbox.pinkwallet.com/v1/payments" -H "authorization: Bearer $MKT " \ -H 'content-type: application/json' \ -d '{"payee_id":"p_meta","amount":220,"purpose":"Instagram ad boost, morning promo"}' Enter fullscreen mode Exit fullscreen mode { "decision": "allowed", "rule": "Ads up to $300 a day", "credential": { "max_amount": 220, "locked_to": "Meta Ads" } } Enter fullscreen mode Exit fullscreen mode Second ad spend, same day, same agent, same rule pair:

curl -s -X POST "https://agentic-sandbox.pinkwallet.com/v1/payments" -H "authorization: Bearer $MKT " \ -H 'content-type: application/json' \ -d '{"payee_id":"p_meta","amount":150,"purpose":"Instagram ad boost, afternoon top-up"}' Enter fullscreen mode Exit fullscreen mode { "decision": "pending_human", "rule": "Ads above $300 a day: ask the owner", "hold_id": "pay_50bbb7ea049b", "who": "Maya Chen (Owner)", "approvers_needed": 1, "expires_at": "2026-10-20T12:59:25.026Z" } Enter fullscreen mode Exit fullscreen mode $220 plus $150 is $370, over the $300 line, so the second request held instead of executing. The agent never had to track its own running total. One nuance worth knowing: after this hold, the agent's spentToday field still read $220, not $370, because a pending_human payment hasn't actually moved money yet.

It only counts toward the window once it's approved.

Approving a hold is a real admin-key call: POST /v1/admin/requests/{hold_id}/approve with {"by": "<approver name>"}. This step reproduces the $220 allowed / $150 pending_human pair in a fresh coffee-template workspace (company "Thistle & Oak Roasters," workspace_id t91ic75cpd ) to show the approval closing the loop end to end.

The owner approves the held $150 with the admin key, the same key behind the approvals console:

curl -s -X POST "https://agentic-sandbox.pinkwallet.com/v1/admin/requests/pay_7b5fcf234bea/approve" \ -H "authorization: Bearer $ADMIN " -H 'content-type: application/json' \ -d '{"by":"Maya Chen"}' Enter fullscreen mode Exit fullscreen mode { "payment_id": "pay_7b5fcf234bea", "decision": "approved", "amount": 150, "rule": "Ads above $300 a day: ask the owner", "credential": { "type": "virtual_card", "max_amount": 150, "locked_to": "Meta Ads", "single_use": true }, "approved_by": [ "Maya Chen" ] } Enter fullscreen mode Exit fullscreen mode The agent's own key can't take that same action on its own behalf. Calling the identical endpoint with the Marketing AI agent's key instead of the admin key gets rejected outright:

curl -s -X POST "https://agentic-sandbox.pinkwallet.com/v1/admin/requests/pay_7b5fcf234bea/approve" \ -H "authorization: Bearer $MKT " -H 'content-type: application/json' \ -d '{"by":"Maya Chen"}' Enter fullscreen mode Exit fullscreen mode { "error": "unknown route for an agent key" } Enter fullscreen mode Exit fullscreen mode After approval, the agent's own key can still read the resolved state and the now-issued credential with GET /v1/payments/{id}:

{ "payment_id": "pay_7b5fcf234bea", "decision": "approved", "amount": 150, "credential": { "type": "virtual_card", "max_amount": 150, "locked_to": "Meta Ads", "single_use": true }, "approved_by": [ "Maya Chen" ] } Enter fullscreen mode Exit fullscreen mode And the agent's spentToday only moves once the hold clears: it read $220 right after the hold was created, and $370 ($220 + the now-approved $150) once Maya Chen's approval went through. In practice, this is a human opening the approvals console at a URL shaped like /console?key=<admin_key>, seeing the hold, and clicking approve. The API call above is what that click does under the hood.

Spending authority isn't just a number. Two more real calls against the same agent:

{ "decision": "blocked", "rule": "Never: gift cards, cash-like, crypto", "why": "matched · block" } Enter fullscreen mode Exit fullscreen mode A brand-new payee, routed to the owner regardless of amount:

{ "decision": "pending_human", "rule": "New payee: always ask the owner", "who": "Maya Chen (Owner)", "approvers_needed": 1 } Enter fullscreen mode Exit fullscreen mode A first-time vendor gets a human look once, even at $100, which is the same instinct behind requiring a second signature the first time a new supplier gets paid.

This is test money against a public sandbox. Production with real money is early access, not something generally available yet. The evaluation order checked monthly budget and a company-wide daily ceiling as hard stops ahead of the rule table itself, according to the docs; this sandbox run exercised the per-agent rule table, not the company-wide ceiling specifically.

Does raising a monthly budget override the daily window? No. The docs describe both as separate checks, monthly budget as a hard stop checked ahead of the rule table, the daily window as a rule within that table; raising one doesn't change the other.

What happens if the owner never approves the hold? It carries an expires_at. After that, the payment is no longer actionable and the agent has to ask again if the work still needs doing.

Can different categories have different spending authority on the same agent? Yes. The ads pair above is scoped by payee: "cat:ads", so the same agent can have a tighter window for ads than for, say, approved software subscriptions, inside one rule table.

Sandbox is free, no account required: agentic-sandbox.pinkwallet.com. Full docs: pinkwallet.com/agentic/developers. Production with real money is early access only, not generally available.

Quelle: dev.to (dev.to). Original öffnen