Skip to content
Logicwaitstable

Wait

Pauses the flow for a fixed duration. The wait is interrupted if the call ends or bridges (e.g. an agent picks up while the call is queued). Use it to exp…

What it does

Pauses the flow for a fixed number of seconds. The worker schedules a delayed BullMQ job (via scheduleFlowWait) and parks the run on this node; when the job fires, the flow follows on['wait.elapsed']. While the wait is in progress the call is still live — the caller hears whatever was last started (hold music, a long playback, silence). The duration is clamped to the inclusive range 1-3600 seconds; a value outside that range is logged and the node skips without scheduling anything.

The wait is also interrupted by call lifecycle events. If the caller hangs up while we're waiting, call.hangup interrupts on this node and follows the configured transition (typically to a cleanup or end). If the call bridges — for example because the caller was parked in a queue and an agent just picked up — call.bridged fires on this node and you can route to a "got connected, stop the timer" target. The pending wait job's id is stashed at vars._waitJobId; when the flow leaves the wait node (via any transition), the worker removes the delayed job through cancelFlowWait so it doesn't fire after the fact. If the cancel races the timer firing, the handler falls back to a no-op because the run has already moved past the node.

When to use it

  • Implement a max-wait-then-voicemail fallback in a queue: enqueue → playAudio (loop) → wait 600 → voicemail. The bridge interrupt on the wait jumps you to "connected" if an agent picks up first
  • Pause briefly between actions in an outbound call campaign — e.g. speak a prompt, then wait 2 before listening for digits, to avoid clipping the start of the response
  • Build periodic announcements during a long hold: wait 60 → say "You are still in queue" → wait 60 → say ... — wired in a loop
  • Stagger automated messages so they don't all fire at once when many callers enter the same hold experience
  • Add a small grace period after a setVar / HTTP call before the next playback, so the caller doesn't feel the abrupt switch

Configuration

Pauses the flow for a fixed duration. The wait is interrupted if the call ends or bridges (e.g. an agent picks up while the call is queued). Use it to express max-wait-then-fallback in a queue: enqueue → playAudio (loop) → wait → voicemail.

FieldLabelTypeRequiredDefaultNotes
durationSecsDuration (seconds)numberRequired300How long to wait before firing the wait.elapsed transition. Capped at 3600 (1 hour).

Outgoing events: wait.elapsed, call.bridged, call.hangup

Examples

Max-wait fallback in a queue

The wait fires after ten minutes; if an agent bridges first, the call.bridged interrupt routes to end and the worker cancels the pending wait job on the way out (best-effort — see Gotchas).

{
  "id": "max-wait",
  "type": "wait",
  "config": { "durationSecs": 600 },
  "on": {
    "wait.elapsed": "voicemail",
    "call.bridged": "end",
    "call.hangup": "end"
  }
}

Tiny gap between TTS and digit-collect

A two-second gap between a TTS prompt and the gatherDigits step gives the caller time to start speaking before the DTMF window opens.

{
  "id": "settle",
  "type": "wait",
  "config": { "durationSecs": 2 },
  "on": { "wait.elapsed": "collect-digits", "call.hangup": "end" }
}

Gotchas

  • Duration is clamped to 1-3600 seconds. A durationSecs of 0 or a negative number logs a warning and skips the node entirely — no wait, no transition fired. A value above 3600 is silently capped at 3600 (one hour). Use a chain of multiple wait nodes for longer holds, or rethink the design.
  • Interrupt events fire on the wait node, not the upstream enqueue / dial. When call.bridged or call.hangup arrive while the wait is in flight, the worker reads node.on[event] on the wait node itself — wire those edges directly. Forgetting to wire call.hangup here means a caller who hangs up during the wait stays "running" until the BullMQ job eventually fires and tries to advance into a node whose context is already gone.
  • The pending wait job is cancelled when the flow leaves the wait node. If a call.hangup, call.bridged, or any other event interrupts the wait before the timer fires, the worker's leaveNode hook calls cancelFlowWait(_waitJobId) and removes the delayed BullMQ job. Cancellation is best-effort: if BullMQ has already moved the job to active by the time we try to remove it (sub-second race), the no-op fallback still applies — the handler runs, sees state.currentNodeId has moved on, and advances nothing. Stale jobs from runs created before this fix shipped will still no-op until they fire and age out.
  • vars._waitJobId is reserved bookkeeping. Don't write to it from a customScript. The variable is overwritten on every wait node entry; only the most recent wait's job id is tracked.
  • Real-world latency adds 100-500ms. BullMQ delayed jobs are best-effort, not real-time. A durationSecs: 1 wait can land 200ms late under worker load. For tight timing inside a single call, prefer the catalog's built-in inter-digit / playback timeouts where they exist.