holdCallstableHold call
Starts hold music for the caller and fires any On-call-placed-on-hold triggers. The flow auto-advances to the next step — pair with "Resume call" later in…
What it does
Places the caller on hold. The node starts your chosen hold music in a
loop on the caller leg, opens a row in the call-holds table for analytics
and reporting, and fires any On call placed on hold trigger chains
you've configured. The hold reason (transfer, queue, or manual) is
inferred from the surrounding flow state so the report can tell a hold
during a transfer apart from a hold during a queue wait.
Unlike most voice nodes, holdCall does not wait on a Telnyx event —
it fires the hold music command, stamps the state, runs triggers, and
auto-advances to the next node immediately. This means the flow keeps
moving while the caller hears music. Pair it with Resume call
(unholdCall) later in the flow to stop the music and close the hold
record. If the call hangs up while still on hold, the trigger chain for
onCallEnd still fires normally.
When to use it
- Before a
transferstep so the caller hears music while the destination is being dialed - Inside an agent-side workflow that pauses the caller while the agent performs a lookup or warm-transfer dance
- At the start of a long external integration step (CRM lookup, payment authorization) so the line isn't silent
- To deliberately invoke an
onCallHoldtrigger chain — for example, a Slack notification when a VIP is parked
Configuration
The only field is the hold music URL. Leaving it blank uses whatever the binding's default music is (or nothing, if none is configured).
Starts hold music for the caller and fires any On-call-placed-on-hold triggers. The flow auto-advances to the next step — pair with "Resume call" later in the flow to stop the music.
| Field | Label | Type | Required | Default | Notes |
|---|---|---|---|---|---|
holdMusicUrl | Hold music | audioUrl | Optional | "" | Plays in a loop until "Resume call" runs (or the call hangs up). |
Examples
Hold during a CRM lookup
Place the caller on hold, run a custom script that hits an external API, then resume the call once the data is loaded.
{
"id": "park-for-lookup",
"type": "holdCall",
"config": {
"holdMusicUrl": "https://cdn.example.com/hold-loop.mp3"
},
"on": { "call.hangup": "end" }
}
The flow auto-advances after the music starts — wire the next step on
any transition (the worker takes the first one). holdCall doesn't
emit a hold-specific event; the next node simply runs.
Gotchas
- Auto-advance picks the first transition. The worker takes
Object.values(node.on)[0], not a named edge. If you wire several edges (e.g. bothcall.bridgedandcall.hangup), only the first one in object order is followed on entry. Keeponsmall or rely on interrupt events (call.hangup) which can match later. - Hold music URL must be publicly reachable. Telnyx fetches it server-side. Signed S3 URLs that have expired, private endpoints, or anything behind auth fail silently — the music never starts but the flow advances anyway. Test the URL by pasting it into a browser.
- Reason is inferred, not configured. The hold record's
reasonis derived fromstate.vars:transferif a transfer is staged,queueif a queue is active, otherwisemanual. There's no manual override — set the surrounding state if you need a specific reason in reports. onCallHoldtriggers run synchronously. The flow doesn't advance until every trigger chain attached toonCallHoldhas finished entering its first node. Heavy work in those chains (long customScript steps, slow external calls) delays the flow.- Pair with
unholdCallto close the row. Thecall_holdsrow stays open (noendedAt) untilunholdCallruns or the call ends. Forgetting to resume leaves an open-hold row that's still useful for reporting but reads oddly in the timeline.
