Skip to content
VoiceplayMusic

Play 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".)

FieldLabelTypeRequiredDefaultNotes
audioUrlMusicaudioUrlRequired""Pick a built-in track or paste any public mp3/wav URL. The custom-URL mode supports {{vars.x}} templating.
offsetSecsStart at (seconds)numberOptional0Where 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.
playSecsPlay for (seconds)numberOptional30How long to play before the flow moves on down the "Finished playing" path. Leave blank to play the track once through to the end.
interruptOnDtmfLet the caller press a key to skipbooleanOptionalfalseWhen 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).
validDigitsKeys that interrupttextOptional1234567890*#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.
saveOffsetVarSave stop position to variabletextOptionalmusicPosOptional. 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.