The ACD queue model
Inbound voice and digital conversations reach a human through the same machinery: an ACD (Automatic Call Distribution) queue. A queue is a named pool of waiting work items — PSTN calls in a voice queue (/api/v1/voice/queues), chat/email/social conversations in a digital queue (/api/v1/inbox/digital-queues) — and a membership roster of the agents allowed to take them. Every tenant-owned control on a queue — SLA target, overflow action, skill requirements, routing strategy — is part of the same small model. This page explains that model once so the voice and digital operator guides are thin procedure surfaces on top of it.
Read this before you wire a flow queue node, a DID’s routing config, or an inbox queue into production. The step-by-step setup lives on the voice queues guide and the omnichannel queue routing guide; this page covers what the underlying machine guarantees, not which buttons to click. The SLA-breach and escalation pipeline (the queue’s service-level objective) is a sibling concept page — read Per-queue SLA breach alerting and escalation policies after this one.
Inbound routing decides where a conversation lands — a flow node, a DID config, or the inbox routing-rule evaluator. ACD queue routing decides who answers it, exhausted from a FIFO waiting list plus a member-eligibility filter. Outbound MT voice and SMS exit only through the Devotel wholesale softswitch — queue controls are inbound, tenant-owned configuration on /voice/queues and /inbox/digital-queues, never a provider-side or carrier-side signalling change.
1. What an ACD queue is — voice vs. digital vs. inbox routing rules
Three systems share the word “queue”:
The voice queue covers PSTN and in-app call legs. The digital queue covers every inbox channel (chat, email, WhatsApp, SMS, RCS, social, the APAC OTT channels). The inbox routing-rule evaluator sits in front of both and decides which digital queue — or, if no queue matches, which direct-assigned user — a conversation even enters. Both voice and digital queues share the same five-state presence model and the same dispatch machinery, so one blended agent handles both channels; for a comparison column-by-column, the omnichannel queue routing guide spells out where the two shapes differ and where they converge on one supervisor surface.
2. The agent presence state machine
An agent’s membership row on a queue carriesacd_queue_members.state — five values, one at a time:
available— eligible for dispatch right nowbusy— on a live assignmentwrapup— post-call window; protected from new dispatch while the disposition screen stays openpaused— an auxiliary (AUX) code hold, “be back in 10 minutes”offline— not on shift
Dispatch eligibility at scan time is a hard gate:
state='available' and nothing else. A busy agent is mid-assignment; a wrapup agent is in their post-call window (the dispatcher skips them on purpose, so a waiting caller cannot interrupt the disposition screen); a paused agent has said “hold off”; an offline agent is not on shift. “Eligible” means available + a member of the queue + the member’s skills cover the queue’s requirements. POST /api/v1/voice/agents/{id}/status is the toggle surface the agent UI uses; an agent who needs more post-call time extends their own wrap-up window with POST /api/v1/voice/agents/me/extend-wrapup (bounded per window, audit-logged per extension).
Omnichannel-capacity scoring blends in beside the gate: an agent available on voice but saturated on open digital conversations gets skipped by the blended loadScore that voice-queue routing, digital-queue routing, and the omnichannel work-item router all consume read-only. The capacity picture is a suggestion the dispatcher reads; the state gate is the rule.
3. Queue entry lifecycle — enqueue, dispatch scan, overflow
A work item enters a queue at enqueue and leaves either successfully (an eligible agent took it) or at an overflow action (its SLA window expired). Voice queue entry.POST /api/v1/voice/queues/{id}/enqueue creates a row in acd_call_queue_entries with a FIFO position and an optional call-level requiredSkills. Lower priority wins; a supervisor-set bump to priority=0 re-scores the caller’s spot without resetting the wait clock.
Digital queue entry. Inbox routing rules (or a direct-assigned fallback) nominate the queue; the conversation is stamped onto a Redis waiting set, oldest first. Digital queue entries carry the conversation’s contact attributes and the channel’s identity, not a telephony id.
FIFO dispatch walk. On every tick the dispatcher scans one queue:
- Filter members by
state='available', queueskills, and the queue’sminSkillLevelrequirement (next section). - For a digital item, also subtract saturated agents by the blended
loadScore; a candidate walk that finds nobody eligible stops and the item keeps waiting. - Walk waiting entries in FIFO order, attempt the same eligibility filter per entry on its per-item
requiredSkills. - Assign the earliest entry that passes — a dispatched voice entry moves the member to
busy, a dispatched digital entry does the same with the conversation reserved. - If a member ignores the ring within the queue’s
rnaTimeoutSeconds(default 30s), the dispatcher re-dispatches to the next eligible member.
The
emergencyActive bypass (voice queue, per voice queues guide) skips the queue’s whole model: new callers route to a fixed E.164 destination on the next ring, independent of state or FIFO.
4. SLA and overflow — max_wait_seconds vs sla_seconds
Two different deadlines get conflated:
maxWaitSeconds(voice queues, default 300s, bounds 30–3600) — the caller-visible deadline. At expiry the caller is removed from FIFO and routed through the queue’soverflowAction. The queue’stargetServiceLevelSecondsis a separate reporting number the queue uses to compare its SLA % against — the SLA forecast, on thequeue-sla-forecast-callbacksurface, pairs both against real traffic.sla_seconds(digital queues) — the service-level deadline each waiting conversation is scored against on the queue’s waiting set. A scheduler pass walks the waiting set for items whose deadline has passed and re-routes each: assign if an agent freed up, otherwise apply the queue’s overflow action (routing_rules,overflow_queue,auto_responder,hold). The queue’s per-deadline SLA breach policy is a sibling set of configuration — read Per-queue SLA breach alerting and escalation policies for the full ladder (objective → forecast → breach → escalation) and how the breach policy chains with the queue’s overflow.
5. Skills and eligibility filtering
Skills are queue-scoped minimum plus member-scoped coverage:queue.skills— the tags the queue as a whole wants (e.g.["english", "billing"]). A member with any skill tag is eligible only if the member’s own list intersects — or the member carries an empty skill list, which is the wildcard “any skill” membership.queue.minSkillLevel/ per-callrequiredSkills— the per-call filter the dispatcher intersects at scan time. A bilingual queue can requirespanishon the queue’sskillswithout restricting member coverage; an escalation queue can require a seniorsupporttag at enqueue without re-writing the queue.member.skillLevels(1–5 per skill) — intersects the queue’sminSkillLevelrequirement. A senior agent who needsbillingrated 3+ matches; the same agent who’s a wildcard member does not have to be rated.
requiredSkills value (too narrow, or misspelled) blocks all eligible members from picking the oldest entry; the dispatcher keeps walking FIFO. Verify the filter is the gate by reading queue entry attributes, not by summing member availability.
6. Reading queue state
Three surfaces, hundreds of times a day:- Live snapshot per queue —
GET /api/v1/voice/queues/{id}/stats(voice) andGET /api/v1/inbox/digital-queues/stats/GET /api/v1/inbox/digital-queues/{queueId}/stats(digital). Current depth, longest-wait, available/busy, occupancy, plus the queue’s overflow counters on digital. TheliveSSE variant on voice keeps a supervisor’s wallboard in sync tick-by-tick; the digital queue stats are the tenant-wide read surface. - Windowed analytics —
GET /api/v1/voice/queues/{id}/analytics?from=...&to=...&interval=30m|1h|1d(voice) returns per-bucketsla_pct,aht_seconds,asa_seconds,abandon_pct,occupancy_pctplus a First-Call-Resolution heuristic. The response is volume-weighted on the backend — read the returned summary, do not average buckets. Two sibling surfaces cover routing decisions and skill breakdown:GET /voice/queues/{id}/comparisonandGET /digital-queues/{queueId}/decisions. - Routing decision audit —
GET /api/v1/inbox/digital-queues/{queueId}/decisions(and per-conversationGET /api/v1/inbox/routing-decisions/{conversationId}) is the “why did it land here” read. Every decision the dispatcher made — skills-filter reason, blended-load score, the queue entry it routed — is persisted in the audit trail; trace a stuck conversation from the queue or from the conversation id, not from member availability counts.
GET /api/v1/voice/supervisor/omnichannel-queue) blends both queue shapes into one position-ordered list — the same ordering the dispatcher assigns by.
Once the model is clear, the voice queues guide walks the full setup sequence and the omnichannel queue routing guide covers the digital-queue/digital-ACD side without re-teaching the state machine.