An approval gate in an agent is not a confirmation dialog. It is a pause in a running process that may last minutes or days, and the agent has to resume exactly where it stopped.
Pause and persist state. Scalekit's guide to approval gates describes the core pattern: when the agent reaches a write action, it serializes the pending tool call, emits an approval request and blocks, with its full state saved until a decision arrives. It notes that frameworks implement this differently, but the requirement is the same everywhere: execution must be resumable from persisted state (Scalekit, Human-in-the-loop tool calling). LangChain's documentation states it as a configuration rule: a checkpointer is required for human-in-the-loop (LangChain, Human-in-the-loop).
Mark which tools need approval. Microsoft's Agent Framework lets you set an approval mode on each function tool, and its sample code notes that the no-approval setting is used "for sample brevity" and tells developers to use the always-require setting in production (Microsoft, Tool approval).
Offer the right decisions. LangChain defines four options a reviewer can take on a proposed tool call (LangChain):
| Decision | Effect | Example |
|---|
| Approve | Run the tool with the agent's arguments | Send the drafted email as written |
| Edit | Change the arguments, then run | Correct the recipient before sending |
| Reject | Skip the call and tell the agent why | Deny a file deletion with a reason |
| Respond | Return the person's reply as the tool result | Answer an "ask the user" question |
Two cautions from the same documentation are worth keeping. Use reject, not respond, to deny an action with side effects, because the model may read a response as a successful result. And edit arguments conservatively, because large changes can make the model rethink its approach and act unexpectedly.
Gate by policy, not by tool alone. Not every call to a tool deserves the same treatment. LangChain supports conditional interrupts that pause only when the arguments meet a condition, such as a file write outside the workspace. Scalekit describes a policy check at the moment of each call that returns allow, block or escalate based on who is asking, which resource is affected and which environment it runs in. Its example: developers may merge to feature branches automatically, but merges to the main branch escalate to a person (Scalekit).
def approval_policy(call, user, env):
if call.tool == "issue_refund":
if call.args["amount_cents"] > user.refund_limit_cents:
return "ESCALATE"
return "ALLOW"
if call.tool == "delete_account":
return "ESCALATE"
if env == "production" and call.tool in WRITE_TOOLS and not user.can_write:
return "BLOCK"
return "ALLOW"
Recheck before executing. Time passes between request and approval. Scalekit points out that credentials can expire or be revoked during the wait, so the resume path should check them before calling the tool rather than failing halfway (Scalekit). The same applies to the request itself: confirm it has not changed or been cancelled since it was approved.