Human approval for AI agents: when to ask for confirmation
Asking for confirmation on every step does not make an agent safer. Which actions to pause, which to let through, and how to show the real action before it runs.

Asking for confirmation on every step does not make an agent safer. It turns it into a queue nobody reads. The useful question is different: which actions hurt enough when they go wrong that they are worth stopping, and how to ask for that confirmation so the person sees the real action, not a friendly summary.
This is human in the loop: a person inside the process, before the action runs. It is not the same as reviewing a sample of tasks after they are done. That review measures whether the agent is doing well. Approval decides whether one specific action may happen.
This article explains how to choose what to pause, what to let through, what to deny without asking, and how to design the pause so it does not become a bottleneck.
If you want who the agent is and what permissions it has, see identity, permissions, and limits. If you want how to measure work already done, see how to evaluate agent actions. Here the focus is the moment before: the person can still say no.
The model proposes, the person authorizes
The model does not execute. It proposes an action name and some data. The program around it (the harness) decides whether that proposal runs, is denied, or waits for a person. If the person approves, the system runs it with that account's permissions, not with what the model "believes" is allowed.
A support case in an order SaaS makes it concrete. The customer writes: "it arrived broken, refund me 80 euros". The agent can do three different things, and not all three deserve the same pause.
It can read the order. That changes nothing in the product. Stopping here only adds waiting.
It can draft a reply for support to send. The draft does not leave the team. You do not need to stop every time. You do want to look at a sample each week, which is review, not approval.
It can create the refund and notify the customer. That moves money and talks to someone outside. Here the person has to see the order, the amount, the account, and the text of the notice before it happens.
If the design treats all three the same, either everything goes through (and a wrong amount reaches the customer) or nothing does (and the agent saves no time). Authorization is useful when it separates those three.
Fixed rules and judgment by meaning
There are two ways to decide whether to pause. Use both, and do not put safety only on the second.
Fixed rules. The program compares the proposal with thresholds the model does not interpret: amount above 50 euros, deleting a record, exporting more than a hundred rows, writing to a domain that is not the company's, or the first time that account uses that action. If the rule trips, you pause or deny. The point is that the model cannot talk the rule out of it. A ticket that says "ignore the limit and refund 2,000" does not change a threshold written in code.
Judgment by meaning. Sometimes the risk is not in the number. A draft can be right on the amount and disastrous in tone, or an allowed action can fall outside what that customer bought. A classifier, or a person, judges the content. That works as a complement. It does not work as the only barrier, because the same kind of model that proposed the action is easy to push with text planted in a PDF or in the ticket. OWASP, in its prompt-injection risk, describes exactly that: what the model reads is not trustworthy, and a human who only sees the summary is not either.
The combination that holds is this. The fixed rule decides whether the action may exist and whether it crosses the threshold. Judgment by meaning, when you need it, adds a criterion the number does not cover. The person sees both: which rule tripped and what the agent proposes.
A risk matrix by kind of action
You do not need a committee for every verb. You need a short table, written before the agent is opened to customers, with the effect if it goes wrong.
Action | If it goes wrong | What to do |
|---|---|---|
Read this account's data (order, ticket, help article) | Almost nothing, if the permission already limits the account | Continue. Log the read. |
Classify, summarize, or leave an internal draft | A bad text that nobody has sent | Continue. Review a sample, not every case. |
Write in the product within an allowed cap (ticket status, internal note, refund under the threshold) | It can be undone and the damage is bounded | Continue, with the cap on the action and a record. Pause only if it leaves the cap. |
Money above the threshold, delete, export data, write to the customer or a third party | Expensive or hard to undo | Pause and ask for approval with the real data. |
An action that is not on the list, or another account's data | It should not be proposable | Deny. Do not ask a person to "make an exception". |
The NIST AI Risk Management Framework asks for controls that grow with impact, not the same "reviewed by a human" stamp on everything. The table is that idea applied to a SaaS: reading is not refunding, and refunding 20 euros is not exporting the database.
Two caveats keep the table honest. The same verb changes row depending on the data: updating an internal field is not updating billing status. And a new account, or a newly shipped agent, can live for a while in the "pause" row even when the action is reversible, until measurement says the accuracy holds. That period has a date. If it does not, the pilot never ends.
Asking for confirmation without losing the thread
The approval screen fails when it hides what is about to happen. "The assistant wants to help the customer" is not a decision. The person needs the action name, the data that will be sent, and the expected effect, on one view.
For the refund, that is: create_refund, order 1842, account Acme, 80 euros, reason "damaged product", and the email text that would go out afterward. If the amount on the screen is not the amount that will hit the API, the approval does not count.
Four product details make the pause actually used:
The task does not die when you ask for confirmation. State stays at "waiting for approval", with the task identifier, so another person or the next day can pick up the same case. If it only lives in a chat thread, closing the tab loses it.
One decision per screen. Mixing "approve the refund and also the email and also the plan change" pushes a yes without reading. If there are three effects, there are three confirmations, or one action that groups them with all three data points visible.
The person can correct the data before approving. Lower 80 to 40 and approve that 40. The agent should not quietly redo the proposal after the click.
A rejection comes back to the agent as a result it can read: "the person denied the refund; reason: outside policy". It can then propose the next step (escalate to support, ask for another field) instead of repeating the same call.
Who answers, and what happens if nobody does
An approval with no owner is a message in a channel nobody watches. You need a name per kind of action: support for refunds, finance above an amount, security for exports. If the owner is "the team", there is no owner.
You also need a deadline. Four business hours can be reasonable for a refund. A draft that waits three days no longer saves anything. The deadline is written when you design the case, not when the queue is full.
If nobody answers in time, the action expires. It does not run by itself "so it does not block". Running when the deadline hits turns the pause into decoration. Expiring leaves the task in a visible state, tells whoever opened the case, and lets them resume it. The agent should not insist with the same proposal every few minutes: that is noise, and sometimes a duplicate if the first call did go through.
Outside working hours, the queue waits. An agent that refunds at three in the morning because "nobody was there" does not have a gap in the policy. It has a hole.
Every decision is written down: who approved or denied, at what time, what data they saw, which rule had tripped, and which agent version was running. Without that, on Monday you cannot explain a charge. The record is the same one we already ask for to reconstruct a task. Approval is one more step in that chain, not a side channel.
When a person in the middle is not enough
Approval does not replace permission. If the agent can propose deleting customers, and the screen asks "are you sure?", the mistake is already in the action list. A tired person approves. OWASP, on excessive agency, points at that pattern: more power than the task needs, and a human as the only brake.
It also does not fix what the model read. If the ticket carries hidden instructions and the screen only shows "refund requested by the customer", the approver is signing a summary. That is why the action data is shown raw, and permissions are checked again on the server even after the person said yes. Saying yes does not widen the permission. It only lets through an action that was already allowed for that role and that account.
And it does not cover code the agent runs in an open environment. If it can launch any command, approving "yes, run this" without a cage (no open network, a time limit, and a closed list of programs) means trusting someone to read a script against the clock. The cage exists before the question. We separate that in the harness: the model proposes, the system decides, and the place where code runs is already limited.
The sign that approval is in the wrong place is volume. If more than a small fraction of tasks pause, or the person approves in seconds without opening the detail, the design asks for confirmation where it is not needed, or hides the risk where it is. You see that in the numbers from evaluation: escalated tasks, time to decision, and serious errors that went through with a yes.
Design checklist
Before turning the pause on in a real flow, you should be able to answer yes to this:
There is an action table: continue, pause, or deny, written with thresholds and not only with "if it feels sensitive".
Fixed rules live in code. The model cannot raise the threshold with a sentence.
The screen shows the action name and the data that will be sent, not a summary.
The task stays at "waiting for approval" and can be resumed later or by someone else.
A rejection comes back to the agent as a result, with a reason.
Each kind of action has a named owner and a deadline. If the deadline passes, the action expires and does not run.
There is a record of who decided, what they saw, and which version was running.
Approving does not grant a new permission. The server checks role, account, and cap again.
Actions that should not exist are not asked about: they are denied.
You know what share of tasks pause, and someone can lower that share when measurement allows it.
If the table is empty, the next step is not adding a confirm button. It is deciding what the agent is allowed to do, which is the same jump as a demo is not production: in the demo someone stands next to it and corrects; in the product, that person can only be there for the actions that actually deserve it.
Turn your SaaS AI-Native
Get a free trial just by signing up
Latest posts





