Skip to main content

Action approvals: the human-in-the-loop gate

Large parts of the Orby operator assistant are read-only and harmless. The danger part is its tools: Orby can send a message, reassign an inbox conversation, pause a campaign, or scaffold a draft agent. When a turn calls one of those tools, the call does not execute. It raises a pending action — a durable, auditable proposal with the exact arguments and an estimated cost — and a person must approve it before the tool runs. That gate is the action-approval model; this page explains its rules so you can reason about any proposal Orby raises, now or after new tools are registered.

1. What an approval gate is

Every Orby tool declares a confirmation policy, and the gate consults three facts before it lets any tool execute:
  • Policy never. The tool runs inline in the turn: reads, searches, summaries, drafts, cost previews. No proposal is created.
  • Policy always. The tool pauses at a proposal. A pending action is created and nothing executes until an operator approves it. This is the policy assigned to chargeable and side-effecting tools — message sends, conversation reassignment, campaign state changes.
  • Policy threshold. The tool pauses only when its estimated cost meets or exceeds a dollar threshold. A chargeable tool’s own cost estimator runs before the gate consults the amount, so the card always quotes the real price it is asking you to approve.
Two additional gate sources sit on top of the declared policy, as defense-in-depth:
  • Destructive verb names. A tool whose name contains a destructive verb — delete, remove, release, revoke, cancel, purge, drop, refund, charge, disable — is gated regardless of its declared policy, so a forgotten always on a dangerous verb cannot bypass review.
  • Fail-closed persistence. A proposal that cannot be recorded never executes. The tool is refused outright with PENDING_ACTION_PERSIST_FAILED rather than slipping through ungated.
A proposal is never “whatever the model meant.” The pending action records the tool name, the exact validated arguments, the proposing operator, the estimated cost, and a deterministic idempotency key. What you approve is that concrete payload, and the audit trail keeps it.

2. Proposal lifecycle: pending → approved / rejected / expired

A pending action has one of six states: pending, executing, done, cancelled, undone, and failed. The gate transitions a proposal along one of three paths:
  • Approve. The approve call atomically claims the row (pendingexecuting), re-checks ownership, re-validates the persisted arguments against the tool’s input schema, re-checks the operator’s role at decision time rather than proposal time, and then runs the executor. On success the row ends at done with its result; on a thrown executor it ends at failed with an error code.
  • Reject. The reject call atomically marks the row cancelled. The tool never runs. Cancellation is terminal — a later approve on the same row is refused.
  • Expire. A proposal’s window is five minutes. When the window elapses without a decision, the row is cancelled with PENDING_ACTION_EXPIRED and nothing executes. The expiry exists so a stale proposal cannot be approved after its context has moved — and so an abandoned browser tab never leaves a “ready to fire” row hanging for hours.
Two terminal-state rules make the gate safe for retries and races:
  • Single resolution. Only the first approve or reject that claims the row wins. A second approval attempt, or two operators racing the same card, is refused with PENDING_ACTION_RACE / PENDING_ACTION_NOT_APPROVABLE, so an action can never run twice.
  • Delayed-fire window. Approved destructive tools can carry a short undo window (~30 seconds) before the executor fires, recorded on the same row; cancelled by the operator inside that window the row ends undone.
The full error-code vocabulary and its recovery playbooks live in Troubleshooting: Orby pending-action approval.

3. Actor model: who requests, who decides

The approval gate is deliberately asymmetric — the act of proposing is open to the assistant, the act of executing is reserved for the operator. Two structural properties are worth internalizing:
  • The gate re-grounds on approval. The tool arguments are re-read and re-validated at decision time. If the conversation you were reassigning was already moved, or the persisted args no longer match the tool’s schema, the approval refuses to run on stale input — it does not act on a snapshot of the past.
  • The gate pins the identity, not the session. The proposing operator’s user id is recorded on the row, and the decision routes re-load that id at decision time. A delegated or leaked credential cannot decide someone else’s proposal.

4. The durable event trail

Orby does not dispatch platform webhooks for gate transitions — the gate is an operator-facing surface, not a customer-facing event plane. What it does emit is a triple-sink record on every transition:
  • An audit-log row (orby.tool_action.approved / orby.tool_action.rejected / orby.tool_action.expired) naming the tool, the operator, the tenant, the surface (chat intercept vs explicit button), and the approximate IP/user-agent of the decision.
  • The row itself in-place, with terminal status, executed/cancelled timestamps, and a sanitized result/error payload — so the row is the second sink an auditor can read directly.
  • A sanitized executor result persisted on the row, so what the approval report says is what the audit trail will replay.
Reach the audit log from your workspace’s audit surface or via the event ledger; the row stays queryable through GET /api/v1/orby/tool-actions/:id — the poll surface the dashboard’s confirmation card uses to refresh the countdown and the result.

5. Where this differs from the re-auth challenge

Two distinct human-gates exist in Orbit and they are easy to confuse. The destructive-operation re-auth challenge gates irreversible settings and identity mutations — workspace deletion, GDPR erasure, HIPAA disable, 2FA changes, conversation force-transfer — by demanding a fresh single-use X-Reauth-Challenge token minted on a session-recency or step-up namespace within the last five minutes. The action-approval model instead gates agent- and inbox-driven operations — the tools Orby, or in some deployments other agent runtimes, can invoke — by raising a durable pending row whose decision is a human click inside the operator surface. They differ in three ways: A tool can of course sit behind both gates — a proposal to disable HIPAA would still have to mint the re-auth challenge before its executor runs — but the gates exist for different reasons and fail in different ways.

6. Configure which actions require approval

The gate’s master posture is opt-in per organization, kept in the Admin Panel under “Approval requirements.” Read or replace it with:
The response carries enabled (the master switch, default off) and require_approval_for (the subset of known high-risk action types that must pause for approval). Update it with:
The body is strict — unknown keys are rejected, unknown action types are dropped server-side, and the list is canonically re-ordered — so a hand-edited config blob can never widen the gate beyond the known types. The known types, in canonical order: While the workflow is disabled, or the action type is not listed, the gate is inert and the operation executes without review — the fail-open default. Turning the feature on never surprises an org that has not opted into approval review. If an approval surfaces as a PENDING_ACTION_* error, jump directly to Troubleshooting: Orby pending-action approval for the code table and recovery playbook. If the failure is an authorization or session rejection on the same Orby panel, see Orby operator errors.

See also