playMusicPlay music
Plays hold/wait music to the caller — pick a built-in track or paste a public mp3/wav URL. Built for hold experiences: start at an offset, play for a set…
type: playMusic status: stable sinceVersion: 0.6.0 seeAlso: [playAudio, say, holdCall, gatherDigits] keywords: [music, hold music, ringback, audio, offset, resume, skip, dtmf, wait]
What it does
Plays hold/wait music to the caller. The source is either a built-in track (the bundled hold-music / ringback presets) or any public MP3/WAV URL. Unlike Play audio (a plain looped file) it's built for hold experiences:
- Start at an offset (seconds) into the track.
- Play for a set number of seconds, then continue down the Finished playing path. Leave it blank to play the track once through.
- Optionally let the caller press a key to skip, which routes a Key pressed branch.
- Save the stop position to a variable so a later Play music step can pick up right where the caller left off.
When to use it
- A resumable music bed split across several steps — save the offset on one step, feed it into Start at on the next to resume seamlessly.
- Timed hold music: "play 30 seconds, then re-check the queue."
- A "press any key to skip the intro" experience before a menu.
For a one-off clip or a simple Forever loop, use Play audio. For a
spoken prompt or TTS, use Play prompt or audio (say).
Configuration
Plays hold/wait music to the caller — pick a built-in track or paste a public mp3/wav URL. Built for hold experiences: start at an offset, play for a set number of seconds, and optionally let the caller press a key to skip ahead. Save the stop position to a variable and feed it back into "Start at" on a later Play music step to pick up right where the caller left off. (To loop a specific audio file or play a one-off clip, use "Play audio"; for spoken prompts or TTS, use "Play prompt or audio".)
| Field | Label | Type | Required | Default | Notes |
|---|---|---|---|---|---|
audioUrl | Music | audioUrl | Required | "" | Pick a built-in track or paste any public mp3/wav URL. The custom-URL mode supports {{vars.x}} templating. |
offsetSecs | Start at (seconds) | number | Optional | 0 | Where in the track to begin. Accepts a {{vars.x}} template so you can resume from a saved stop position. If it lands past the end of a built-in track, playback restarts from the beginning. |
playSecs | Play for (seconds) | number | Optional | 30 | How long to play before the flow moves on down the "Finished playing" path. Leave blank to play the track once through to the end. |
interruptOnDtmf | Let the caller press a key to skip | boolean | Optional | false | When on, a keypad press during playback stops the music and routes the "Key pressed" branch. When off, keypresses are ignored and the music plays until the play time elapses (or the call is bridged / hangs up). |
validDigits | Keys that interrupt | text | Optional | 1234567890*# | Which keys are allowed to interrupt. Leave blank to accept any key. Only used when "Let the caller press a key to skip" is on. |
saveOffsetVar | Save stop position to variable | text | Optional | musicPos | Optional. When this step ends — whether the play time elapsed or the caller pressed a key — the seconds-offset where playback stopped is written to this variable. Reference it later as {{vars.musicPos}} and drop it into "Start at" on another Play music step to resume. |
Outgoing events: call.playback.ended, call.playback.dtmf, call.hangup
Examples
Built-in music for 30 seconds
{
"id": "hold-music",
"type": "playMusic",
"config": { "audioUrl": "opusringback", "playSecs": 30 },
"on": { "call.playback.ended": "check-queue", "call.hangup": "end" }
}
Resume where the caller left off
Two steps that share a position variable. The first plays 20s from the
start and saves the stop offset to musicPos; the second resumes from
there. {{vars.musicPos}} resolves before playback.
[
{
"id": "music-a",
"type": "playMusic",
"config": { "audioUrl": "hey-quad-line", "offsetSecs": 0, "playSecs": 20, "saveOffsetVar": "musicPos" },
"on": { "call.playback.ended": "announce" }
},
{
"id": "music-b",
"type": "playMusic",
"config": { "audioUrl": "hey-quad-line", "offsetSecs": "{{vars.musicPos}}", "playSecs": 20, "saveOffsetVar": "musicPos" },
"on": { "call.playback.ended": "menu" }
}
]
Press a key to skip the intro
With Let the caller press a key to skip on, a keypress stops the music and
routes the Key pressed branch (call.playback.dtmf); otherwise it plays
to the play-time and routes Finished playing (call.playback.ended). The
key the caller pressed is available downstream as {{vars.gatheredDigits}}.
{
"id": "intro",
"type": "playMusic",
"config": { "audioUrl": "opusringback", "playSecs": 15, "interruptOnDtmf": true },
"on": { "call.playback.dtmf": "menu", "call.playback.ended": "menu", "call.hangup": "end" }
}
Gotchas
- Offsets are tracked in seconds, assuming 8 kHz audio. The built-in tracks are 8 kHz mono, so the seconds ↔ samples conversion is exact. A custom URL at a different sample rate may drift slightly on resume.
- Offset wrap only applies to built-in tracks. "If Start at is past the end of the track, start from the beginning" needs the track length, which we only know for the bundled presets. For a custom URL the offset is passed through as-is.
- The saved offset on a key-press is an estimate. When key-interrupt is on, the platform reports which key was pressed but not where playback stopped, so the saved position is derived from elapsed time (accurate to within processing latency). The Finished playing path and the no-interrupt path save an exact position.
- "Play for" longer than the track plays once to the end. There's no auto-loop for the timing fields — a 30s play time on a 20s clip plays 20s. To loop a clip, use Play audio with a loop count.
- Key-interrupt off means keys are ignored. The music plays until the play-time elapses, the call is bridged (an agent answers), or the caller hangs up.
- Audio URLs must be publicly reachable. The media server fetches the file server-side; private or expired-signed URLs fail and the flow stalls.
