Skip to main content
A voice queue is an ACD (Automatic Call Distribution) target: inbound callers wait in FIFO order, the platform dispatches each caller to an eligible available agent, and everything about the queue — depth, wait times, SLA, occupancy, dispositions — is observable over the API. This guide walks the full operator loop: create the queue, enroll members, control priority, verify staffing, watch health, run an emergency cutover, record wrap-up codes, and wire alerting. Inbound routing to a queue is usually the flow’s queue node or a DID’s routing config; this page picks up where that traversal ends. Base path: /api/v1/voice Authentication: Clerk session (Authorization: Bearer <token>) or API key (X-API-Key). Scope: voice write for management; voice read for monitoring.

1. Create the queue

POST /api/v1/voice/queues creates the queue and returns its id (an opaque queue_* string every subsequent call uses). Every queue setting is tenant-owned: you choose the SLA target, the overflow behavior, hold-music and announcements, the skills a caller must match, and the routing strategy.
cURL
Key fields you set at creation (all optional except name; defaults noted):
  • skills — the skill tags the queue requires. A call’s requiredSkills (set at enqueue) only dispatches to members whose own skills cover them; leave empty to accept any member.
  • overflowAction — what happens when a caller exceeds maxWaitSeconds (default 300s, bounds 30–3600). Choices: voicemail (default), childQueue (fails over to a designated queue), or hangup.
  • targetServiceLevelSeconds — SLA numerator for the live dashboard’s Service Level metric (default 20s, bounds 5–300).
  • announcePosition / announceWaitTime — play queue position or estimated wait time to waiting callers (both default true; announceIntervalSeconds controls the spacing, default 30s, bounds 15–180).
  • routingStrategy — member selection preference at dispatch time (default longest-idle).
  • welcomePromptUrl / periodicPromptUrl / holdMusicUrl — the queue’s own playback prompts. A malformed URL returns 422 before persistence.
  • rnaTimeoutSeconds — ring-no-answer timeout before the platform re-dispatches to the next member (default 30s, bounds 5–120).
  • Callback tune-upscallbackMaxAttempts and callbackRetryIntervals drive the abandoned-callback rescue schedule when abandonedCallbackRescueEnabled is on.
  • Dispatch tune-upsmaxHoldSeconds, skillPriorities, minSkillLevel, and maxOccupancyPct are optional dispatch-level edits.
PUT /api/v1/voice/queues/{id} accepts the same body as a partial update — omitted fields keep their stored value. DELETE /api/v1/voice/queues/{id} removes the queue; its waiting callers are terminated and its active calls route through the overflow fallback.

2. Enroll members

Agents become eligible the moment they appear on the queue’s member list. Membership carries the per-member dispatch order, the member’s own skill tags (intersected with the queue’s requirements), optional proficiency levels, and the initial state.
cURL
  • agentUserId — the agent’s user id; either the Clerk-managed id or a platform-minted agent_<hex> id works.
  • priority — dispatch preference within the queue; lower wins (a VIP responder tier picks 1–10, a fallback tier picks 200+). Defaults to 100.
  • skills — the member’s skill list. An empty array means the member is eligible for every call on the queue regardless of a call’s requiredSkills; a non-empty list intersects with the queue’s skills and the call’s requiredSkills at dispatch. Use it when a bilingual agent should only take Spanish calls or a senior agent should only take escalations.
  • skillLevels — proficiency per skill (1–5), intersected with the queue’s minSkillLevel and composite weighting at dispatch.
  • state — initial state while you wire the roster; the agent later flips themselves available via POST /api/v1/voice/agents/{id}/status. The queue-safe default is offline, so you can enroll the whole roster before anyone logs in.
Loop this call for each agent. To adjust membership later use PATCH /api/v1/voice/queues/{id}/members/{memberId} with any subset of priority, skills, state, skillLevels; DELETE removes the membership. List the roster with GET /api/v1/voice/queues/{id}/members (keyset-paginated, ?limit=50&cursor=...).
An agent whose skills do not satisfy the queue’s requirements (or who is not a member at all) is never dispatched on a call for that queue, even while the agent reports available for other queues. Enroll every agent before you announce staffing in the supervision surface — the available-agents count below ignores non-members.

3. Priority queueing

Enqueue a caller when your routing logic parks the call on the queue (e.g. IVR queue node outbound, or a manual dispatch). Lower priority numbers win; a caller bumped by a supervisor lands at priority 0.
cURL
  • callControlId — the voice leg’s platform call id. The queue’s FIFO, max-wait, and overflow semantics anchor on this id; once a call is enqueued, every subsequent queue endpoint keys on it.
  • priority — initial ranking; smaller numbers pass earlier (0–99, default 10). Use supervisor-set business rules: a callback reservation comes in with an explicit priority, a VIP bump pushes priority to 0, a partners-tier queue assigns 5.
  • requiredSkills — the caller’s own skill tags (e.g. a speech intent resolved upstream, or the contact’s account attributes) that intersect with member eligibility.
