Skip to main content

Troubleshooting: queue SLA forecast callback gate

A queue opted into the SLA forecast callback gate can refuse a new inbound caller at the entry check: the forecast projects the wait the caller is about to accrue (or finds the head-of-line caller already past the target) and, on a queue set to block, diverts the call into the press-1 virtual-callback consent flow instead of accepting FIFO depth. This page owns the decision matrix for that refusal — when the gate should suggest, when it should block, and what to fix when a caller routes into the press-1 flow unexpectedly. For the depth-cap refusal instead, work the queue capacity gates page; the two refusals look alike at the call-control layer but have different gates.

Recognize the signal

The clearest signal is a 503 on POST /api/v1/voice/queues/{id}/enqueue (or on the live-call transfer-to-queue path) whose decision body carries slaCallbackBlocked: true — not a QUEUE_DEPTH_CAP_REACHED code:
  • slaCallbackBlocked — always true on this shape; the SLA gate fired, not the depth cap.
  • slaThresholdSecs — the queue’s targetServiceLevelSeconds the forecast projected against.
  • projectedWaitSecs — the projected wait the caller was spared; unbounded when no agents are staffed.
  • overflowActioncallback, the path the gate drove the call into.
If the response instead carries error.code: "QUEUE_DEPTH_CAP_REACHED", this page is the wrong gate — work the queue capacity gates page.

Cause table

Fixes

1. Pick the posture per queue: suggest vs block

The slaCallbackPolicy field on the queue config is the decision:
  • Omitted (default) — fail-open; entry always succeeds. Use this on queues where you have not wired callback dispatch yet.
  • suggest — the forecast computes, the caller joins the queue normally, and the supervisor surface flags “press 1 for a callback advised.” Use this while you validate callback dispatch, or on queues where a hard refusal is worse than real hold.
  • block — a breaching forecast denies the entry and routes the call into the press-1 virtual-callback consent flow with the caller’s position saved.

2. Confirm the queue’s SLA target

The forecast projects against targetServiceLevelSeconds (5–300, default 20) on the queue config. Read the queue back with GET /api/v1/voice/queues/{id} and confirm the target is the bound you intended: a target of 5 seconds on a queue whose handle time averages 4 minutes blocks nearly every entry; an unset target means the gate never fires. Set the target once and every surface — wallboard, forecast, breach policy — reads the same bound. On block, the diverted caller hears the press-1 virtual-callback consent prompt: “press 1 to keep your place and get a callback.” Confirm the prompt copy your call-control layer plays on the diverted path is the consent copy you intend — a diverted caller who hears the wrong prompt is told nothing about keeping their position, and abandons instead of opting in. The prompt wiring lives on the voice queues guide.

4. Verify the callback dispatcher wiring

A blocked entry lands in the same virtual-callback dispatch path the in-queue press-1 escape uses, with the dispatcher’s built-in guards — emergency-dial blocking and the tenant-owned TCPA calling window — applied the same way. If you take block before the callback path is wired, every breaching entry refuses callers into a flow that never dials back. Wire dispatch first on the callback runbook, then opt into block.

Decision checklist

Work the refusal in this order:
  1. Read the response shape. slaCallbackBlocked: true means the SLA gate; a QUEUE_DEPTH_CAP_REACHED code means the depth gate — switch to the queue capacity gates page.
  2. Compare projectedWaitSecs to slaThresholdSecs. If projected wait exceeds the threshold, the forecast math fired; check staffing against GET /api/v1/voice/queues/{id}/stats.
  3. Check head-of-line when depth reads near zero. A longest-wait value over the threshold breaches the queue even at zero waiting — the gate reads the head’s unserved wait, not the position math.
  4. Confirm the posture and the target. GET /api/v1/voice/queues/{id} and read slaCallbackPolicy plus targetServiceLevelSeconds — an unset target or a block you never intended is the config bug, not a forecast bug.
  5. Change one thing. Re-set the target, re-pick the posture, or fix staffing. A queue at its SLA target with two idle agents has a dispatcher problem the gate posture will never rescue.

What not to do

  • Do not delete the queue. The refusal is tenant-owned config — slaCallbackPolicy, targetServiceLevelSeconds, and callback dispatch wiring are all queue fields you set; deleting the queue destroys the membership and the SLA policy with it.
  • Do not rename the gate as a “fail-open default” without confirming the overflow policy. Omitting slaCallbackPolicy restores fail-open, but the queue’s overflowAction still decides what happens at max-wait — make sure the untargeted queue still overflows to voicemail or a sibling before you lift the gate.
  • Do not loop retries inside head-of-line when staffing is unset. A queue with no agents staffed projects an unbounded wait; re-enqueue retries against that queue re-refuse at the gate every time. Add agents or re-route first.
  • Do not take block before callback dispatch is wired. The gate then refuses breaching entries into a flow with no working callback path — the supervisor-cue posture (suggest) is the correct intermediate step.

Escalate

If the config reads clean and entries still refuse through the SLA gate:
  • capture the queue id and the full decision body (slaCallbackBlocked, projectedWaitSecs, slaThresholdSecs, overflowAction) from the refusal,
  • include the callControlId from the body and the HTTP request_id from the response envelope,
  • attach a GET /api/v1/voice/queues/{id}/stats snapshot and the queue config from GET /api/v1/voice/queues/{id}.
Send those to support with the tenant’s organization name — the operator will compare the gate’s verdict against the queue’s live stats and the dispatcher counters.

See also