Depute Logo

Binding Approval

Cryptographic intent gate with an isSigning state.

Install

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

Overview

Unlike a standard ApprovalGate used for day-to-day coordination, BindingApproval provides a heavy-duty friction boundary for irreversible, legally binding, or financial actions. It forces explicit acknowledgment of terms and implements a multi-phase isSigning visual state.

Wire Transfer Authorization

Under Review

Agent requests authorization to execute a $5,000 wire transfer.

Terms (0/2 acknowledged)
Signing asalice@company.com
Binding approval: Wire Transfer Authorization. Status: Under Review.
Interactive StorybookView all states, toggle props, and test edge cases.

Basic Usage

<BindingApproval
  title="Wire Transfer Authorization"
  description="Execute a $5,000 wire transfer."
  impactStatement="$5,000.00 will be transferred immediately."
  signerIdentity="alice@company.com"
  terms={[
    { id: 't1', text: 'I authorize the transfer of $5,000.00', acknowledged: false },
    { id: 't2', text: 'I understand this action is irreversible', acknowledged: false },
  ]}
  onSign={() => initiateSignature()}
/>

Solution Patterns

This primitive replaces ApprovalGate in high-stakes nodes. The internal state expects a clear distinction between reviewing and signing, where isSigning: true disables interaction and renders a deterministic loading state while waiting for the protocol layer to secure the ledger / signature.

Props

PropTypeDefaultDescription
titlestringTitle of the binding action
descriptionstringDetailed explanation of the action
impactStatementstringEstimated impact (e.g., "$5,000 will be transferred")
status'reviewing' | 'signing' | 'signed' | 'rejected' | 'expired''reviewing'Current status
termsBindingTerm[]Terms that must be acknowledged
requireAllTermsbooleantrueRequire all terms acknowledged to sign
isSigningbooleanfalseToggles the active signing visual state
signerIdentitystringIdentity of the approver
timeoutSecondsnumberSigning window timeout
onSign() => voidCalled when approved/signed
onReject(reason?: string) => voidCalled when rejected

Design rationale

When to use ApprovalGate vs BindingApproval:

ApprovalGateBindingApproval
Use whenThe action is reversible, operational, or low-to-moderate stakesThe action creates a legal, financial, or cryptographic record
Friction levelQuick scoped grant — review and approveMandatory term acknowledgment + signing flow
State modelpending → approved / rejectedreviewing → signing → signed / rejected
Examples"Send this email", "Deploy to staging", "Run this query""Wire $50k", "Sign this contract", "Commit to ledger"

Use ApprovalGate by default. Escalate to BindingApproval only when the action is irreversible and the user's identity must be cryptographically bound to the decision.

Why isSigning? Normal approvals execute instantly. Cryptographic/financial approvals often require a ledger transaction, hardware wallet interaction, or passkey challenge that takes time. isSigning freezes the component deterministically to prevent duplicate submissions.

Why explicit terms? Click-fatigue trains users to blindly click "Approve." Forcing the user to manually acknowledge explicit terms (e.g. "This action cannot be undone") breaks automatic behavior and captures verifiable intent.

On this page