← All posts
AgentsReliabilityEngineering

The approval queue: how to put humans back in an agent's loop without slowing it down

Autonomous agents still need a human gate for risky actions. Here's how to design approval queues that catch the right 1% without turning into a bottleneck.

Every “autonomous” agent in production has a human somewhere in the loop — the question is whether you designed that gate on purpose or discovered it during an incident. Teams that skip this step ship an agent that can refund $4 or $40,000 with the same confidence. The fix isn’t “add a human review step” in the abstract — it’s building an approval queue with the same rigor you’d apply to any other piece of production infrastructure: clear triggers, a fast reviewer UX, and a feedback path back into the agent.

Decide what actually needs a gate

Not every action deserves a human. If you route everything through approval, reviewers drown in low-stakes requests and start rubber-stamping — which is worse than no gate at all, because it gives you false confidence. Gate on consequence, not on category:

Everything else should execute without a human — that’s the entire point of building the agent. The approval queue exists for the tail, not the median case.

Design the queue like a production system

An approval queue is a piece of production infrastructure, not a Slack channel someone checks “when they get a chance.” It needs:

type ApprovalRequest = {
  id: string;
  action: string;          // "refund_order"
  payload: Record<string, unknown>;
  riskReason: string;       // "amount > $500 threshold"
  requestedAt: string;
  expiresAt: string;        // default-safe cutoff
  status: "pending" | "approved" | "rejected" | "expired";
};

Route by risk, not by role

A common mistake: every gated action goes to the same reviewer queue regardless of domain. A finance approval and a content-moderation approval need different reviewers with different context — route by the type of risk, and let each queue develop its own fast, informed reviewers instead of one generalist team guessing at every domain.

flowchart LR
    A[Agent proposes action] --> B{Risk check}
    B -->|low risk| C[Execute]
    B -->|high risk| D[Approval queue]
    D -->|approved| C
    D -->|rejected| E[Agent notified, replans]
    D -->|expired| F[Default-safe: no action]

Close the loop back into the agent

The queue shouldn’t be a dead end. Every rejection is a labeled training signal: feed it back into your eval set so the agent’s confidence model learns what “looked fine to the model, got rejected by a human” looks like. Over time this should shrink the gated surface — not because you loosened the rules, but because the agent got better at knowing which 1% to flag in the first place. If your approval rate stays flat for months, either the agent isn’t learning from rejections, or the threshold was wrong from day one.

The measure that matters

Track two numbers, not one: time-to-approval (is the gate usable?) and false-gate rate — the share of approved requests that a human waved through without real scrutiny. A gate that’s always rubber-stamped isn’t a safety control, it’s latency with no benefit. Tune the threshold until both numbers look honest, and revisit it every time you ship a new action the agent can take.

Want something like this built for your team?

Get a quote →