When a caller needs to jump the line, the supervisor bumps the call:
cURL
That endpoint is admin-only — only a supervisor role re-queues a caller. Individual waiting entries are also adjustable with PATCH /api/v1/voice/queues/{id}/entries/{entryId} using a priority from 0–99 (0 is the queue head; the original wait clock is preserved, so a bump is a re-score, not a re-join).

4. Check membership before staffing

Before you trust the floor, the platform gives a tenant-wide head count of agents whose queue-membership plus skills make them dispatchable:
cURL
200 OK body:
This is a DISTINCT count, not the sum of each queue’s stats.agentsAvailable. An agent who is a member of two queues (and whose skills satisfy both) is counted once — summing the per-queue figures double-counts bilingual agents and skews the staffing decision. Use available-agents in any capacity planner.

5. Watch queue health

Loop every supervisor aggregation in as often as the board refreshes. Live snapshot: GET /api/v1/voice/queues/{id}/live is an SSE stream, not a plain snapshot — the endpoint keeps the connection open and publishes, on each tick, the named metrics plus any alert rules that fire. If you only need one tick, poll the aggregate routes below; live is the continuous feed that powers the in-app wallboard.
cURL
This is the headline-numbers call: queue config plus current depth, waiting, longest wait, SLA, agents total/available/busy, occupancy, and hangup counters. Poll it per queue for a plain dashboard; do not chain it as your aggregation — every metric below ties a different window or distribution. Windowed analytics: GET /api/v1/voice/queues/{id}/analytics?from=...&to=...&interval=30m|1h|1d (30-minute default; optional fcr_window_hours tunes the First-Call-Resolution heuristic past the default-24h definition) returns per-interval sla_pct, aht_seconds, asa_seconds, abandon_pct, occupancy_pct, plus a fcr_pct (heuristic resolved against call logs) with the echoed window so you can label the column “FCR-24h”. ASA here is answered-queue-wait only; abandoned calls are excluded. Hour-of-week heatmap: GET /api/v1/voice/queues/{id}/heatmap normalises into a 7×24 grid (missing cells are zero) so a wallboard can show when the queue’s SLA band saturates each week. Queue comparison: GET /api/v1/voice/queues/comparison ranks queues side by side. Hangup distribution: GET /api/v1/voice/queues/{id}/hangup-breakdown answers which overflow disposition (voicemail, child queue, hangup) takes the terminal volume. Per-member leaderboard: GET /api/v1/voice/queues/{id}/agent-leaderboard ranks members on a chosen metric across the same supervisor visibility rules as the platform-wide wallboard. Skill-by-window channeling: GET /api/v1/voice/queues/{id}/skill-breakdown collapses the [from,to] window into one row per skill (no interval bucketing) for a per-skill staffing check.

6. Emergency mode

Flip the queue straight to an off-queue destination when the floor takes an outage — fire drill, all-offline, an upstream carrier cutover. Arming requires a destination (E.164); the controller refuses an active: true arm with no landing, and disarm leaves the queue’s normal overflow behavior intact.
cURL
The inbound routing bridge checks the queue’s emergencyActive flag on every ring, so a flip takes effect on the next call. Disarm by POST’ing active: false (destination optional, destination: null explicitly clears). Every queue.emergency_toggled event lands in the audit log so post-incident review correlates the cutover to a named supervisor.
Emergency override is a zero-player safety rail: it bypasses the queue’s overflowAction (which sends an overload caller after maxWaitSeconds to voicemail or a fallback queue; it cannot rescue callers while the outage happens). Set the destination the moment a supervisor arms the override — the API demands it when you flip to active: true, not when the phone rings.

7. Wrap-up and dispositions

Per-queue wrap-up codes let a finished agent record the outcome. The catalog is administered on GET/POST/PATCH/DELETE /api/v1/voice/queues/{queueId}/dispositions, with two guards built into disposition recording — only the assigned agent records the outcome, and free-text outcomes are rejected. A finished conversation posts its disposition with POST /api/v1/voice/queues/{queueId}/calls/{callId}/disposition:
The endpoint is idempotent on (queueId, callId). Setting requireDisposition on a queue blocks the busy → available flip while the agent’s most recent finished call carries no recorded disposition, and the queue’s AI suggestion endpoint GET /api/v1/voice/queues/{queueId}/calls/{callId}/disposition/suggestion pre-fills a hypothesis to speed up the manual pick. The full lifecycle — enforcement gate, AI suggestion, outlier-flag analytics — lives on the wrap-up codes reference.

8. Wire queue alert rules

