The safest useful AI agent is not the one with the longest system prompt. It is the one that cannot exceed a clearly defined authority boundary.
That boundary has two parts. Permissions determine what the software identity can do. Human approval determines which otherwise-permitted actions may proceed in a particular situation. Mixing them together produces a common failure: an agent receives broad administrator access, then the team relies on a conversational "ask me first" instruction as the control.
A prompt is not an access-control system. The model can misunderstand, a tool can be called with the wrong arguments, or untrusted content can influence the run. Authorization and approval must be enforced outside the model.
Authentication, authorization, and approval are different controls
Authentication answers, "Which user or service is making this request?" Authorization answers, "What is that identity allowed to do?" Approval answers, "Should this exact permitted action happen now?"
An approval click must not elevate the agent into a role it never had. If the agent is authorized to draft an email but not send one, approving the draft should not magically grant mailbox-wide send access. The application should hand the approved payload to a narrowly scoped sending service or execute it through a dedicated tool with the minimum required permission.
This separation also improves auditability. You can distinguish a denied permission from a missing approval, and an operator can see whether the control failed at identity, policy, or review.
Start from capabilities, not job titles
"Sales agent" and "operations copilot" are descriptions, not permission sets. Inventory concrete capabilities:
- read a contact record by ID;
- search contacts within the current tenant;
- draft a message;
- send to one approved recipient;
- update a specified CRM field;
- create a calendar hold;
- cancel an appointment;
- export records;
- delete records.
Expose only the capabilities required for the current workflow. Separate read tools from write tools, and separate reversible changes from destructive or externally visible actions. Avoid one generic "execute API request" tool that can call any endpoint. The tool boundary should make disallowed behavior impossible, not merely discouraged.
OWASP's guidance on excessive agency identifies three root causes: excessive functionality, excessive permissions, and excessive autonomy. The remedies follow directly: remove unused tools, narrow each tool, minimize credentials, and require independent authorization for high-impact actions.
Apply least privilege at three layers
First, narrow the service identity. Give each deployed agent or workflow its own account, tenant scope, and credentials. Avoid shared administrator tokens.
Second, narrow the exposed tools. A mailbox summarizer needs message read access, not send and delete functions. A CRM enrichment workflow may need to update three fields, not arbitrary records and schema settings.
Third, validate each operation at execution time. Check tenant ownership, record scope, allowed fields, amount limits, recipient rules, and current state in deterministic code. The agent's proposed arguments are untrusted input, even when the model generated them from a legitimate request.
For MCP servers, the official authorization tutorial recommends least-privilege scopes per tool or capability, token validation, short-lived tokens, correct audience checks, and HTTPS. The MCP security best practices explicitly forbid token passthrough because it can bypass server controls and damage accountability. An MCP server remains responsible for authorizing every inbound request; a client-side consent screen is not enough.
Choose approval based on impact, not uncertainty alone
Requiring approval for every action creates fatigue and teaches reviewers to click through. Requiring it only when the model says it is uncertain misses confident mistakes. Define approval rules from the effect of the action.
Usually safe without per-action review:
- read-only retrieval within an authorized scope;
- classification or drafting that remains internal;
- reversible changes below a documented limit;
- actions validated by strong deterministic rules and covered by monitoring.
Usually appropriate for explicit approval:
- sending a message or publishing content;
- moving money or changing billing;
- deleting, cancelling, or closing records;
- changing access, credentials, or security settings;
- disclosing sensitive data to a new destination;
- bulk changes or actions above an amount or record-count threshold;
- legally or clinically consequential decisions.
Some actions should remain unavailable even with ordinary approval. Examples include exposing unrestricted shell access to a business workflow, allowing an agent to grant itself permissions, or letting one reviewer approve a high-risk action they initiated when policy requires separation of duties.
A reusable permission and approval matrix
Build this matrix from the deployed identity and actual tool handlers, not from a job title or prompt. The example decisions below show the level of specificity required; each organization must set its own limits and separation-of-duties rules.
| Capability | Runtime permission | Per-action review | What approval must bind |
|---|---|---|---|
| Read one in-scope customer record | Allowed for the verified tenant | Usually no | Tenant, record ID, allowed fields, and purpose logged |
| Draft an outbound message | Allowed in an internal draft store | Usually no | No external side effect; recipient and source context retained |
| Send one message | Allowed only through a narrow sending tool | Yes unless an approved deterministic policy covers it | Final recipients, subject, body, attachments, sending identity, and expiry |
| Update permitted CRM fields | Limited to named fields and record scope | Based on field sensitivity, reversibility, and batch size | Record version plus before-and-after field values |
| Bulk update records | Limited by tenant, filter, fields, and maximum count | Yes | Resolved record set or immutable query, exact changes, count, and expiry |
| Delete, cancel, or move money | Separate narrow capability or unavailable | Yes, with stronger review where policy requires it | Exact target, amount or effect, reason, reviewer identity, and single-use token |
| Change access or credentials | Not available to the ordinary workflow agent | Separate administrative process | Authenticated administrator, change set, second reviewer if required, and audit record |
| Execute arbitrary shell or API calls | Unavailable | Approval must not make it available | Replace with purpose-built tools rather than approving an open-ended command |
The important column is not “human in the loop.” It is the immutable material that the human actually authorizes. If the recipient, amount, record version, attachment, or affected set changes, the previous decision no longer applies.
Show the reviewer what will actually happen
"Approve tool call?" is a weak review surface. The reviewer needs a plain-language summary and the exact material details:
- action and tool name;
- destination account, recipient, or record;
- fields that will change, shown as a before-and-after diff;
- message or content that will leave the organization;
- amount, quantity, or number of affected records;
- source request and relevant evidence;
- whether the action is reversible;
- why approval was required.
Do not hide decisive details behind an expandable JSON block. Raw arguments can remain available for technical review, but the default screen should help the responsible person make the decision.
Approval must be informed. A reviewer should be able to edit the proposed action, reject it with a reason, or send it back for correction. If the same person cannot reasonably assess both business meaning and technical risk, split the review between the appropriate roles.
Bind approval to immutable arguments
An approval should authorize one exact operation, not a general intention. Canonicalize the tool name and arguments, store their hash with the approval, and reject execution if the payload changes. Include the authenticated requester, tenant, target resource version, and an expiration time.
This closes a subtle gap: the agent proposes a safe action, receives approval, then state or arguments change before execution. For a record update, use the record version or an optimistic concurrency check so an approval for yesterday's balance cannot be applied to a different balance today. For a message, approval should bind to the final recipients, subject, body, and attachments.
Make approval single-use by default. "Always approve" can be useful for a low-risk tool during one tightly scoped run, but it is a policy decision, not a convenience toggle. The OpenAI Agents SDK human-in-the-loop guide distinguishes per-call approval from sticky decisions and supports pausing, serializing, and resuming a run. Whatever runtime you use, preserve the same semantics explicitly.
Revalidate after approval
Approval does not replace input validation or current-state checks. Run them again immediately before execution because permissions, target state, policy, and external conditions may have changed while the request waited.
A sound sequence is: 1. Agent proposes a structured tool call. 2. Code validates schema and policy. 3. The system creates an approval request bound to the validated arguments. 4. A reviewer approves, edits, or rejects it. 5. The system verifies identity, approval binding, expiration, permissions, policy, and resource version again. 6. A narrow execution service performs the action once and records the external operation ID.
If the reviewer edits the payload, treat it as a new version and re-run validation. If a rejection occurs, do not let the model silently route around it through another tool. Record the denied goal and stop or return control to the user.
Protect the approval channel
Approval links need normal application security: authenticated sessions, authorization to review that tenant and action, CSRF protection where relevant, short expiration, and resistance to replay. Do not put the full sensitive payload or a bearer token in a URL.
For high-impact actions, use step-up authentication or two-person approval based on existing organizational policy. Deliver reminders through email or chat if useful, but make the decision in the authenticated application rather than accepting an ambiguous emoji or reply from a forwarded notification.
The approver also needs time and context. Set escalation and expiry rules for unattended requests. Never interpret silence or a timeout as approval.
Log enough to reconstruct the decision
An audit record should connect the user request, agent run, proposed tool call, policy result, approval version, reviewer identity, timestamps, final arguments, execution result, and external operation ID. Record edits and rejections, not just successful approvals.
Keep sensitive content out of general logs. Store detailed approval payloads in an access-controlled audit store with a defined retention policy. A trace that every developer can read may be a new data exposure even when the action itself was correctly approved.
NIST's Generative AI Profile recommends defining roles and responsibilities for human-AI configurations and making evaluation rigor proportional to identified risk. See the official NIST AI 600-1 profile. That is a better basis for review policy than a universal rule such as "put a human in the loop."
Human approval is not a universal safety layer
Review does not prevent prompt injection before the approval screen. It does not make overbroad credentials safe. It does not detect a misleading summary, sanitize a malicious attachment, make a tool idempotent, or guarantee that a rushed reviewer notices a problem.
Use approval alongside least privilege, structured tools, deterministic validation, isolation, rate and scope limits, audit logs, and tested recovery. For the operational controls around the runtime, use the self-hosted AI agent deployment checklist. For implementation, our AI agent systems service designs approval into the run itself, and custom MCP server development applies permission checks at the tool boundary.
The design goal is simple: the agent should have enough authority to complete the intended work, no authority to expand its own reach, and a specific accountable decision before any action whose consequences deserve one.
