Skip to main content

Inbound queue SLA forecast: pre-suggest callback before breach

Inbound ACD queues forecast a breach of the queue’s configured SLA target by comparing the queue head’s projected wait (EWT) against that target. When the projected wait meets or exceeds the target — while the caller is still short of the actual breach — the head is flagged to be offered the keep-your-place callback opt-in: the caller can hang up and get a callback once they reach the front of the queue, without losing their saved position (the callback-in-queue dispatcher dials them back on the Devotel wholesale softswitch). The forecast is bounded: a single evaluation wave flags at most a small, fixed number of queue heads (10), hard-ordered by smallest-positive-EWT first. The closest-to-breach head is flagged before any less-urgent head, so a bounded consumption budget drains the most urgent entries first — breaching queues get a near-term relief lever without flooding every waiting caller.

How the forecast decides

The evaluator wave computes each head’s EWT the same way the in-queue EWT-announcer does:
  1. Rolling window stats. The wave pulls the queue’s recent answered wait time and the queue head’s aht-ahead rank (the current position of the head inside the queue’s ZSET).
  2. Projected wait. EWT = (ahead-in-that-rank + 1) × measured AHT (seconds) ÷ agents-available. When the queue has no recent answered calls (cold start), the helper clamps into a bounded constant and never emits Infinity.
  3. Compare against SLA. If the resulting EWT ≥ slaTargetSeconds, the head is a forecast candidate. The flag is pure — the evaluator only marks “you should offer the opt-in”; the in-queue press-1 surface and the supervisor-side POST /api/v1/voice/queues/:id/sla-forecast advisory route still own the actual offer.
  4. Another pass is a no-op. If EWT < slaTargetSeconds, the head is skipped silently — no flag, no permission, no callback hint.
The wave itself is bounded, not one-flag-per-tick. One wave evaluates the heads of every running queue, hard-sorts candidates smallest-EWT-first, and returns the closest 10. A queue with several heads queued all still re-evaluates on the next wave; nothing is persisted across waves, so the same queue head is re-flagged each wave until the caller is served or the head is no longer past target.

Configure the SLA target

The forecast reads the same per-queue answer-within target that feeds SLA-aging, the wallboard service-level metric, and the SLA-breach policy. Without it, the queue is opt-out and no forecast ever fires. Dashboard. Open Voice → Queues in the console, select the queue, and set SLA target (seconds) (5–300, default 20). This is the same targetServiceLevelSeconds knob the voice-queues guide covers. The forecast, the wallboard’s SLA numerator, and the SLA-aging engine all resolve it through the same queue config — change it once and every surface adopts the new bound. API. Send the same field the dashboard exposes, as a partial update on the queue:
cURL
If you also run the queue-entry gate (slaCallbackPolicy), the same targetServiceLevelSeconds is the threshold that drives ok / suggest / block. The forecast’s pre-suggestion keeps working even when you only run one of the two gates.
The forecast is opt-in per queue and only becomes live when targetServiceLevelSeconds is set. A queue with no SLA target is never foreclosed — the feature is strictly non-intrusive on legacy traffic. Queues whose admins only configure the gate policy never get a supervisor flag unless they also set a target.

Pre-requisites and effective behavior

  • SLA target set. Without targetServiceLevelSeconds, the queue collapses to ok on every surface — no pre-suggestion and no supervisor flag.
  • Statistical window. The projected wait relies on the queue’s answered-call window window (the same offeredCalls / answeredWithinSla sample the wallboard tick publishes). A queue with no recent answered traffic falls back to a default handle-time proxy, so the forecast is still correct in posture; it only mistakes for “breaching” once the rolling window saturates.
  • Agent availability. When agentsAvailable is zero, the EWT math intentionally degrades to Infinity (the queue-entry gate reads this as the worst posture) and the supervisor advisory treats “0 heads available” as its own no-suggestion signal. Forecasts degrade predictably, not silently.
  • Opt-in scope. slaCallbackPolicy is not the gate the pre-suggestion uses — the forecast is purely advisory; the entry gate remains a separate opt-in for operators who want to refuse entry, not just flag a callback.

