Skip to main content

Pending replies queue — the supervisor workflow from login to decision

Inbox → Pending replies is the single queue every gated reply funnels through. When the reply-approval gate is on for an agent, that agent’s composed reply is held here instead of sending, and a supervisor — anyone in the owner, admin, or supervisor seat — turns each held reply into a decision: approve it and it dispatches, or reject it and it goes back to the agent with feedback. This page walks the queue from login to decision; the gate mechanics and API surface live in Reply approvals, and the operator walkthrough of the same surface lives in Pending replies guide.

What the pending-replies queue is

The queue is the supervisor view of the reply-approval gate. An agent whose gate is on submits a reply as usual; Orbit holds the proposed body as a pending row and returns gated: true to the composer. Every gated reply across the workspace lands in this one shared list, so a supervisor never hunts per-agent queues — the whole backlog of sign-off decisions is one page. The queue is deliberately narrow in scope. It decides outbound text, nothing else: conversation assignment, SLA clocks, and routing live in the standard Inbox list. What the queue adds on top of that list is the gated sign-off — a second named person between the composed reply and the customer. Open it from the dashboard sidebar under Inbox → Pending replies. The page is restricted to owners, admins, and supervisors; an agent without one of those seats never reaches it, and the server enforces the same role set on every call.

Sort and filtering — oldest first, per agent, per channel

The queue reads oldest first. The server returns pending rows in submission order, so the row at the top of the page is always the reply that has been waiting longest — work the queue top-down and the backlog drains in arrival order. Screenshot: queue header, oldest-first rows. Per agent. The queue is shared across all gated agents, and each row names its requester by display name resolved from the team roster. When several agents are gated, scan the requester column and work a specific agent’s submissions as a group — clearing one junior agent’s queue end-to-end is how a coaching session concludes. To narrow the backlog to a single conversation instead, the API lists pending rows filtered by conversation_id (see the Reply approvals API). Per channel. Every row carries a channel badge — SMS, MMS, WhatsApp, Email, RCS, Viber, Instagram, Messenger, LINE, Telegram, Apple Messages, or fax — so the shaping decision (“is this phrasing right for SMS length”) is visible before you read the body. The badge reads off the channel the reply will dispatch on, which is the same channel an approval sends through. Long queues page honestly: the server returns up to 50 pending rows, the page mounts the first 10, and Load more grows the window with a “Showing N of M” counter. Screenshot: Load more control with the visible-row counter.

Per-row actions — approve and reject, and what each writes

Each row carries two actions, and both write a permanent decision record. Approve & send. One click, no dialog — the decision gate was reading the body. The exact queued body dispatches through the same channel path a direct reply would take: an SMS goes out as SMS, an email goes out with the subject and cc/bcc the agent entered, a WhatsApp reply posts to the thread. The row is marked approved, stamped with the deciding user, the decision timestamp, and the id of the dispatched message — the audit trail points from the decision to the message the customer actually received. Screenshot: a row’s Approve & send button. If dispatch itself fails — the conversation closed, the provider is down, a send-time validation trips — the approval errors and the row stays pending. A failed approve never half-sends: fix the cause and retry, or reject the row instead. Reject. Opens a dialog with an optional reason, up to 1,000 characters. Nothing is dispatched; the row is marked rejected with the deciding user and timestamp, and the reason rides on the row as the feedback the agent reads when they review their rejected submission. Leave a reason — “Offer the discount code instead of a refund” is actionable; a blank reject is a dead end for the agent. A failed reject surfaces inline inside the dialog, not only in the toast, so your typed reason survives a retry. Screenshot: reject dialog with the reason field.

Reading the queue row

Every row is a complete decision record, before and after the decision:
  • Submitter — the composing agent’s display name (requested_by_user_id on the wire), resolved against the team roster so you read a name, not an id.
  • Proposed body — the exact text you approve or reject, rendered verbatim. Read it, not just the metadata line.
  • Age — how long ago the reply was submitted, rendered relatively (just now, 12m ago, 3h ago) until thirty days, then as an absolute date.
  • Decider and decision timestamp — once decided, the row records who decided (decided_by_user_id) and when (decided_at). The pair of submitter and decider is the controlled four-eyes record a QA review or audit reads.
  • Dispatched message link — on an approved row, dispatched_message_id points at the message that went out, so an auditor follows the decision through to the actual send. The Open thread link on the card takes you to the full conversation, which is how you review the customer’s earlier messages before deciding.
