On hold
Fires when an agent puts the caller on hold (the softphone Hold button). Its chain supplies what the held caller hears (a Play music or Say node) and can…
What it does
Fires when an agent puts the caller on hold from their softphone (the
Hold button, on any host). There is no holdCall flow node anymore — hold is
something an agent does mid-call, not something a flow does. The caller's
leg stays parked in the call's conference room; this trigger's chain is what
the held caller actually hears.
Concretely: the conference's waitHook calls back into the caller's own
active flow and runs its onCallHold chain. Whatever say / playAudio /
pause verbs the chain produces are what plays to the held caller, on a
loop, until the hold ends; a terminal hangup at the end of the chain is
stripped (it can't hang up a leg that's merely on hold). No onCallHold
trigger in the flow — or a chain that produces nothing playable — falls back
to silence, not an error.
Because the chain also runs any side-effects on the way (a Slack ping, a
setVar, an agentNotify), it's a natural place to combine "what the caller
hears" with "what happens elsewhere" in one chain.
When to use it
- Play hold music or a "please hold" message to a caller an agent has parked
— the chain's
say/playAudio/pauseverbs ARE the caller's audio while they're held - Send a Slack ping to
#opswhen a customer is placed on hold so the team can keep an eye on long waits - Stamp
vars.heldAtwith a timestamp, then read it afteronCallUnholdto compute hold duration - Fire a webhook to your own dashboard so a live agent view can highlight calls currently parked
Configuration
_label and _note are author-only metadata; this trigger has no runtime
configuration of its own.
Fires when an agent puts the caller on hold (the softphone Hold button). Its chain supplies what the held caller hears (a Play music or Say node) and can kick off side-tasks such as a Slack ping.
| Field | Label | Type | Required | Default | Notes |
|---|---|---|---|---|---|
| _label | Trigger label | text | Optional | — | |
| _note | Internal note | textarea | Optional | — |
Outgoing events: triggered
Examples
Hold music plus a Slack ping
The chain does double duty: the playAudio node's audio is what the held
caller hears, and the apiCall off to the side notifies the team — the
caller never hears that call.
{
"id": "hold-trigger",
"type": "onCallHold",
"config": { "_label": "Hold audio + notify ops" },
"on": { "triggered": "hold-audio" }
}
[onCallHold] ──► [playAudio sys:music/default-hold, loop=forever]
│ (this IS the caller's audio)
└──► [apiCall POST hooks.slack.com/...
body: { text: "Caller {{event.from}} is on hold" }] ──► [end]
Just a spoken message, no side-effects
{
"id": "hold-trigger",
"type": "onCallHold",
"config": {},
"on": { "triggered": "hold-message" }
}
{
"id": "hold-message",
"type": "say",
"config": { "prompt": { "kind": "text", "text": "Please hold, we'll be right with you." } },
"on": { "next": "end" }
}
Gotchas
- Fires from the softphone, not from a flow node. There is no
holdCallnode anymore — an agent puts the caller on hold from their softphone UI, and that's what triggers this chain. A flow can't put a caller on hold itself. - Only
say/playAudio/pauseverbs reach the caller. The conferencewaitHookfilters the chain's output to those three and drops everything else, including a terminalhangup— don't rely on the chain to end the call. - An empty or missing chain is silent, not broken. If the flow has no
onCallHoldtrigger, or the chain's filtered output is empty, the held caller hears nothing (a bare pause) rather than an error tone. - The chain runs again for as long as the hold lasts. The media plane
calls the
waitHookrepeatedly while the leg is held, so don't assume it runs exactly once per hold. - Side-chain runs in an isolated state branch. It shares the call's
varsbut can't move the main flow's cursor. - Implemented by the conference
waitHook(GET|POST /hooks/conf-waitinrust/bins/heyquad-walker/src/webhooks.rs), which resolves and walks the caller's own flow'sonCallHoldchain.
