Play audio (Cloud queue)

Your app can use a cloud queue hosted on your server to play a dynamic list of tracks on a group of Sonos players. Players will continue to play and fetch new tracks from the cloud queue even after your app has left the network, and any changes to the list of tracks will still be reflected on Sonos.

The Control API defines the protocol between your app and the player, the Cloud Queue API defines the communication between players and your cloud queue server. See the Cloud Queue API for the latest version number.

Players call the Cloud Queue API to get data to stream content from your media server. Then players stream content over HTTP from your media server using the MusicObjectId. This diagram simplifies the communication. See account matching for details.


What is a cloud queue?

A cloud queue is a list of audio tracks in the cloud that can be loaded and played in a playback session on a group of players. See Playback Sessions for details.

After your app has created a new playback session or joined an existing playback session on a group, it can instruct a group to load a cloud queue. Once loaded, players request a window of tracks from the cloud queue. Changes to the queue are sent to players when they request a new window, either explicitly or at set intervals.

The player gets the first track and a window of additional tracks and begins playing. Once the player gets close to reaching the end of the window, or after a set amount of time, the player requests another window. This continues until the window includes the last track in the cloud queue, or until playback is stopped.


Tracks in the cloud queue

Each track in the cloud queue has a unique itemId that identifies that particular track in that particular queue. If the same track appears twice in a queue, each instance has a different itemId value. The itemId values should never be re-used. The simplest way to implement an itemId is to generate a new Globally Unique Identifier (GUID) for each track as it is added to a cloud queue.

Each track in the cloud queue has all the standard metadata that is needed to fetch the audio for the track. This may include either a mediaUrl (the URL to the actual audio) or a MusicObjectId. If an item contains both of these objects, Sonos ignores the mediaUrl. This is because the MusicObjectId resolves to a URL using SMAPI. See MusicObjectId and account matching for details.

Each track should also include the contentType (also called the MIME type or media type) for the audio. See the playback object cloud queue inputs for details.

Each track has the standard metadata to display a basic "Now Playing" screen for that track:

  • Track name
  • Artist name
  • Album name
  • Album art image URL
  • Track duration

See Supported audio formats for the file formats supported by Sonos Players.


A cloud queue use case

Players use a sliding window technique to fetch tracks from your cloud queue. This is described in the use case below.

Typically, you would map an existing list of tracks on your server, such as a playlist, to a cloud queue. Let’s say you have a playlist of 100 tracks. A user on your app starts playing the playlist from track 65.

Your app first needs to create or join an existing session on a group of players with one of the createSession, joinSession or joinOrCreateSession Control API commands. The session grants your app control of what is playing on a group of players. See Playback Sessions for details. If your app is creating a new session, send the accountId to tell the player which account it should use for playback. The player verifies that the accountId matches a stored account. If the accountId does not match, you'll receive an ERROR_INVALID_PARAMETER error.

Once connected to a session, your app sends a loadCloudQueue Control API command to load the cloud queue on the group, centered on track 65. Your app can optionally provide metadata for track 65, and instruct the player to start playing immediately, before it connects to the cloud queue server. This optimization allows for shorter time-to-music for the user.

The player loads metadata for track 65 and starts playing it on the group of players. Next, the player sends a GET /context request to get the container metadata, playback policies, and reporting options from your cloud queue server. Then, the player fetches a window of 20 tracks from the cloud queue with the GET /itemWindow Cloud Queue API, centered around track 65 (tracks 56-75). It uses that data to play the tracks in sequence.

When playback continues without interruption and gets close to the end of the window, for example, track 72, the player fetches a new window of tracks centered around the currently playing track.

To allow for gapless playback, the player will always have enough tracks ahead to buffer the audio.

If your app sends a play command when the cloud queue is empty, the player will attempt to resume playback of content that was playing prior to this command. For example, if the user was previously listening to a radio station or had a playlist queued, it will attempt to resume playback of that content.

Your app can enable users to perform the following actions on the cloud queue using Control API commands:


Cloud queue workflow

The following workflow shows an overview of the cloud queue use case described above.