Decisions stay recorded on the row: the queue shrinks to pending-only, but the audit events (inbox.reply.queued, inbox.reply.approved, inbox.reply.rejected) and the row’s own fields keep both sides of every decision retrievable.

Bulk policies — deciding in batch

The queue processes one decision per action — there is deliberately no “approve all” button where a signature is a decision on outbound text. Batch work is still the normal case:
  • Read the batch, then decide the batch. Open the page, read top-down oldest-first, group by requester when one agent dominates the backlog, and run the decisions as a sweep. Approve is a single click per row, so a clean queue clears in seconds.
  • Rejections cluster. When a coaching pattern shows up — say, an agent offering refunds against policy — reject the affected rows in one sitting with the same corrective reason, so the agent gets consistent feedback rather than a mixed bag.
  • A second reviewer never tramples you. Both actions reject a row already decided (422 INVALID_STATE), so two supervisors working the same queue concurrently fail safe: the second decision attempt errors instead of overwriting the first.
  • Size your sweep to the window. The server caps the list at 50 pending rows; if the queue routinely saturates that cap, the bottleneck is review throughput, not the queue — gate fewer agents or add reviewer seats, and use Load more to keep the page responsive while you work it down.

The four-eyes rule in this queue

The gate is the four-eyes control for outbound text: two named people touch every customer-facing reply from a gated agent — the composing agent submits, a supervisor decides. In this queue’s context the rule means one thing concretely: the submitting agent must not be the decider.
  • The privilege split is structural: any full-seat agent can submit, but only owner, admin, and supervisor seats can list, approve, or reject. An agent who holds only the composer role can’t reach the queue at all — the page guard blocks it and the server returns 403 on the endpoints — so a gated agent can’t approve their own request even through the API.
  • The row records both sides: requested_by_user_id and decided_by_user_id name the pair, and that pair is exactly what a QA review or audit checks for self-approval.
  • The workspace owner is never gated — routing the sole administrator’s replies through an approval queue would strand them behind their own gate, so owner’s replies always dispatch directly. Four-eyes applies to gated agents, never to the owner.
Whether to run the gate at all, and on which agents, is a tenant-owned control: the per-agent override under Inbox → Settings → Reply approvals wins over the org default, and the org default require_reply_approval_default is off until you turn it on. Many teams apply it during junior onboarding, then lift it per agent once sending directly is proven; complaint-handling and regulated queues often keep it on as a standing policy.

Where the queue intersects AI deflection

The reply-approval gate and the AI co-pilot are two separate review surfaces that meet in the composer: the co-pilot decides what gets drafted, the gate decides whose reply needs sign-off.
  • Drafts are proposals. When the co-pilot is enabled, it drafts a suggested reply for each inbound message, and the draft sits above the composer with Accept, Edit, and Discard. Nothing ships until a person acts on the draft — every draft carries require_human_review = true.
  • The interactive path ends in this queue. A gated agent who accepts or edits a draft and hits send submits through the gate — the draft’s body becomes a pending row here, and only a supervisor’s approval turns it into a resolution. Drafts become resolutions through this queue on the interactive path; autonomy is a workspace policy decision, and the gate keeps the human checkpoint when it’s on.
  • The two gates compose. An agent can be gated while the co-pilot’s auto-approve confidence threshold qualifies a draft for one-click acceptance — one-click acceptance still routes through the reply gate, so the supervisor queue stays the last checkpoint. Only send-shaped co-pilot suggestions are ever one-click eligible, and that eligibility applies to the composer’s own accept flow, not to the supervisor decision here, which is always a deliberate approval click.
If your workspace runs both, the operating picture is: the co-pilot raises draft quality, the gate keeps a signature on gated agents’ sends, and this queue is where those two reviews meet. Screenshot: a pending row whose body originated as an accepted AI draft.

See also