Depute Logo

Approval Gate

Block execution and ask the user to approve a risky or high-stakes agent action.

Install

npx ax-depute@latest add approval-gate
pnpm dlx ax-depute@latest add approval-gate
yarn dlx ax-depute@latest add approval-gate
bunx ax-depute@latest add approval-gate

Overview

Approval Gate is the trust boundary primitive. It pauses agent execution and surfaces a structured approval UI before the agent crosses a trust boundary — sending emails, writing to a database, calling an external API.

The key design principle (from Stripe's SPT pattern): approvals are scoped grants, not binary yes/no decisions. A user approving "send emails" shouldn't be approving "send unlimited emails forever."

Send external email

Approval Required

The agent will send a follow-up email to 3 recipients.

Agent Reasoning
The task requires notifying stakeholders of the completed analysis.
88% confidence
Interactive StorybookView all states, toggle props, and test edge cases.

Basic usage

<ApprovalGate
  title="Send email to customer"
  description="The agent will send a follow-up to 3 recipients."
  agentReasoning="Task requires notifying stakeholders of completed analysis."
  status="pending"
  onApprove={() => agent.continue()}
  onReject={() => agent.abort()}
/>

With scoped grant

<ApprovalGate
  title="Charge payment method"
  description="The agent will charge $49.00 to the customer's card on file."
  agentReasoning="Subscription renewal detected. Auto-charge is enabled for this account."
  mode="simple"
  status="pending"
  scope={{
    target: 'Stripe API',
    resourceLimit: 4900,     // in cents
    durationSeconds: 300,    // expires in 5 minutes
  }}
  onApprove={() => agent.continue()}
  onReject={() => agent.abort()}
/>

Staged mode (Preview → Confirm → Execute)

For high-risk multi-step actions, use mode="staged" to walk the user through three phases before execution:

<ApprovalGate
  title="Deploy to production"
  description="The agent will deploy the current build to the production environment."
  agentReasoning="All tests passed. Deployment window is clear."
  mode="staged"
  status="pending"
  onApprove={() => agent.deploy()}
  onReject={() => agent.cancelDeploy()}
/>

Props

PropTypeDefaultDescription
titlestringShort label for the action being approved
descriptionstringHuman-readable explanation of what the agent will do
agentReasoningstringWhy the agent thinks this action is necessary
mode'simple' | 'staged''simple'Staged adds Preview → Confirm → Execute steps
status'pending' | 'approved' | 'rejected''pending'Controls gate state
scopeApprovalScopeOptional scoped grant (target, resourceLimit, durationSeconds)
onApprove() => voidCalled when user approves
onReject() => voidCalled when user rejects

Composition flow

Approval Gate usually sits immediately after a Plan Card and before Run Controls:

Plan Card → [Approval Gate] → Run Controls → Tool Trace

It pauses execution and requires a human cryptographic signature (or explicit click) to proceed.

Design rationale

Why scope? Binary approve/reject is the wrong mental model for agents. Stripe never approves an action with a simple yes/no — they issue capability grants scoped to a specific target, resource limit, and time window.

Why mode="staged"? Agent flows are state machines, not request-response cycles. staged mode maps to Stripe's Authorization Chain pattern: Intent → Plan → Permission Check → Execution → Audit. Each phase can fail or pause for human input.

On this page