Mytrya
Start a project

Approval steps for AI agents that touch money

When an AI agent can touch money, I make those actions requests rather than actions: the agent proposes a trial extension or a discount, and a person approves it in Slack or in a billing queue before anything runs against the billing provider. Cancellations need a second admin to confirm, and the agent never cancels because a customer went quiet. Underneath that, identifiers come from context rather than from the model, dry-run is the default, and each integration has its own write gate.

The setting

The AI support employee I built for a B2B software vendor with two product brands works in Freshdesk, Freshchat and a chat widget of its own, and it can reach the client's billing provider, FastSpring. Among its 20 tools are looking up a billing account, fetching an invoice, applying a discount and cancelling a subscription, plus requesting a trial extension or a marketplace discount. Those are exactly the tools a support hire needs and exactly the ones you do not want a model to call on a whim.

Requests, approved where the team already is

Trial extensions and discounts do not run when the agent decides they should. The agent sends a request, and a person approves it, either in Slack or in the billing approvals queue in the console. Only then does anything happen in the billing system.

Putting the approval in Slack matters as much as having one. The team already lives there, and the escalation channel is where they talk to the agent anyway. They can mention it to check a ticket, list open tickets, extend a trial or apply a discount. An approval step in a tool nobody opens turns into a backlog, then into someone approving everything without reading it.

Cancellation needs two people and an explicit yes

Cancelling an account is the action with the least room for error, so it has two separate guards. From Slack, a cancellation requested by one admin needs a second admin to confirm before it runs. In a retention flow with a customer, the agent cancels a subscription only when the customer says so explicitly. It never cancels on silence. A customer who stops replying halfway through a conversation about cancelling has not asked to cancel, and an agent that treats a timeout as consent will eventually cancel someone who simply went to lunch.

Identifiers come from context, never from the model

Every ticket ID and account ID the tools act on is read from the assembled context: the ticket, the conversation, the billing account that was looked up. None of them come from model output. If the model produces an account number in its reasoning, that number has nowhere to go, because the tool does not accept it as input.

This removes a whole category of failure that no amount of review catches easily. A discount approved for the right reason but applied to the wrong account looks fine in Slack. The safest version is the one where the wrong account was never an option.

Dry-run first, one integration at a time

Dry-run is the default state. With it on, the agent goes through the whole run and records every action it would take, and writes nothing. A master stub switch goes further and returns canned data from every external system, so the full loop can be exercised without touching a real account. On top of that, each integration has its own write gate.

That combination is what let the systems go live one at a time. Helpdesk writes could be on while billing stayed stubbed, and billing could be switched on later, after the team had watched it rehearse. I work this way on every project: weekly demos run the real system against real data in dry-run, so the client sees every action before it is allowed to take one.

A system page that says what the agent can do

The console has a system page that lists every capability with three things: its current state, a plain sentence about what that state means for a customer, and the setting that changes it. A capability might read as the agent can look up a customer's billing account but cannot change it, next to the flag that would allow it to.

It started as five LIVE badges, one per integration. That turned out to be the wrong question. A badge that says the dev board integration is live cannot tell you whether the agent is reading the board or writing to it, and for money it cannot tell you whether the agent can look at an invoice or apply a discount. The person approving requests in Slack needs to know which, and so does whoever is answering a customer's complaint about a charge. Plain sentences answer that question, and a badge cannot.

The same pattern in my own product

NEPSE Copilot, my investing tool for the Nepal Stock Exchange, follows the same rules with real orders. The evening scan proposes orders, each member gets a Telegram card with Approve and Skip, and approved orders are submitted in the next session through a broker adapter. The voice mode has 31 tools and none of them approve an order. IPO auto-apply sits behind a master switch and needs the member's own credentials. There is a paper account that fills at the next session's open with real one-way fees, so a strategy has a record before anyone trusts it with money.

None of these guards are clever. They are the controls a careful finance team would put on a new hire, written into the tool layer so they hold even when the model does something unexpected. The model's job is to notice that a customer qualifies for a trial extension and write a good request, and the decision to grant it stays with a person on the team.

Drawn from

More notes

Taking new projects · start within 2 weeks

Tell me about the process that eats someone's week.

What it is, who does it, how often, and what goes wrong when it’s late. I reply within one working daywith a scoping call or an honest reason it isn’t worth automating.

Start a project [email protected]

First projects from $3,000 · fixed price