Canopy
← All posts
Policy & Risk6 min read·April 27, 2026

Three ways your AI agent will lose money — and how to stop it.

Every team running agents in production has a story about almost losing money. There are really only three shapes of that story, and a single control closes each one.

ATAtif · Founder & CEO
/ share

We talk to a lot of teams putting agents in production for the first time. The conversation always reaches the same place: someone tells a story about the night an agent almost — or actually — moved more money than it should have.

The stories sound different in detail. They are the same story in shape. There are three versions of it, and they map cleanly to three controls. If you have all three controls in place, you can let the agent run. If you don't, you're either babysitting it or running on hope.

1. The runaway loop

A research agent kicks off a batch job over a long weekend. A retry path nobody tested fires every fifteen seconds against a paid API. Each call is six cents. By Tuesday morning the run has burned $9,400 of API budget and the team is on a call with the provider asking nicely about a refund.

Or: a trading agent on a sub-second cycle gets a state read it doesn't know how to interpret, decides the right move is to retry the order, and spends three minutes hammering a DEX before someone notices.

These are not exotic failures. They are the normal failure mode of a system that combines an LLM's planning with a tight loop and a balance to draw against. The loop doesn't have to be malicious. It only has to be a little wrong, for a little while.

The control: a rolling spend cap.

Set the cap on the agent's policy and forget it. We default new policies to a $10/24h cap. For something with real work to do you'll want higher — but the right move is to set the number based on what a good day costs, not based on what you're willing to lose.

spend_cap_usd       = 50
cap_period_hours    = 24

When the agent hits the cap, the next canopy.pay() returns denied with a clear reason. Your agent can branch on it (good agents do — they back off and try again later, or escalate to a human). Either way, the bleed stops at the number you chose. It doesn't matter how the loop got there.

The trade-off is friction at the boundary. An agent doing legitimate high-volume work will occasionally hit the cap and need to wait or get bumped. That's the right friction to have. A cap that's so generous it never trips is the same as no cap at all.

2. The redirected payment

This one shows up later, after the agent has been running long enough to feel safe. A vendor sends an email — really, a webhook, or a chat message, or a search result the agent reads — saying "please send the next invoice to this new wallet." The agent does what it's told. The "vendor" is not the vendor.

Or, the version we see more often in agentic-search workloads: an agent pays for a paid API, the API's response includes attacker-controlled text, and the text contains an instruction the agent acts on. Two API calls later it's signing a transfer to a wallet it had never heard of an hour ago.

Prompt injection isn't a hypothetical anymore. It's the most reliable adversarial behavior we see, because every paid surface an agent touches is potentially an injection vector.

The control: a service allowlist.

Allowlists in Canopy are keyed on registered service slugs, not raw addresses. You set the list of services the agent is allowed to pay — a SaaS subscription, a paid data provider, a specific vendor — and any payment to a service that isn't on the list gets denied at signing time, before the wallet sees it.

allowlist_slugs = ["stripe", "exa-search", "alpha-vantage"]

The check happens server-side, in the same RPC that evaluates the cap. It is not optional and it is not bypassable from the agent's side. If the agent has been talked into paying somewhere new, the signing service refuses, the agent gets a denied result, and the right humans get a chance to look at why.

The trade-off is operational: someone has to keep the allowlist current. The first time you onboard a new vendor it's an extra step. We think this is the right place to feel friction. The cost of paying the wrong recipient once is much higher than the cost of adding a row.

For payments to wallets that aren't registered services — one-off vendor payouts, treasury transfers — the allowlist doesn't apply. The cap and the approval threshold (next) are doing the work for those.

3. The one big mistake

Caps and allowlists handle volume and direction. They don't handle the case where the agent is permitted to make a payment, the payment is the right shape — but the amount is wrong by an order of magnitude.

The shape is: a normal recurring vendor invoice that's usually $50 comes in at $5,000 because of a typo, a billing error, or an upstream system glitch. The agent looks at it, sees a recipient it knows and a category it's seen, and pays. By the time anyone looks, the money is gone and the dispute is a phone call.

The control: an approval threshold.

Pick a number that catches "unusually large" without trapping every payment. Anything below the threshold flows through automatically. Anything at or above it pauses, routes to a human approval queue, and only proceeds if a human says yes.

approval_required        = true
approval_threshold_usd   = 25

In the SDK, an over-threshold payment doesn't fail. It returns pending_approval with an approval request ID. The agent can wait (waitForApproval: true blocks for up to five minutes by default) or it can hand the work back and pick it up after a human has decided. The dashboard shows the queue. Approvers see the recipient, the amount, the agent that asked, and the reason — and they decide with one click.

The trade-off is response time. Approvals are humans, and humans aren't fast. For a treasury bill-pay agent processing 80 invoices on the first of the month, you'd put the threshold at the level where you'd want to look. For a research agent doing per-call data buys at $0.005 each, you'd put it well above any reasonable single call so the agent stays autonomous.

Putting all three together

The three controls are designed to compose. You set them once, on a policy, and attach the policy to the agent. Most teams start with the same three numbers we default to, then tune from there:

ControlConservative startWhat it catches
Spend cap$50 / 24hRunaway loops, cycle-time bugs
Service allowlistInitial vendor listPrompt-injection redirection
Approval threshold$25Single-payment mistakes

After a couple of weeks you'll have a clean record of what your agent actually does. That's when the dial-in happens — caps move up to where they're not blocking real work, the allowlist grows, the threshold drops or rises depending on whether approvals are firing on noise or on signal.

The point is not that any one of these is novel. Spend caps exist; allowlists exist; approval queues exist. The point is that all three are enforced at signing time, on the same primitive, in front of every payment your agents make. If one trips, the wallet refuses. The agent doesn't get a chance to try harder.

That's the bar where letting the agent run stops being the scary part of the job.

Set your first policy in five minutes, or browse the policy patterns we see most often for a starting set.

/ share