An AI agent tool approval flow lets a human or policy authority review a proposed action before the agent executes it. If approval binds to a natural-language summary the model generates rather than the canonical action envelope, the action that runs can differ from the action the approver saw. This post documents a safe default for binding approval to the exact tool invocation and its trade-offs.
System description
The approval system binds every approved action to an action_hash computed over the full normalized envelope. The executor receives only an envelope ID and fetches every parameter from the trusted store, not from the model. No execution proceeds without a matching approval in the store, and each envelope is accepted for execution exactly once.

AI Tool Execution Workflow
Architecture choice
There are three common placements for the approval decision. The right choice depends on whether the agent serves a single user or a broader team, and how much human review each action class warrants.
Inline user approval
The agent pauses and presents the proposed action to the user who initiated the task. That same session receives the approval and continues execution.
Use this when:
The action affects only the requesting user
The user is present and can review the exact target and parameters
The agent is a personal assistant or a local developer tool
Trade-off: the requester may be the wrong approver for financial or cross-tenant actions. Timeout and replay handling are harder when the approval decision is tied to an interactive session.
Policy-backed approval queue
The agent submits the normalized envelope to a queue. A policy engine chooses the required approver and the execution window.
Use this when:
Actions affect shared resources or regulated workflows
Different action classes need different approvers
You need searchable evidence for audits and incident review
Trade-off: adds latency and product complexity. The queue also becomes a surface for stale pending actions and approval phishing.
Delegated automation approval
A workflow owner pre-approves a class of actions through a policy rule. The executor can run without a human when the envelope matches the policy exactly.
Use this when:
The action class is repetitive and low-variance
The policy can be expressed as exact targets and allowed parameter values
Failed or incorrect execution is reversible
Main risks: policy drift and approval scope that gradually expands into standing privilege.
Common middle ground: require human approval for new action shapes and high-impact targets; allow delegated approval only for repeated actions with the same normalized envelope shape.
Golden path
Start with this path:
Agent proposes tool call → normalizer creates canonical envelope → policy assigns approval tier → approver approves `action_hash` → executor fetches stored envelope by `envelope_id` → executor claims envelope before side effect → evidence log records outcomeRelated patterns:
For the upstream control that prevents a malicious tool description from steering the agent toward deceptive approvals, see Designing a Safe Approval Flow for MCP Tool Descriptions; the hash-binding mechanism is identical, applied one stage earlier in the same pipeline
The interception layer that makes envelope enforcement possible across agents is the AI Agent Gateway; without a mediation layer, there is no single place to attach the envelope and enforce the hash check before tools are called
For the closest human-in-the-loop analog outside the AI context, see Designing a Safe Support Impersonation Flow; both share the scoped-credential and action-gate design, with the key difference being that impersonation binds to a session and this pattern binds to a single tool invocation
Minimal system context
This pattern assumes the agent cannot call tools directly. Tool calls pass through an agent gateway or execution service that normalizes the proposed action and enforces policy.
User (identity): the person or service that assigns the task to the agent
LLM agent: proposes tool calls; untrusted for authorization
Action normalizer (control plane): translates proposed calls into server-controlled envelopes
Policy engine (authorization): decides
allow,deny, or the required approval tierApproval UI (control plane): presents envelope fields to the approver; the decision surface
Envelope store (data plane): authoritative source for envelope state; must support atomic state transitions
Executor (control plane): claims an approved envelope and runs the side effect
Execution authority: limits the executor to the approved operation and target; implemented by target-side authorization, scoped tokens, assumed roles, or a credential broker
Target tool: performs the side effect; enforces its own authorization separately
Evidence log (data plane): append-only record of every envelope lifecycle event
Core design
Action envelope
The envelope is the canonical, server-created record of a proposed tool call. The approver approves action_hash; the executor runs only from the stored envelope. Minimum fields:
envelope_id: server-generated ULID or UUIDv7; the single-use execution tokenactor_id: authenticated identity of the requesting principal, derived from the session, never from the request bodytenant_id: derived from the session token, never from the model's outputtool_id: stable internal identifier, distinct from any display nameoperation: the specific method on the tool (send,delete,deploy)target: normalized resource identifierparameters: the full, normalized parameters objectparameters_hash: SHA-256 over canonical JSON (RFC 8785 JCS) ofparameters; the executor re-derives and checks this at execution timenormalizer_version: version of the normalization logic applied toparameterstool_schema_version: version of the tool's parameter schema at proposal timeexpires_at: policy-assigned approval deadline, set at envelope creation time before the approval view is rendered; never nullaction_hash: SHA-256 over canonical JSON oftenant_id,actor_id,tool_id,operation,target,parameters_hash,normalizer_version,tool_schema_version, andexpires_at; this is what the approver approves
Normalizer
The normalizer maps the model's proposed tool call into the stable envelope schema. It resolves aliases (prod / production / PROD → "production") and converts amounts to integer smallest-unit representation (cents, not dollars). Unknown parameter values are rejected at normalization time, not passed through. Use RFC 8785 JCS for deterministic hashing across runtimes and languages; ordinary JSON key ordering is not stable.
The same normalization logic runs at proposal time and at execution time. Store the current version in normalizer_version. At execution time, reject envelopes whose normalizer_version or tool_schema_version is no longer in the active allowlist; require a new envelope and re-approval.
Policy engine
The policy engine decides allow, deny, or the required approval tier based on the fully normalized envelope. It operates as a whitelist: any tool call matching no rule is denied, not forwarded to a human for improvised review. When routing to a human approver, the engine excludes envelope.actor_id from the candidate pool.
Approval UI
The UI renders directly from the server-side envelope, not from the model's chat context. Show all security-relevant fields by default: tool, operation, target, tenant, amounts, recipients, environment, and permission scope. Make the full canonical envelope available without truncation. For irreversible operations, display the "cannot be undone" label derived from the tool schema.
For high-risk actions, require the approver to type the target name or amount before submitting.
Executor
The executor receives only the envelope_id. At execution time, it fetches the envelope from the server-side store, recomputes parameters_hash from envelope.parameters, recomputes action_hash from the stored envelope fields, and compares the recomputed action_hash against the approved record's action_hash. It then checks expiry, status, active normalizer_version / tool_schema_version, and tenant/resource consistency before performing an atomic compare-and-swap to claim the envelope. That claim must precede the side effect.
The executor should not rely on reusable broad credentials for approval-bound actions. Limit execution authority to the approved operation and target.
For internal tools, the target service can enforce this directly by accepting envelope_id, fetching the stored envelope, and verifying tenant, operation, target, status, and expiry before executing. For external APIs or cloud operations, use a short-lived scoped token or assumed-role session tagged with envelope_id.
The required property is scoped execution authority. A credential broker is one implementation, not a required component.
Evidence log
State transitions produce append-only events. On the approval side: action.proposed, approval.required, approval.granted, approval.revoked. On the execution side: execution.claimed, execution.started, execution.succeeded, execution.failed. The log joins envelope_id, actor_id, approved_by, tool_id, target, tenant_id, and the outcome. A reconciliation job should alert on execution.claimed or execution.started events with no terminal outcome after twice the approval TTL.
Minimal API shape
POST /agent-actions
→ { envelope_id, action_hash, expires_at, approval_requirement }
GET /agent-actions/{envelope_id}/approval
→ approval view rendered from envelope fields
POST /agent-actions/{envelope_id}/approve
→ { approved_at, action_hash, expires_at }
POST /agent-actions/{envelope_id}/revoke
POST /agent-actions/{envelope_id}/execute
→ executor-only; all parameters come from the store, not the request body
Threat model
Baseline assumptions
The LLM agent and its tool invocations are untrusted for authorization purposes
The normalizer, policy engine, envelope store, executor, and credential broker are trusted components
The requester may be operating under prompt injection or plan drift
The approver is authenticated and can provide freshness or step-up context for high-risk actions
Target systems still enforce their own authorization independently of the approval check
Standard infra controls (TLS, service authentication, secret management, database access controls) are assumed to be in place. This model focuses on the proposal-to-execution binding
A note on risk
This table is not a checklist. Focus on preventing the highest-impact failures first. Detection and response are acceptable where prevention is impractical.
Phase 1: Proposal and normalization
Focus: Preventing the proposed action from diverging into a different effective action through input manipulation or cross-tenant substitution
Asset | Threat | Baseline Controls | Mitigation Options | Risk |
|---|---|---|---|---|
Envelope record | Plan drift: Agent presents an action for approval at T1, re-reasons between approval and execution due to prompt injection or new retrieval context, and submits mutated arguments at T3 | Envelope created at proposal time | 1. Executor fetches all parameters from the stored envelope; ignores any arguments supplied by the model at execution time 2. Short envelope expiry (5–30 min) bounds the drift window 3. Pre-execution state check on the target resource for high-impact operations | High |
Normalization layer | Alias bypass: Agent proposes | Tool schema validation | 1. Alias registry maps all known equivalents to canonical form before the envelope is created 2. Policy engine operates on the post-normalization canonical form, never on raw model output 3. Unknown parameter values are rejected at normalization time, before the envelope is created | Medium |
Approved parameters | Hidden parameter injection: Model includes optional flags ( | Envelope contains full parameters object | 1. Approval UI renders every field from the envelope's parameters object, not a model-generated summary 2. Tool schema marks parameters requiring explicit acknowledgment before they can be included in an envelope 3. Policy engine evaluates the full parameters object, including optional fields | Medium |
Tenant namespace | Cross-tenant substitution: Agent proposes an action targeting a resource in another tenant's namespace; if the executor derives the target from model output, it runs on the wrong tenant | Auth context available per request | 1. Envelope service derives 2. Policy engine verifies the proposed target is within the resource registry for 3. Executor re-verifies tenant match from the stored envelope before issuing scoped credentials | High |
Phase 2: Approval decision
Focus: Ensuring approval is granted by the right principal with deliberate intent, and that the approval record cannot be silently bypassed
Asset | Threat | Baseline Controls | Mitigation Options | Risk |
|---|---|---|---|---|
Approval decision | Self-approval: Requester and approver are the same principal, defeating independent review | Approval routing exists | 1. Hard constraint: 2. Policy engine excludes the requester from the candidate approver pool before dispatching the request 3. Alert when the eligible approver pool for a required action has fewer than two candidates | Medium |
Approver session | Stale session: High-risk approval granted from an hours-old browser session, without deliberate re-authentication | Session authentication required | 1. Require WebAuthn or step-up MFA bound to the specific 2. Maximum session age for approval: reject if the session is older than the policy window (typically 10–30 min for write actions) 3. Approval UI shows elapsed time since last authentication when the freshness limit is approaching | Medium |
Approval view | Approval phishing: Agent frames a destructive action as benign in conversational context; approver consents based on the framing rather than the envelope fields | Approval UI exists | 1. Approval UI renders exclusively from envelope fields; the agent's conversational context is not displayed on the approval screen 2. For destructive operations, require the approver to type the target or quantity before submitting 3. "Cannot be undone" label derived from the tool schema, not from the model's description of the action | Medium |
Phase 3: Execution and evidence
Focus: Ensuring the executor accepts the approved envelope once and emits complete evidence regardless of outcome
Asset | Threat | Baseline Controls | Mitigation Options | Risk |
|---|---|---|---|---|
Envelope state | Approval replay: A previously-consumed or expired envelope is presented again to trigger a second execution | Single-use flag on envelope | 1. Atomic compare-and-swap: 2. Mark the envelope consumed before the side effect, not after 3. Reject execution requests for envelopes in | High |
Claim transition | Concurrent consume race: Two executor instances receive the same | Compare-and-swap on consume | 1. Treat zero rows updated by the compare-and-swap as an immediate rejection 2. Reject execution requests for envelopes already in 3. Reconcile | Medium |
Execution authority | Authority overreach: Executor has reusable broad credentials and can perform actions beyond the approved envelope | Service authentication | 1. Target service fetches 2. For systems that cannot enforce envelope checks, issue a short-lived scoped token or assumed-role session bounded to the approved operation and target 3. Include | Medium |
Evidence log | Missing outcome: Execution fails or partially succeeds but no terminal evidence event is emitted, leaving the audit record ambiguous | Log infrastructure present | 1. Emit 2. Alert on 3. For partial executions, emit | Low |
If you use delegated automation approval
When a policy rule replaces the human approver, the threat profile shifts in a few ways:
Policy drift is the dominant risk: a rule that began as narrow gradually expands through incremental edits to cover a broader target set. Treat policy rule changes like production code: require review and audit before deployment
Alias bypass and hidden parameter injection carry more weight because no human reviews the envelope before execution. The normalization layer bears the full burden of keeping the effective action within the approved class
Automation approvals warrant shorter TTLs and tighter target constraints than human approvals
FAQs
Should every agent action require human approval?
No. Approval is slow and easy to rubber-stamp when it covers too many actions. Reserve it for irreversible operations and those that cross a trust boundary. Low-impact read operations are better covered by normal authorization and logging rather than an approval queue.
Is a chat confirmation enough?
Usually not. A chat "yes, do it" can work as a user experience, but it needs to produce a structured approval record behind the scenes. The executor requires a server-side approval record with a bound action_hash and a consumed flag. A transcript line cannot carry those guarantees on its own.
Verification checklist
Envelope and normalization
Every sensitive action converts to a canonical envelope before approval is requested
The approval UI renders
tool_id,operation,target,tenant_id, and every parameter from the stored envelope, not from a model-generated summaryThe same proposed action produces the same
parameters_hashandaction_hashacross app nodes and runtimesChanging
tool_id,operation,target,tenant_id, any parameter,normalizer_version,tool_schema_version, orexpires_atproduces a differentaction_hashIf
normalizer_versionortool_schema_versionis no longer active at execution time, execution is rejected and re-approval is requiredAny tool call matching no policy rule is denied, not forwarded to a human for improvised approval
Policy and approver selection
approver_id != actor_idis enforced at the store layer, not only in UI routingHigh-risk approvals require a fresh WebAuthn or step-up MFA context
Policy engine excludes the requester from the candidate approver pool before dispatching
Approval routing operates on the normalized envelope, not on raw model output
Approval lifecycle
Approvals expire on a short TTL by action class; the executor rejects expired envelopes
Pending and approved envelopes can be revoked before execution; revocation takes effect immediately
A revoked envelope fails the status check regardless of what the caller asserts
Approval store uses append-only state transitions: revocations are new records, not mutations to existing ones
Execution
Executor fetches the envelope from the server-side store by
envelope_id; it does not accept tool arguments from the model at execution timeExecutor recomputes
parameters_hashandaction_hashimmediately before execution; any mismatch is a security event, not a normal errorExecutor marks the envelope
consumedbefore the first non-idempotent side effectA second execution attempt on a
consumedenvelope is rejectedConcurrent execution requests for the same
envelope_idresult in exactly one accepted attempt; the others are rejected by the compare-and-swap
Evidence
Evidence log joins
envelope_id,actor_id,approved_by, and the outcome eventReconciliation alerts on
execution.claimedorexecution.startedevents with no terminal outcome after twice the envelope TTL
Implementation & Review
The full threat model matrix, architectural diagrams, and a printable verification checklist for this pattern are available in the Secure Patterns repository. Use these artifacts to guide your design reviews and internal audits.