Workflow details

  1. Your app uses SSDP to discover Sonos players and groups.
  2. A Sonos player sends a unicast response.
  3. Your app connects to a Sonos player using a secure WebSocket.
  4. The player sends an HTTP 101 response and provides a WebSocket URL in the WEBSOCK.SMARTSPEAKER.AUDIO header.
  5. Your app sends a match command to the player to match the account in your app with one already saved on the player. In this command, your app provides a hashed user ID, a nickname for the account, your service ID, and a linkCode. See Account matching for details.
  6. The player uses the hashed user ID to match the account to one that it has stored. If it doesn't find a match, the player creates a guest account. The player sends a response to your app that includes the Sonos account ID and indicates whether or not it's a guest account.
  7. To control players, start or join a session using the createSessionjoinSession, or createOrJoinSession command in the playbackSession namespace. If creating a new session (createSession or createOrJoinSession), your app provides the accountId to indicate which account to use for playback in the session.
  8. The player sends a sessionStatus object as a response. This object indicates whether the session was newly created or if it already existed.
  9. A user browses your app and chooses a playlist of 100 tracks. The user chooses to play track 65. Your app sends a loadCloudQueue command to the player. This command includes trackMetadata for track 65 and the playOnCompletion parameter is set to true. The trackMetadata includes the musicObjectId for this track.
  10. The player returns an empty body with a "success" value of true.
  11. The player uses the musicObjectId from trackMetadata to send getMediaURI to request the URI for track 65 from your SMAPI server.
  12. Your SMAPI server returns the URI for the track audio in your getMediaURI response.
  13. The player uses the media URI to send a GET request to your content delivery network (CDN) for the track audio.
  14. Your CDN returns the track audio to the player.
  15. The player starts playback. The player disables most playback capabilities until it can send GET /context and GET /itemWindow and process your responses.
  16. The player sends a GET /context request to your cloud queue server.
  17. Your cloud queue server responds to the GET /context request. Your response includes container metadata, playback policies, and reporting options.
  18. The player sends a GET /itemWindow request to your cloud queue server. The player uses the itemId for track 65 to request a window around this track, specifically tracks 56-75. The previousWindowSize parameter is set to "9" to request tracks 56-64, and the upcomingWindowSize is set to "10" to request tracks 66-75.
  19. Your server responds to the GET /itemWindow request with metadata for tracks 56-75.
  20. Playback continues without interruption. The player sends a GET /itemWindow request to refresh the window as it gets close to the end of the current window. The player updates the new window around the track that is currently playing.

Players poll the cloud queue on a regular basis

Players poll the cloud queue with GET /version on a regular basis. They do so to determine if the cloud queue changed the list of tracks or updated the state of container types, metadata, or playback policies. This background polling occurs at regular intervals. Background polling intervals are different when the group is playing and paused. When paused, players poll the cloud queue every 5 minutes. When playing, players poll the cloud queue every 10 minutes.

Additionally, players fetch new tracks as needed using GET /itemWindow. The player requires a version in your response, so it's not necessary for the player to send GET /version in this case. After the player gets a new item window, it resets the polling interval to another 10 minutes.

Your app can tell players to immediately poll the cloud queue

Your app can use the refreshCloudQueue Control API command to tell the player to immediately fetch an updated window of tracks from the server. You may want to do this if, for example, a user deleted the current track. This avoids the delay the user might experience if the player waited until the next poll interval.


Players retry unsuccessful cloud queue requests

Players retry background cloud queue requests such as GET /context, GET /itemWindow, and GET /version when they receive an unexpected response. Unexpected responses include:

  • WebSocket request timeouts
  • Aborted WebSockets
  • Players can’t connect
  • Players can’t resolve the Domain Name System (DNS)
  • Unexpected HTTP response codes

When this happens, players retry the request up to three times. This results in four total attempts, including the original request. Players wait 10 seconds after the original request to retry and then another 10 seconds before each retry.

If the player uses all the retries without a successful response, it will play what it has left in the current itemWindow and then enter a pause state. The player keeps the playback session valid when this happens.


Players send API requests over HTTPS

All Cloud Queue API requests from players to cloud queue servers will be over HTTPS.

SSL connection establishment adds additional overhead and latency. To reduce this overhead of the SSL handshake, session resumption is used. Players support the Session ID-based resumption mechanism, as defined in the TLS 1.0 RFC 2246.

👍

Server certificate verification

Be sure that the root certificate used to sign the server certificate presented by your cloud queue servers is a well-known trusted CA.


Authorization for media and the cloud queue

Access tokens (such as OAuth tokens) can be used as the authorization mechanism to allow users access to playing audio resources on media servers, and to allow players access to the track metadata on the cloud queue server. Your app can pass an access token to the player with an "Authorization" HTTP header in the httpAuthorization parameter of the loadCloudQueue Control API command. The player will use the access token when playing any audio tracks on the cloud queue, and when calling the Cloud Queue APIs.

