---
summary: What held callers hear, the three kinds of playlist, uploading your own music,
  and choosing a playlist per extension.
title: Music on hold
path: concepts/music-on-hold
status: published
---

Whenever someone is on hold — a plain hold, a caller waiting while you consult a transfer target, or part of a conference you split off — they hear music, and nothing of the call. The music player is a participant only the held parties can hear.

## Which playlist plays

1. The **holding extension's** playlist (`moh_playlist_id` on the extension),
2. else the **tenant default** playlist,
3. else the built-in **ScaiLabs Classics**: public-domain piano recordings (Satie, Debussy, Mozart, Schubert).

A playlist without tracks is skipped. Playlists loop; with `shuffle` on they play in random order, otherwise in the order you set.

## Three kinds of playlist

| `scope` | Who manages it | Who can use it |
|---|---|---|
| `platform` | ScaiLabs (read-only) | everyone |
| `tenant` | tenant admins (`scaidial:trunks:manage`); one can be the tenant default | every extension in the tenant |
| `personal` | the user who made it | extensions that user manages |

Tenant admins manage playlists on **ScaiDial → Music on hold**; ScaiWave users manage their personal playlists in ScaiWave.

## Uploading music

```bash
# Create a playlist (admins get a tenant playlist; others a personal one, or pass "personal": true)
PL=$(curl -s -X POST $SCAIDIAL/moh/playlists -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name": "Reception", "shuffle": true}' | jq -r .data.id)

# Upload a song. rights_confirmed=true is required.
curl -s -X POST $SCAIDIAL/moh/playlists/$PL/tracks -H "$AUTH" \
  -F file=@"spring-theme.mp3" -F rights_confirmed=true -F title="Spring theme" -F artist="Our band"

# Make it the tenant default
curl -s -X PATCH $SCAIDIAL/moh/playlists/$PL -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"is_default": true}'
```

- **Formats:** MP3, WAV, FLAC, OGG/Opus, M4A/AAC.
- **Limits:** 40 MB and 15 minutes per file, at least 5 seconds; 50 tracks per playlist; 20 tenant playlists and 5 personal playlists per user.
- **Conversion:** tracks are mixed to mono, converted to phone quality (16 kHz) and levelled to a calm volume, so loud and quiet songs play at the same level.
- **Rights:** whoever uploads confirms the tenant may play the music to callers (own recordings, licensed or royalty-free music). The confirmation is recorded with their user id. Commercial music usually needs a licence for public playback.

## Endpoints

| Method + path | Does |
|---|---|
| `GET /moh/playlists` | Platform, tenant and your own playlists (admins also see users' personal ones), plus the upload limits |
| `POST /moh/playlists` | `{name, shuffle?, personal?}` |
| `PATCH /moh/playlists/{id}` | `{name?, shuffle?, is_default?}` (`is_default`: admins, tenant playlists) |
| `DELETE /moh/playlists/{id}` | Deletes it and its tracks; extensions using it fall back to the tenant default |
| `GET /moh/playlists/{id}/tracks` | Tracks in play order |
| `POST /moh/playlists/{id}/tracks` | Multipart upload: `file`, `rights_confirmed=true`, `title?`, `artist?` |
| `PUT /moh/playlists/{id}/order` | `{track_ids: [...]}` — every track once, in the new order |
| `DELETE /moh/tracks/{id}` | Removes a track |
| `GET /moh/tracks/{id}/audio` | The converted track (`audio/flac`) to preview |

**Playlist:** `id`, `name`, `scope`, `owner_user_id`, `shuffle`, `is_default`, `track_count`, `duration_ms`, `editable`. **Track:** `id`, `playlist_id`, `title`, `artist`, `duration_ms`, `size_bytes`, `position`, `license`, `source_url`, `created_at`.

## Choosing a playlist for an extension

`PATCH /extensions/{id}` with `{"moh_playlist_id": "<playlist id>"}` (`""` = the tenant default), or the *Music on hold* field in the extension form. Users can choose for extensions they manage in ScaiWave.
