Background Music Service — Quick Start Guide
Overview
This guide covers integrating Tuned Global's APIs for background music services — the kind used in retail stores, restaurants, hotels, and venues. Unlike consumer streaming, background music players typically operate as store-and-forward systems: they cache audio locally on the playback device so music continues uninterrupted even when the network drops.
By the end of this guide you'll have: authenticated a venue device, fetched a scheduled playlist, cached tracks to local storage, played them back offline-capable, and logged plays for royalty reporting.
Estimated time: 15–20 minutes
Prerequisites
- A StoreId issued by Tuned Global for your background music environment
- OAuth2 client credentials (client ID and secret) from Tuned Global
- A registered device ID for the venue player
- cURL or Postman installed
- Local storage available on the playback device (minimum 2 GB recommended)
Architecture: Store-and-Forward
Background music players differ from consumer apps in several key ways:
| Aspect | Consumer Streaming | Store-and-Forward |
|---|---|---|
| Playback model | Stream on demand | Pre-cache, play locally |
| Network dependency | Continuous | Periodic sync only |
| Playlist control | User-driven | Centrally scheduled |
| Device type | Phone/tablet | Dedicated player, Raspberry Pi, POS system |
| Failover | Buffering/stop | Seamless from local cache |
The typical sync cycle:
- Device connects to the API on a schedule (e.g. every 4 hours, or overnight)
- Fetches the current playlist assignment and any updates
- Downloads and caches any new tracks not already stored locally
- Removes expired tracks that are no longer in any active playlist
- Plays from local cache — no network required during playback
- Logs play events locally and uploads them in batch when connectivity is available
Step 1: Authenticate the Device
Obtain a JWT token using your device's service account credentials. Background music devices typically use client credentials grant rather than user/password.
Endpoint: POST https://api-authentication-connect.tunedglobal.com/oauth2/token
curl -X POST "https://api-authentication-connect.tunedglobal.com/oauth2/token" \
-H "StoreId: YOUR_STORE_ID" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "bearer",
"expires_in": 86400
}
Save the access_token. For store-and-forward devices, implement token refresh logic that requests a new token before expiry — the device may need to sync while unattended.
Tip: Store the token expiry timestamp locally so the device knows whether to re-authenticate before attempting a sync, even after a reboot.
Step 2: Fetch Playlist Assignment
Retrieve the playlist(s) currently assigned to this device or venue location. Background music playlists are typically managed centrally by the venue operator or brand team.
Endpoint: GET https://api-metadata-connect.tunedglobal.com/api/v2.4/playlists/{playlistId}
curl -X GET "https://api-metadata-connect.tunedglobal.com/api/v2.4/playlists/55001?offset=0&count=500" \
-H "StoreId: YOUR_STORE_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
The response includes the full track listing with Track IDs, durations, and metadata. Store this response locally as your playlist manifest.
To check for schedule or playlist changes without downloading the full track list:
Endpoint: GET https://api-metadata-connect.tunedglobal.com/api/v2.4/playlists/{playlistId}/updated
curl -X GET "https://api-metadata-connect.tunedglobal.com/api/v2.4/playlists/55001/updated" \
-H "StoreId: YOUR_STORE_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Compare the returned timestamp against your last sync to decide whether a full refresh is needed.
Step 3: Cache Audio to Device
This is the critical step for store-and-forward reliability. For each track in the playlist, request a download URL and save the audio file to local storage.
3a. Request Download Token
Endpoint: POST https://api-services-connect.tunedglobal.com/api/v3/plays/{deviceId}/{trackId}/token
curl -X POST "https://api-services-connect.tunedglobal.com/api/v3/plays/VENUE-PLAYER-01/987654/token" \
-H "StoreId: YOUR_STORE_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
3b. Get Download URL
Endpoint: POST https://api-services-connect.tunedglobal.com/api/v3/plays/{deviceId}/{trackId}/stream
curl -X POST "https://api-services-connect.tunedglobal.com/api/v3/plays/VENUE-PLAYER-01/987654/stream?streamType=Music&streamProvider=Tuned" \
-H "StoreId: YOUR_STORE_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '"STREAM_TOKEN_FROM_STEP_3A"'
3c. Download and Store Locally
curl -L -o /cache/music/987654.mp3 "SIGNED_URL_FROM_STEP_3B"
Important: The signed URL has a TTL (time-to-live). Download the file promptly after receiving the URL. Do not store the URL for later — store the audio file itself.
Caching Best Practices
- Use the Track ID as the filename (e.g.
987654.mp3) so you can easily check which tracks are already cached - Check before downloading — compare the playlist manifest against files already on disk to avoid re-downloading unchanged tracks
- Download in batches — process 5–10 tracks concurrently to speed up initial sync without overwhelming bandwidth
- Verify file integrity — check that the downloaded file size is non-zero and matches expected duration; re-download corrupt files
- Implement retry logic — transient network errors are expected; retry failed downloads with exponential backoff
- Reserve disk space — monitor available storage and alert if cache usage exceeds 80% of allocated space
- Handle storage full gracefully — if the device runs out of space, keep existing cached tracks playable and flag the sync as incomplete
Example cache management pseudocode:
# On each sync cycle:
remote_tracks = fetch_playlist_tracks(playlist_id)
local_tracks = list_cached_files("/cache/music/")
to_download = remote_tracks - local_tracks # New tracks to cache
to_remove = local_tracks - remote_tracks # Expired tracks to clean up
for track_id in to_remove:
delete("/cache/music/{track_id}.mp3")
for track_id in to_download:
token = request_stream_token(device_id, track_id)
url = request_stream_url(device_id, track_id, token)
download(url, "/cache/music/{track_id}.mp3")
Step 4: Local Playback
Once tracks are cached, playback happens entirely from local storage with no network dependency.
Playback Loop
The player should:
- Read the locally stored playlist manifest
- Play tracks in the defined order (or shuffle, depending on venue configuration)
- Loop continuously — background music should never stop
- Record play events to a local log file for later upload
Handling Gaps
If a track file is missing or corrupt:
- Skip to the next track immediately
- Log the error locally
- Flag the track for re-download on the next sync cycle
- Never stop playback because of a single missing file
Scheduling
Many background music deployments use daypart scheduling — different playlists for morning, lunch, evening, etc. The device should:
- Store multiple playlist manifests if assigned a schedule
- Switch playlists at the configured times using the device's local clock
- Sync all scheduled playlists during the cache cycle, not just the currently active one
Step 5: Log Playback Events
Logging is mandatory for royalty reporting and compliance, even for background music. The key difference: store-and-forward players log locally first and upload in batch when connectivity is available.
5a. Log Locally During Playback
For each track played, record:
{
"TrackId": 987654,
"LogPlayType": "Start",
"Seconds": 0,
"Source": "Playlist",
"SourceId": "55001",
"Guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"Country": "AU",
"PlayerType": "BackgroundPlayer",
"Timestamp": "2026-03-31T09:15:00Z",
"DeviceId": "VENUE-PLAYER-01"
}
Log the same event types as consumer playback:
LogPlayType When Seconds
--------------- ------------------------------------------- ----------------
Start Playback begins 0
30SecondMark 30 seconds elapsed (mandatory for royalties) 30
EndOfFile Track finishes Total duration
Skip Track skipped (e.g. schedule change) Current position
5b. Batch Upload Play Logs
During each sync cycle, upload accumulated play logs to the API.
Endpoint: POST https://api-services-connect.tunedglobal.com/api/v3/plays/{deviceId}
curl -X POST "https://api-services-connect.tunedglobal.com/api/v3/plays/VENUE-PLAYER-01" \
-H "StoreId: YOUR_STORE_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"TrackId": 987654,
"LogPlayType": "Start",
"Seconds": 0,
"Source": "Playlist",
"SourceId": "55001",
"Guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"Country": "AU",
"PlayerType": "BackgroundPlayer"
}'
Upload each log entry individually or iterate through the local log file. After a successful upload (HTTP 200), mark the entry as synced so it isn't re-uploaded.
Critical: Do not delete local logs until you have confirmed successful upload. If the upload fails, retain the logs and retry on the next sync cycle. Play logs are legally required for royalty reporting — losing them is not acceptable.
Step 6: Sync Cycle Summary
Tie it all together in a single sync routine that runs on a timer (e.g. every 4–6 hours):
1. Re-authenticate if token is expired or near expiry
2. Check for playlist updates (Step 2 — /updated endpoint)
3. If playlist changed:
a. Fetch new playlist manifest
b. Remove tracks no longer in any active playlist
c. Download and cache new tracks (Step 3)
4. Upload pending play logs (Step 5b)
5. Report sync status (success/failure, tracks cached, logs uploaded)
Network Resilience
The sync cycle should be fully idempotent — if it fails partway through, the next run picks up where it left off:
- Partially downloaded files should be discarded and re-downloaded
- Play logs that failed to upload remain in the local queue
- Playlist manifest updates are atomic: apply the new manifest only after all new tracks are cached
Troubleshooting
Error Cause Solution
---------------------------- ---------------------------------------- ------------------------------------------------
403 on stream/download Token expired or subscription lapsed Re-authenticate (Step 1), check licence status
Playback silence Cache file missing or corrupt Check disk, trigger manual sync
Play logs not appearing Upload failed or entries not synced Check local log queue, verify network on sync
Disk full Cache exceeds allocated storage Increase storage or reduce playlist size
Playlist not updating Sync cycle not running or /updated stale Verify cron/timer, check API connectivity
Tracks re-downloading Track IDs not matching local filenames Ensure filename matches Track ID exactly
Quick Reference — Sync Endpoints
Action Method Endpoint
--------------------- ------ --------------------------------------------------------
Authenticate POST /oauth2/token
Fetch playlist GET /api/v2.4/playlists/{playlistId}
Check playlist update GET /api/v2.4/playlists/{playlistId}/updated
Request stream token POST /api/v3/plays/{deviceId}/{trackId}/token
Get download URL POST /api/v3/plays/{deviceId}/{trackId}/stream
Upload play log POST /api/v3/plays/{deviceId}
Notes
- Replace YOUR_STORE_ID, YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_ACCESS_TOKEN, and example IDs with your actual credentials from Tuned Global.
- Background music licensing is separate from consumer streaming. Confirm your licence covers public performance rights for the territories where devices are deployed.
- For large venue networks (100+ devices), contact Tuned Global about bulk sync endpoints and fleet management APIs.