Reply approvals — the supervisor review gate for the inbox
Reply approvals turn an agent’s outgoing reply into a supervised decision instead of an immediate send. An agent whose approval requirement is on composes a reply as usual; Orbit holds the proposed text in a queue instead of dispatching it, and a supervisor — or any owner/admin — approves it or rejects it with a reason. Approving dispatches the exact queued body through the same channel path a direct reply would take; rejecting sends nothing and tells the agent why, so they revise and resubmit. The gate and the queue live under Inbox → Pending replies (supervisors) and Inbox → Settings → Reply approvals (who is gated). The API surface mirrors both endpoints and is documented here in full.Who the gate is for
Use the gate where a reply must be reviewed before it leaves the workspace:- Junior-agent onboarding. A new or training agent answers with real customers from day one, and a supervisor clears every reply until the required competence to send directly is proven. Turn the gate off for that agent when the review stops adding value.
- Regulated or high-stakes queues. Teams answering financial, legal, or complaint-handling conversations often run a four-eyes rule — two people touch every customer-facing message — as a customer control. Orbit supplies the control; whether it applies is the tenant’s call, default off.
When approvals are required (resolution rules)
The requirement resolves in two levels, checked every time a reply is submitted:- Per-agent override — the requirement set for that specific user wins over everything below.
- Org-wide default —
require_reply_approval_defaultunder the org’s inbox settings; the fallback for every agent with no override. Default is off.
gated: true and lands in the queue.
The workspace owner is never gated. Routing the sole administrator’s own replies through an approval queue would strand them behind their own gate, so the server always dispatches owner’s replies directly — enabling an override for an owner is refused, and clearing a stale one is always allowed.
Submit a reply through the gate
POST /api/v1/inbox/replies/queue — any authenticated full-seat agent may call it.
202 ACCEPTED — the call is accepted either way:
- Gated.
{"gated": true, "approval_id": "repla_...", "status": "pending"}— the reply is queued and waits for a supervisor. - Not gated.
{"gated": false, "dispatched": {...}}— the reply was dispatched immediately through the standard reply path, and carries the dispatched message payload.
conversation_id and body are required; channel, from, media_url, metadata are optional. Email replies additionally accept subject, cc, and bcc, and those ride with the queue row so an approved email reply ships with the operator-entered headers instead of a blank subject.
Work the pending queue
List, approve, and reject are restricted to owners, admins, and supervisors — the approval charter. List pending rows:conversation_id, requested_by_user_id, body, channel fields, and status. A workspace where the approvals table isn’t provisioned yet gets an empty list, not an error.
Approve:
approved with the dispatched message id stamped on it.
Reject:
rejected with an optional reason (up to 1,000 characters) the agent can read as review feedback.
The dashboard renders the same list under Inbox → Pending replies: each card shows the composing agent, conversation, channel, and proposed body, with approve and reject actions inline. Rejecting opens a reason dialog.
Manage who is gated
Read the effective setting. Any workspace member can read the resolution for themselves — the composer uses it to decide whether the send goes through the queue:agent_overrides map — the full per-agent override table — only ships to supervisor roles; other callers get back their own override and the org default.
Set an agent’s requirement. Owners, admins, and supervisors may pin or unpin an agent:
true on the workspace owner returns 422 — the owner exemption; clearing a stale override (false) always succeeds.
Error contract
A 422 from a parse failure carries the field-level issue list, so a client surfaces “body is too long” rather than an opaque rejection. If dispatch fails when the supervisor approves — the provider is down, the conversation closed, a send-time validation trips — the approve request errors with the dispatch reason and the row stays
pending, so the supervisor can retry after the cause is fixed. Nothing is half-approved.
On reject, the reason is optional; an empty reject works, but the agent reviewing their rejected queue row only sees feedback when one is supplied.
Audit trail
Every gate decision writes an audit event against the org: the queued submission (inbox.reply.queued), the not-gated bypass (inbox.reply.queue.bypassed), the supervisor decision (inbox.reply.approved or inbox.reply.rejected), and every settings change (inbox.reply.settings.updated). Each event carries the acting user, the conversation or approval id it touched, and the extra detail (the reject reason, the dispatched message id, the new setting value).
The queue row itself keeps the record too: requested_by_user_id and decided_by_user_id name both sides of the four-eyes pair, decided_at timestamps the decision, reject_reason holds the feedback, and dispatched_message_id points to the message that actually went out. That pair — submitter and decider — is the controlled record an audit or QA review reads.
See also
- Inbox setup — channels, assignment, and the workspace the gate operates inside.
- Per-digital-queue SLA escalation policies — the queue-level alerting counterpart on the inbox tree.
- Agent assist whisper coaching — the other supervisor review surface; coaching instructs live, reply approvals gate outbound text.
- Conversations API — the direct reply path a not-gated submission falls back to.