A supervisor defines thresholds the in-app live feed and the outbound webhook/email surfaces fire on. Alert rules bind to a queue, pick a metric, operator, and threshold, and become real when enabled.
cURL
  • metric choices: queue_depth, asa_seconds, abandon_rate, sla_pct, aht_seconds, occupancy_pct, agents_available. The canonical per-queue SLA breach alert is sla_pct with op = lt and a threshold like 0.8.
  • actionsse for an in-dashboard banner (default), webhook to fan out a tenant webhook event of your choosing (the queue.alert_rule_fired slug is the default; PagerDuty/Opsgenie/custom may subscribe), or email to notify org admins. delivery_config tunes the override slug or the subject prefix for email.
  • cooldown_seconds (0–86400; 300 default) suppresses re-fire; a rule that just fired is silenced across every delivery surface until its cooldown elapses. 0 restores legacy fire-every-tick.
  • Update with PATCH /api/v1/voice/queues/{id}/alert-rules/{ruleId} — the patch needs at least one of name, metric, op, threshold, action, delivery_config, enabled, cooldown_seconds; DELETE on a rule flips every dispatch surface off. List active rules with GET /api/v1/voice/queues/{id}/alert-rules.
A rule fires with the same audit parity the supervisor’s own edit emits — the queue’s SSE event, webhook fan-out payload, and email subject all carry the metric, operator, and threshold so post-incident review is not a chase through Slack.

9. Define the per-queue SLA breach policy

Alert rules cover ad-hoc thresholds; the SLA breach policy is the queue’s persistent service-level objective — the “80% of calls answered within 30 seconds over a 15-minute window” contract a queue breaches. You set it once per queue; every breach against it is logged and routed to the alert surfaces you choose.
cURL
  • targetPercentage (1–100) is the service-level percent the queue must hold; thresholdSeconds is the answer-within bound a call must beat to count, and evaluationWindowMinutes is the rolling window the service level is measured over. The queue’s reported service level compares against the window’s offered-minus-short-abandon denominator — the same convention the analytics endpoint reports.
  • breachAction is the one-shot response when no escalation ladder is set: open_ticket (notify org admins in-app and by email), page_supervisor (publish the queue.alert_rule_fired tenant webhook — the payload a PagerDuty or Opsgenie subscription pages on), reroute_to_overflow_queue (both), enqueue_callback (webhook, so downstream automation queues a callback), or none (log the breach, alert nobody). Read the current policy with GET; remove it with DELETE — a queue with no policy logs nothing and alerts nothing.
  • escalationSteps is the per-queue ladder. When any step is set, the ladder replaces the one-shot breachAction: step 1 notifies the duty supervisors immediately on breach, step 2 pages the escalation channel if nobody has fixed the queue 15 minutes later. Steps without a value for delayMinutesAfterBreach of 0 are scheduled responses — they fire when a downstream executor reads the logged breach, not in the scan request itself. Each step’s actionType uses the same notify/reassign/slack/webhook vocabulary as inbox SLA policies.
  • breachCooldownSeconds (0–86400) dedupes repeated breach alerts: a queue scanned every few seconds still pages at most once per cooldown window (floored at 60 seconds).
Evaluate and log a breachPOST /api/v1/voice/queues/{id}/sla-breach/scan takes a window sample (offeredCalls, answeredWithinSla, optional shortAbandons, windowStart), compares the observed service level against the queue’s stored policy, and on a breach whose cooldown is clear: appends the breach event, fires the routing the policy selected, and returns the verdict (breached, alert_fired, observed_service_level). A queue with no policy answers policy_configured: false and does nothing. Audit the breach historyGET /api/v1/voice/queues/{id}/sla-breach/events returns the queue’s newest-first breach log (up to 200 entries), each stamped with the observed service level, the policy in force, and the surfaces that actually fired. The supervisor wallboard’s breach-log tab reads this same endpoint.

10. Troubleshooting

  • Queue is full of callers, but agentsAvailable reads zero: those agents aren’t members of this queue, or their member skills don’t match the queue’s requirements. Run GET /api/v1/voice/queues/{id}/members to inspect the roster; an agent who exists on paper but has no member record is an empty pool until you enroll them with a POST.
  • A queue is stuck (longest wait explodes, ASA overflows, agents never pick): confirm the caller’s requiredSkills intersects at least one member’s skills. A bad requiredSkills value dispatches nowhere and waits forever — flip it at enqueue or enrol a broader-skilled member.
  • Emergencies left on: a queue that still bypasses its agents after you disarm keeps a destination on file. Run GET /api/v1/voice/queues/{id} and inspect emergencyActive plus emergencyDestinationNumber before blaming the audit — the controller accepts active: false with any supplied destination.
  • The available-agents figure says N but queues each show a higher agentsAvailable: available-agents counts DISTINCT members once; the per-queue stats multiply agents whose skills satisfy more than one queue. The sum always over-reads; never tier-right on the per-queue number.

See also