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.
- 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
alwayson 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_FAILEDrather than slipping through ungated.
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 (
pending→executing), 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 atdonewith its result; on a thrown executor it ends atfailedwith 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_EXPIREDand 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.
- 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.
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.
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-useX-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: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:
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.
7. Related troubleshooting surfaces
If an approval surfaces as aPENDING_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
- Orby operator assistant architecture — the session, thread, and tool-registry model this gate plugs into.
- Destructive-operation re-auth challenge — the session-recency and step-up gates for settings/identity mutations.
- Orby Assistant API — the approve/reject/poll endpoint contract the dashboard calls.
- Using Orby in the dashboard — the operator workflow on top of the gate.
- Settings endpoints — the Admin Panel “Approval requirements” read/write contract.