LLM agents can select tools, generate arguments, and initiate workflows, but a valid tool call does not guarantee that an action is safe or authorized. Human approval gates solve this problem by introducing a controlled checkpoint between an agent's decision and the execution of a consequential operation. The application evaluates the proposed action, pauses when approval is required, presents the exact action to an authorized reviewer, and executes it only after verifying the decision.
The key is to implement approval as an application-level security control rather than a prompt instruction or confirmation button. A reliable design must preserve pending state, bind approval to the intended action, enforce authorization at execution time, and prevent duplicate side effects. This approach allows AI agents to automate routine work while maintaining human oversight of sensitive operations.
What Is a Human Approval Gate in an LLM Tool?
A human approval gate is a mechanism that pauses an AI agent's tool execution until an authorized person reviews and approves a proposed action.
Consider an enterprise AI assistant that can retrieve customer information, draft emails, issue refunds, and delete records. The application might allow authorized read operations automatically, while requiring approval before sending external communications, processing large refunds, or deleting production data.
The distinction is important: an LLM can propose an action, but the application must control whether that action can execute.
Three separate controls make this possible:
- Input validation: Determines whether tool arguments are correctly structured and satisfy business rules.
- Human approval: Establishes whether a designated reviewer has accepted the specific proposed action.
- Authorization: Determines whether the requesting identity or service is permitted to operate on the target resource.
These controls work together. A valid request may still violate access policies, and an approved request may become invalid if its arguments or the underlying resource change before execution.
Which LLM tool calls need human approval?
Not every tool call needs manual review. Requiring approval for every operation introduces unnecessary delays and can make agent workflows difficult to use.
A better approach is to classify tools by risk and define deterministic approval policies.
Search internal documentation: Allow automatic execution when access controls permit it.
- Retrieve customer information: Enforce authorization and data-access restrictions.
- Draft an email: Usually allow automatic execution because drafting does not send the message.
- Send an external email: Require approval when disclosure or business risks warrant it.
- Issue a refund: Apply threshold-based or mandatory approval.
- Delete production records: Require approval and potentially dual authorization.
Deploy a production change: Combine approval with automated release checks.
These are example policies, not universal requirements. Organizations should determine approval thresholds based on their risk tolerance, regulatory obligations, data sensitivity, and operational procedures.
Why LLM Agents Need an Explicit Approval Layer?
Traditional applications usually execute predefined business logic. LLM agents introduce another decision-making component: the model determines which tool to call and generates its arguments based on conversational context, retrieved information, and instructions.
This flexibility creates several risks.
Incorrect tool selection: A model may select a destructive operation when a read-only operation would have been sufficient.
Invalid or unintended arguments: A tool call may contain the wrong customer ID, recipient, amount, or resource identifier.
- Prompt injection: Instructions embedded in documents, emails, or web pages may influence the model to propose unauthorized actions.
- Excessive permissions: An agent with broad access may perform operations beyond what the user's original request requires.
- Unintended side effects: Once an external system processes a request, reversing it may be difficult or impossible.
Approval gates introduce a checkpoint where the application can enforce policy and a human can inspect the proposed action before it takes effect.
OWASP's guidance on excessive agency recommends limiting tool capabilities and permissions, applying least privilege, and requiring human approval for high-impact actions. Approval should complement these controls rather than replace them.
Designing the Architecture for Human Approval
A reliable approval system separates the model's decision-making process from the application's authority to execute tools. The LLM proposes an operation, a policy engine determines whether approval is necessary, and a trusted backend enforces the final decision.
Here’s a reference architecture:
- User request and LLM agent
- Tool validation and policy engine
- Authorized human reviewer
- Downstream service and audit log
Each component has a distinct responsibility:
- LLM agent: Proposes an action based on the user's request and available context.
- Policy engine: Applies deterministic rules to identify operations requiring approval.
- Approval service: Stores pending requests, validates reviewer decisions, and tracks status transitions.
- Execution layer: Enforces authorization and performs the approved operation.
- Audit system: Records decisions and execution outcomes for troubleshooting and compliance.
The approval service should remain independent of the model's generated explanation. A model-generated statement such as “The user has authorized this transaction” must never be treated as proof of authorization.
How to Implement Human Approval Gates With Python?
There are two practical implementation approaches: use a framework's built-in human-in-the-loop functionality or implement an approval service that sits between the agent and sensitive tools.
Framework middleware is useful for orchestrating interruptions and resumptions. A separate backend approval service provides greater control when multiple agents, applications, or business systems must follow the same policies.
Use LangChain and LangGraph middleware
LangChain provides HumanInTheLoopMiddleware for configuring which tool calls require human intervention. LangGraph provides checkpointing and interrupt/resume capabilities to preserve workflow state while waiting for a decision.
The following example illustrates the framework integration pattern.
Install the relevant packages in a Python environment and pin the versions validated by your project:
pip install langchain langgraph langchain-openai
from langchain.tools import tool
@tool
def read_customer(customer_id: str) -> str:
"""Retrieve a customer record."""
# Replace with an authorized read-only repository call.
return f"Customer lookup requested: {customer_id}"
@tool
def delete_customer(customer_id: str) -> str:
"""Delete a customer record."""
# Replace with a real, access-controlled deletion service.
return f"Deletion requested for customer {customer_id}"
These functions are illustrative placeholders. The deletion function must not be connected to a real data store until its authorization, approval, and audit requirements have been implemented.
Configure the agent to interrupt before executing the destructive tool:
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
model="openai:gpt-5.5",
tools=[read_customer, delete_customer],
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"read_customer": False,
"delete_customer": {
"allowed_decisions": ["approve", "reject"],
},
},
description_prefix="Action requires review",
),
],
checkpointer=InMemorySaver(),
)
This configuration demonstrates three important design choices.
First, read-only customer lookups are not interrupted by this middleware. They must still enforce ordinary data-access permissions.
Second, deletion requests require explicit approval or rejection. The policy does not permit reviewers to edit the tool arguments during the approval step.
Third, InMemorySaver is appropriate for demonstrating a workflow locally, but it is not a durable production persistence layer.
The model identifier, imports, and middleware signatures are version-sensitive. Confirm that the selected model integration and framework versions support this configuration before running the example.
Invoke the agent and capture the pending approval
A workflow needs a stable identifier so that its interrupted execution can be resumed later.
from uuid import uuid4
thread_id = str(uuid4())
config = {
"configurable": {
"thread_id": thread_id
}
}
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Delete customer C-1042",
}
]
},
config=config,
version="v2",
)
if result.interrupts:
for interrupt in result.interrupts:
print(interrupt.value)
The interrupt contains information about the proposed operation. The application can use this information to create an approval screen that identifies the tool, target, arguments, and expected impact.
A useful approval screen should answer:
- What action will be performed?
- Which resource will be affected?
- Who initiated the request?
- Why is the action being requested?
- What are the consequences?
- Which identity is approving it?
For a deletion operation, showing the customer identifier alone may be insufficient. A reviewer may also need the record's display name, the scope of deletion, and whether related records will be affected.
Store an explicit approval record
A server-side approval record should contain enough information to establish what was approved and whether it can still be executed.
- approval_id: Unique identifier for the approval request.
- tenant_id: Prevents cross-tenant access.
- requester_id: Identifies the initiating user.
- tool_name: Identifies the proposed operation.
- target_id: Identifies the affected resource.
- arguments_hash: Binds the decision to the normalized arguments.
- policy_version: Records the policy applied to the request.
- reviewer_id: Identifies the person who made the decision.
- status: Tracks pending, approved, rejected, expired, or executing states.
- expires_at: Defines when the approval becomes invalid.
- execution_id: Supports duplicate-execution prevention and reconciliation.
Compute the argument hash on the server from a canonical representation of the action and its security-relevant parameters. Include the tool name and target, not just the visible user input.
A hash alone is not an authorization mechanism. The backend must also validate the approval record, the reviewer's identity, the current permissions, and the action's integrity.
Security Risks and How to Mitigate Them
An approval gate is only as trustworthy as the system that creates, displays, and enforces the approval.
Prompt injection and misleading approval screens
Prompt injection can occur when an agent processes untrusted content that contains instructions intended to manipulate its behavior. An attacker might place instructions in an email or document that encourage the model to send confidential data or delete records.
The application should never treat a model-generated explanation as authoritative evidence that an action is safe.
Instead, construct the approval screen from validated action data and trusted application metadata. Clearly distinguish untrusted document content from system-generated information.
For example, before approving an email, a reviewer should be able to inspect the actual recipient, subject, message body, and attachments. A summary such as “Send the requested report to the team” does not provide enough information to detect an external recipient or an unintended attachment.
How to Choose the Right Implementation Approach?
The appropriate architecture depends on the number of agents involved, the sensitivity of their tools, and the durability requirements of the workflow.
Custom application middleware
Custom middleware works well when an existing application has a small number of sensitive operations and needs precise control over validation, approval, and execution. The trade-off is that the engineering team must implement persistence, workflow recovery, authorization, and concurrency handling.
LangChain and LangGraph
These frameworks are useful when the agent is already built with LangChain and LangGraph. LangGraph provides interrupt-and-resume workflows backed by checkpointing, while LangChain provides middleware for human review of tool calls.
The framework orchestrates the workflow, but the application remains responsible for reviewer authentication, business permissions, and downstream execution controls.
Pydantic AI deferred tools
Pydantic AI supports tools that always require approval through requires_approval=True and conditional approval through ApprovalRequired. This can be useful for Python applications with typed tool interfaces.
As with other frameworks, the application must establish a trusted reviewer workflow and enforce the final authorization decision.
A shared enterprise approval service
For multiple agents and applications, a centralized approval service can provide consistent reviewer roles, policy thresholds, expiration rules, and audit records.
This approach adds a service boundary and operational complexity, but it can prevent each agent team from implementing sensitive-action controls differently.
Testing Human Approval Gates Before Production
Approval logic should be tested as a security mechanism, not merely as a conversational feature. Showing an approval dialog does not prove that an unauthorized operation cannot execute.
Build a reproducible test suite. You should test the following scenarios:
- A permitted read-only tool executes subject to access controls.
- A protected tool pauses without producing a side effect.
- An authorized reviewer can approve the exact pending action.
- An unauthorized reviewer cannot approve the request.
- A rejected request produces no protected side effect.
- Changed arguments invalidate approval or trigger a new review.
- Expired approvals cannot execute.
- Concurrent workers cannot execute the same approval twice.
- Fabricated approval identifiers are rejected.
- Pending requests survive process restarts.
- Timed-out downstream operations are reconciled before retrying.
- Prompt injection cannot bypass application-level controls.
Test the actual downstream side effect. For example, verify that a database record remains unchanged before approval instead of checking only that the agent returned an interrupt.
Conclusion
Human approval gates make LLM tool execution safer by separating model decisions from consequential actions. Use risk-based approval policies, enforce authorization on the backend, bind approvals to exact tool arguments, and persist workflow state. Before production, test rejection, expiration, retries, and prompt injection. Treat approval as a security control, not a UI feature.
FAQs
Should every LLM tool call require human approval?
No. Read-only operations can run automatically when authorized. Destructive, financial, externally visible, or privileged operations may require approval based on deterministic risk policies.
Can an LLM approve its own tool call?
No. The model can propose an action and explain its reasoning, but approval must come from an authorized human or a separately defined, trusted policy mechanism.
How can developers prevent approved tool calls from being modified?
Bind the approval to the exact action and its security-relevant arguments. Revalidate them before execution and require a new review when material changes occur.
How should approval gates handle long-running workflows?
Persist the pending request and execution state, release the original HTTP request, and resume the workflow after an authenticated reviewer responds. Use durable storage rather than process-local state.
Does human approval prevent prompt injection?
No. Prompt injection can influence proposed actions and manipulate review screens. Combine approval with least-privilege tools, trusted action summaries, downstream authorization, and adversarial testing.
Top comments (1)
Binding the approval to an arguments_hash is the part I want to steal. Disclosure: I'm on the Auten team, we build a computer-use MCP server, so our tools are things like "click element 14" or "type into the focused field".
That is where the hash gets weak for us. The arguments of a click say nothing about the consequence. "click 14" is harmless on one screen and "Send to 300 contacts" on the next. So the security-relevant parameters are not in the call, they are on the screen: the window title, the label of the element, the recipient field. We classify at that moment from what is actually displayed (and treat screen text as data, never as instructions), then ask before anything that sends, buys or deletes.
Question for you: in a design like yours, would you hash a snapshot of the visible target (window + element label + key field values) into the approval record, and re-check it right before execution? The gap I keep seeing is the screen changing between approve and click.