Operation and monitoring

  • Supervisor surface. Flagged heads surface through the live queue feed (GET /api/v1/voice/queues/{id}/live SSE) and through the same diagnostic read the voice-queues guide’s SLA breach policy section describes. The advisory endpoint POST /api/v1/voice/queues/{id}/sla-forecast returns the queue’s forecast + a suggestion-action label (no_action / pre_suggest_callback / breach_in_progress), which the wallboard port wires to a “press 1 for a callback” hint.
  • Wave cadence. The forecast evaluator runs on the queue wave scheduler (the same cadence the rest of the ACD wave runs, not a new endpoint) and flags up to the bounded fan-out per wave. There is no per-request API the queue head must trip.
  • Webhook/audit. The pre-suggestion itself is internal — the queue’s callback-suggestion mark feeds the existing in-queue callback opt-in and the audit surface that the queue head’s forecast makes visible. Operators who want it routed outbound should instrument it via the existing alert rules (metric: sla_pct, op: lt, action: webhook) rather than expect a new event envelope.
  • Relationship to press-1. The flagged opt-in is the same queue in-queue callback opt-in offered by the manual press-1 escape path; the forecast simply raises the offer before the breach (similar headroom math, same dispatcher). A caller who ignores the pre-suggestion still retains the manual option at any point while waiting.

Edge cases

  • No history. No recent answered calls → the helper falls back to a bounded avgHandleSeconds constant (60s default via MAX_CALLBACK_EWT_SECONDS clamp) and the forecast still returns correctly; this is intentional so a cold-start queue still pre-warns after its first answer.
  • No SLA configured. Queue without targetServiceLevelSeconds → the slaTargetSeconds parameter is undefined, the helper’s guard stops the candidate, and the queue’s forecast is always no_action. Your dashboard reflects this as “SLA target not set.”
  • Disabled callback. If the queue has callback dispatch disabled (no callbackRequests/in-queue escape wired), the pre-suggestion still fires on the queue — it merely can’t be honored by the caller. Operators should disable SLA target on queues that don’t want the surface.
  • Second wave. Nothing is persisted across waves. On the next wave the same queue head re-flags if the EWT remains ≥ target, and later-flagged heads continue to be re-flagged in earlier-to-later order until they drain.
  • Queue = 0. A queue with depth zero but whose service-level is already under target resolves as alreadyBreached on the supervisor advisory; the in-wave pre-suggestion guard skips since there is no queue head to offer to.
  • Head never reaches front. The flag’s decision point is at enqueue/evaluation. The head may age past the SLA before a pre-suggestion offer lands; that is the expected forecast-lead: it offers about the breach-before-it-happens window, not a fresh breach event.

Troubleshooting

  • SLA breached, no pre-suggestion appeared. Confirm (a) the queue actually has targetServiceLevelSeconds set, (b) the queue has at least one head with EWT ≥ slaTargetSeconds, (c) the head hasn’t already been flagged this wave, and (d) the agentsAvailable count reported at GET /api/v1/voice/queues/{queueId}/stats is non-zero — a zero number yields a forecast no-suggestion rather than a freezing flag.
  • Pre-suggestion fired but the callback was never offered. The dispatcher’s pre-suggestion is advisory: the in-queue press-1 surface and the handleOverflow(call, 'callback', ...) gate still decide whether to voice the offer. If the in-queue callback chain is misconfigured the flag is still marked but no callback path is wired — set the queue’s slaCallbackPolicy to suggest or block and wire the entry gate so the mark reaches the dispatcher.
  • It looks like the forecast is stuck on a queue head. The bounded wave flag does not remove a queue entry; the helper only marks the offer the queue-head can use. If the head never drains, the offer keeps being recomputed on every wave until either capacity arrives or the caller overflows/terminates.