HTTP Request HeaderHeader Value
Authorization<long-auth-token-string>

Since access tokens expire, you can return a "X-Updated-Authorization" HTTP header to the player from your cloud queue or media server, which updates the token that the player should use in subsequent requests. This is the mechanism by which your cloud queue server or your media server can auto-refresh the access token used by the player to ensure that playback continues indefinitely if it is not paused by the user. By following this practice, the user will never have to manually re-authenticate during playback of an arbitrarily long cloud queue, as long as playback is not interrupted.

HTTP Response HeaderHeader Value
X-Updated-Authorization<long-auth-token-string>

If provided, the Sonos players will always include the HTTP "Authorization: httpAuthorization" header whenever they make requests to your Cloud Queue APIs. They will also send them when they request the resources for cloud queue items from your media servers.

Note that you could use the HTTP Authorization data in your cloud queue server to infer the user account or queue identifier in order to return the appropriate cloud queue instance data. See Communicate user identity in the base URL for another method of doing this.


Unique string for the lifetime of the playback session

You may want to differentiate cloud queue instances that are simultaneously serving the same household. For example, in Harry and Sally's household, this would enable Harry to use his account on your service in the kitchen and Sally to use her account on your service in the den. To enable this, the player provides a unique string for the lifetime of the playback session the "X-Sonos-Playback-Id" HTTP request header.

HTTP Request HeaderHeader Value
X-Sonos-Playback-IdUnique string that is constant for the lifetime of the playback session.

The string may or may not change after your app or implementation loads a cloud queue. The value remains constant if the music is moved to a different player within the Sonos controller.


Cloud queue base URL and API version

The base URL identifies your cloud queue. It should consist of a URL with the version at the end. The version will allow players to only use newer Cloud Queue API functionality when your service supports it. For example:

https://www.example.com/musicqueue/v2.1/

You can also communicate the user identity in the base URL. See below for details.

Some guidelines around the Cloud Queue API Version:

  • Following the Semantic Versioning specification, the base URL must end with "/v.[/]".
  • We increment the MAJOR version when incompatible changes are made.
  • We increment the MINOR version when backward compatible changes are made, such as new response data with no new resources.
  • We do not use a PATCH digit; the player will parse, but ignore the PATCH digit.
  • The player will parse, but ignore any pre-release and build metadata. For example, the player will consider "1.0.0-alpha.1" and "1.0.0-beta+exp.sha.5114f85" both as version 1.0.
  • The trailing slash is optional. It is allowed but not required as part of the base URL.

To tell Sonos which cloud queue to play, your app must pass the base URL and version in the queueBaseUrl parameter of the loadCloudQueue Control API command. See the Cloud Queue API for the latest version number.

The current version of the Cloud Queue API is v2.1. We also mark the changes to each endpoint in the documentation by calling out features and parameters as "Added in v#.#". We list the previously supported features below the most recently supported version.


Communicate user identity in the base URL

Other than the version specification described above, the form of the base URL is up to you. For example, it could be a different URL with the user account or queue identifier encoded as a RESTful parameter for each cloud queue instance. For example:

http://www.example.com/cloudqueue/1243328192349892/v2.0/

Alternatively, it could be a fixed URL where you differentiate the actual cloud queue data based on the httpAuthorization.


Use tombstones for deleted tracks

When a user deletes a track from your cloud queue, you should use a tombstone to flag it as deleted. This will enable you to keep track of its position in the cloud queue so that when the player requests a window centered around this deleted track, the cloud queue can then determine the next track and return a window centered around that track instead. This enables a smooth user experience during playback.

To set a tombstone on a track, set the deleted item property as true when your cloud queue server returns the item as a response to a GET /itemWindow Cloud Queue API request.

Your server should maintain tombstone information for deleted tracks until the player has time to fetch a new window with a GET /itemWindow request. The length of this time depends on the length of the tracks in the window and the time it takes for the player to sync up with your cloud queue. We recommend maintaining tombstone information for a few hours. The player will always pass the itemId of the track that is currently playing.

There is no mechanism for players to listen for notifications from cloud queues when there is a change in the queue. So, in order to ensure players are responsive when the user deletes the currently playing track, your app should send a refreshCloudQueue Control API command to the player to request that the player immediately fetch an updated window of tracks. The player will fetch an updated window of tracks containing a tombstone for the track that was deleted. It will stop playing the current track and proceed to play the track after the deleted track.