Playback and Preset Assignment

The Audiogum Platform abstracts playback of playlists, albums, audiobooks and other playable audio from various services behind a simple set of APIs. We call this concept playables.

The starting point is creating an alias to the content that can be easily referenced from a hardware device or app. Companion apps can create these aliases for the user, that in turn can be used from a single "play" button or assigned to some form of preset if the device supports such a concept.

A playable is the piece of data describing the parameters needed for the client to play something and potentially some state associated with playing it. The playable data is specific to a user and also locked to specific devices owned by the user.

We offer this abstraction for these reasons:

1) to simplify firmware integration with services - rather than dealing with every different service you wish to integrate, handling different API authentication schemes and response formats, Audiogum offer a consistent API that returns a list of items to play.

2) to cope with different types of content - not just playlists created by a user or service, but also to allow dynamic personal playlists that can play on any service your customer has a subscription to. In some countries, it is possible to play a playlist that sources media from multiple music services.

3) adding new services requires minimal change to your apps and firmware (typically none). With the config-driven approach, new services can light up without new releases (providing the service does not require an SDK for playback).

Examples of playables:

  • A specific playlist (personal or public) from a specific service (e.g. Tidal)
  • An internet radio station
  • An artist-seeded Audiogum dynamic playlist (e.g. played via Napster)
  • An Audiogum personalised dynamic playlist (e.g. played via Tidal)
  • A voice-seeded Audiogum dynamic playlist


Creating a playable

We offer a simple API for apps to create a playable alias that returns an id that can be used to assign to a preset button or some other hardware interaction on a device that signals playback should start.

Preparing to play content

Playable parameters

The content to play is determined by the parameters part of the playable request, as shown in the examples below. The list of supported services and types of content is growing quickly - please ask for more up to date information on features and roadmap.

In most cases, the parameters object has the following fields:

  • type: the type of content. Currently supported values are:
    • album
    • playlist
    • song
    • internetradio
    • audiobook
    • dynamicplaylist
  • service: the service from which the content will come (if relevant)
  • ref: the identifier or uri of the content according to the service (if relevant)

Device ID

The playable request should contain the unique deviceid of the device that will perform the playback.

(In the case of the app performing playback, this can be omitted. See playback in app)

Error responses

The following error conditions may occur:

StatusMeaning
403For services requiring a linked account, no token was found for the user - the user should sign in to proceed
409User has revoked access - a previously linked account has had the access token revoked - the user should sign in again to proceed
410No items found to play on service - the item may have been removed or a dynamic playlist request may be too specific to allow enough items
499The service returned a custom error message - see error field in response and examples below
599The service call failed or timed out - try again

Example: Create a playable for a Tidal playlist

App request:

POST /v1/user/playables
{
  "parameters": {
    "type": "playlist",
    "service": "tidal",
    "ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
   },
   "deviceid": "X28943.12389.YURW2892"
}

Response:

The response gives the id - this will either be a new id if the parameters have not been used before by the user, or an existing one if the parameters have been used - i.e. previously assigning the same playlist to a different device.

201 Created
{
   "id": "d84af5a1c31b4fb5a186ee6671be53a9",
   "name": "Headbangers Ball: 1987-1995",
   "images": {
      "640x640": "https://resources.tidal.com/images/a8cba84e/378e/4a99/8fd8/12d9f51da813/640x428.jpg",
      "320x320": "https://resources.tidal.com/images/a8cba84e/378e/4a99/8fd8/12d9f51da813/320x214.jpg",
      "160x160": "https://resources.tidal.com/images/a8cba84e/378e/4a99/8fd8/12d9f51da813/160x107.jpg"
    },
   "parameters": {
     "type": "playlist",
     "service": "tidal",
     "ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
   }
}

Example: Create a playable for a Napster album

Playables for albums are created in the same way as playlists.

App request:

POST /v1/user/playables
{
  "parameters": {
    "type": "album",
    "service": "napster",
    "ref": "alb.54624358"
   },
   "deviceid": "c4:67:b5:08:14:c5"
}

Response:

201 Created
{
  "id": "56d9e2db8b0b44278fa2f89a16df6ff5",
  "name": "Origin Of Symmetry",
  "parameters": {
    "type": "album",
    "service": "napster",
    "ref": "alb.54624358"
  },
  "images": {
    "default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
  }
}

Example: Create a playable for a single song from KKBOX

App request:

POST /v1/user/playables
{
  "parameters": {
    "type": "song",
    "service": "kkbox",
    "ref": "ClpUmFvYFKtg28L3dM"
   },
   "deviceid": "c4:67:b5:08:14:c5"
}

Response:

201 Created
{
  "id": "56d9e2db8b0b44278fa2f89a16df6ff5",
  "name": "最怕冷戰",
  "parameters": {
    "type": "song",
    "service": "kkbox",
    "ref": "ClpUmFvYFKtg28L3dM"
  },
  "images": {
    "default": "https://i.kfs.io/album/tw/161877,0v3/fit/500x500.jpg"
  }
}

Example: Create an artist-seeded dynamic playlist playable

See dynamic playlist for supported features and details of parameters combinations.

POST /v1/user/playables
{
   "parameters": {
      "type": "dynamicplaylist",
      "expandartists": true,
      "artists": [
        {
          "id": "05e12a2e6206411086ffb6a48dddb64d",
          "name": "Muse"
        }
      ]
   },
   "deviceid": "X54322.66456.PPO38DF2"
}

Response:

201 Created
{
   "id": "4605791260b4408f9683146b91b236e2",
   "name": "Muse",
   "images": {
        "640x640": "http://artistimgs.audiogum.com/640x640/05e12a2e6206411086ffb6a48dddb64d.jpg",
        "320x320": "http://artistimgs.audiogum.com/320x320/05e12a2e6206411086ffb6a48dddb64d.jpg"
   },
   "parameters": {
      "type": "dynamicplaylist",
      "variant": "artist",
      "expandartists": true,
      "artists": [
        {
          "id": "05e12a2e6206411086ffb6a48dddb64d",
          "name": "Muse"
        }
      ]
   }
}

Example: Create an internet radio station playable

POST /v1/user/playables
{
   "parameters": {
      "type": "internetradio",
      "service": "vtuner",
      "ref": "3212"
   },
   "deviceid": "X54322.66456.PPO38DF2"
}

Response:

201 Created
{
   "id": "4605791260b4408f9683146b91b236e2",
   "name": "BBC Radio 2",
   "images": {
        "default": "http://logo.vtuner.net/007452/logotv/logo-3159.jpg"
   },
   "parameters": {
      "type": "internetradio",
      "service": "vtuner",
      "ref": "3212"
   }
}

Note about images: where the aspect ratio and size of images are known, these are represented in the JSON, for example 320x320. Otherwise, as in the vTuner case, when the images are of arbitrary aspect ratio, this is represented as default.


Using a playable to start playback from device

The device will not have a user context, nor their credentials for connecting to music services. Each playable is only accessible to specific devices that were associated with it by the user via an app, as described above. At the point of consumption of a playable, we return media references which, dependent upon the underlying music service, may point to actual media or require an SDK integration. In the latter case, we also return a up to date credentials (e.g. a service access_token) to pass to the local SDK.

Playing content

Determining how to play content

The device does not need to know the source of the media until the point of playback. Retrieving details about the playable will return the list of items to play along with indication of how to play them:

  • playbackprotocol indicates the method to play content - one of HTTP, HTTPS or SDK. For SDK, a music service SDK is required to play content - if this is the case, SDK credentials will also be provided.
  • timelimited indicates that content URLs are time limited to the specified number of seconds. See refreshing time-limited content URLs.
  • playbackcodec is returned with each item (where possible), this field shows the codec used – typical values will be mp3, aac, flac, etc.
  • total indicates the number of items to play - for example, Internet Radio services will only return one item - a continuous stream of content.

The playable details gives a list of one or more items that are individual streams to play. Each item will either have a streamurl when the service has a playbackprotocol of HTTP or HTTPS, or in the case of a service that requires an SDK, the ref attribute can be passed to the SDK to identify the track to play. Any required SDK credentials are also supplied to initialise the SDK in the services object of the response.

Continuing playback with automatic continuation

As shown in the sequence diagram above, the API is designed so that the device can get songs from a playlist a few at a time. When these are exhausted, the device should make another request to receive more and continue. The index position (startindex) in the playlist is maintained by the Audiogum service. This simplifies the client implementation and enables the following use cases:

  • Audiogum dynamic playlists that can continue indefinitely
  • Playlists can continue where they left off (for example when switching between presets or switching between speakers) rather than repeating songs

The same approach of making another request when the list of items has been played applies to all kinds of playable, except where there is only ever one item therefore no need to request more. One example of this is internet radio where there is only one stream. This will be indicated with a total value of 1. Note that in other cases (for example Douban FM in China) there may be only one item in the response, but no total in response. This indicates there may be more to request.

The API supports optionally specifying the index position to start at using the startindex query parameter, and the number of items to return by using the itemsperpage parameter. The current position is indicated in response by the startindex value. Specifying a startindex overrides the automatic continuation and sets it to the next position for a subsequent request that does not specify startindex.


We recommend the following approach:

  • Omit the itemsperpage query parameter to accept the Audiogum defaults. These are generally small to reduce the chance of expiry of access but may vary by type of playable and/or service - this avoids the need to re-request tracks or credentials.
  • Omit the startindex query parameter, whether starting or continuing, to make use of the automatic continuation maintained by the service, except in the cases described below.


The following cases may require the device to pass the startindex parameter:

  • If the app instructs the device to play a playable from a specific position: For example an app may show the list of songs when browsing a playlist, or future and previous songs from a currently playing playlist, and allow the user to begin from a specific song
  • If device controls are used to rewind to previous songs ("skip back"): In this case, if there are no more earlier songs in the current response, earlier results may be requested using the startindex parameter. We recommend calculating startindex based on the current startindex and itemsperpage found in response, i.e. new startindex = startindex - itemsperpage, or zero if greater.Note: We recommend only doing this for going backwards. For the skip forwards case we advise omitting the startindex to make use of the automatic continuation as described above.

In the case where the device specifies startindex and/or itemsperpage parameters, the request will fail with response code 400 if these fall out of range of the underlying playlist or currently generated dynamic playlist. In the dynamic case, the sequence is indefinite, but we don't keep infinite history or generate too far ahead. If this happens, our advice would be to try again, omitting the startindex so that the service will give an appropriate next set of songs.

Example: Get a Napster album playable

As with the previous example, each item in the items array returned is a song to play - however, Napster serve HTTPS progressive downloads, so a streamurl is included in the item that points to the stream.

Device request: GET /v1/playables/56d9e2db8b0b44278fa2f89a16df6ff5

Response:

200 OK
{
  "userid": "c0cce16a3952487db2cfc08a93edb9e1",
  "itemsperpage": 2,
  "updated": "2017-07-03T16:35:08.764Z",
  "images": {
    "default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
  },
  "name": "Origin Of Symmetry",
  "created": "2017-07-03T16:34:49.766Z",
  "startindex": 0,
  "total": 12,
  "id": "c4a1192fe24f468db86fddde771a83da",
  "items": [
    {
      "service": "napster",
      "index": 0,
      "albumname": "Origin Of Symmetry",
      "ref": "tra.54624359",
      "images": {
        "default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
      },
      "playbackcodec": "AAC",
      "name": "New Born",
      "streamurl": "https://xxxxxx",
      "type": "song",
      "duration": 363,
      "artistdisplayname": "Muse",
      "bitrate": 320,
      "artists": [
        {
          "type": "artist",
          "service": "napster",
          "ref": "art.7035",
          "name": "Muse"
        }
      ]
    },
    {
      "service": "napster",
      "index": 1,
      "albumname": "Origin Of Symmetry",
      "ref": "tra.54624360",
      "images": {
        "default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
      },
      "playbackcodec": "AAC",
      "name": "Bliss",
      "streamurl": "https://xxxxx",
      "type": "song",
      "duration": 252,
      "artistdisplayname": "Muse",
      "bitrate": 320,
      "artists": [
        {
          "type": "artist",
          "service": "napster",
          "ref": "art.7035",
          "name": "Muse"
        }
      ]
    }
  ],
  "parameters": {
    "ref": "alb.54624358",
    "service": "napster",
    "type": "album"
  },
  "services": {
    "napster": {
      "userid": "xxxx",
      "subscription": "premium",
      "playbackprotocol": "https",
      "timelimited": 4440
    }
  }
}

Example: Get a dynamic playlist playable

In the case of dynamic playlists, there is no total since the length is indefinite.

See dynamic playlist for supported features and details of parameters combinations.

Device request: GET /v1/playables/25ce82bab9f24ddbb4d5863def2068b8

Response:

200 OK
{
   "id": "25ce82bab9f24ddbb4d5863def2068b8",
   "type": "playable",
   "name": "Muse",
   "images": {
        "640x640": "http://artistimgs.audiogum.com/640x640/05e12a2e6206411086ffb6a48dddb64d.jpg",
        "320x320": "http://artistimgs.audiogum.com/320x320/05e12a2e6206411086ffb6a48dddb64d.jpg"
   },
   "userid": "ab76a53c69aa4c918135399715fdf362",
   "items": [
     {
      "id": "e185d67057a84c11b4f9f7b5ef9edc1c",
      "type": "song",
      "name": "Citizen Erased",
      "artists": [
        {
          "id": "05e12a2e6206411086ffb6a48dddb64d",
          "name": "Muse"
        }
      ],
      "artistdisplayname": "Muse",
      "ref": "7780557",
      "service": "tidal",
      "streamurl": "https://..."
     }
     // more songs
   ],
   "services": {
    "tidal": {
      "userid": "userid123",
      "subscription": "premium",
      "country": "gb",
      "client_id": "xxxxxx",
      "access_token": "xxxxxx",
      "playbackprotocol": "https"
     }
   },
   "parameters": {
      "type": "dynamicplaylist",
      "variant": "artist",
      "expandartists": true,
      "artists": [
        {
           "id": "05e12a2e6206411086ffb6a48dddb64d",
           "name": "Muse"
        }
      ]
   },
   "startindex": 0,
   "itemsperpage": 5
}

Example: Get an internet radio playable

For internet radio, instead of songs, the items array contains a single internetradio item - note that total = 1. The item includes the streamurl for the audio stream.

Device request: GET /v1/playables/baa576b91981493c9652093ee7bd4e94

Response:

200 OK
{
  "userid": "c0cce16a3952487db2cfc08a93edb9e1",
  "images": {
    "default": "https://api.audiogum.com/v1/vtuner/image?target=http%3A%2F%2Flogo.vtuner.net%2F007452%2Flogo%2Flogo-3159.jpg"
  },
  "name": "BBC Radio 2",
  "createdtime": "2016-10-04T07:32:00.172Z",
  "streamurl": "http://bbcmedia.ic.llnwd.net/stream/bbcmedia_radio2_mf_p",
  "updatedtime": "2017-01-01T11:17:06.044Z",
  "total": 1,
  "id": "baa576b91981493c9652093ee7bd4e94",
  "items": [
    {
      "ref": "3159",
      "images": {
        "default": "https://api.audiogum.com/v1/vtuner/image?target=http%3A%2F%2Flogo.vtuner.net%2F007452%2Flogo%2Flogo-3159.jpg"
      },
      "service": "vtuner",
      "name": "BBC Radio 2",
      "type": "internetradio",
      "streamurl": "http://bbcmedia.ic.llnwd.net/stream/bbcmedia_radio2_mf_p"
    }
  ],
  "parameters": {
    "ref": "3159",
    "service": "vtuner",
    "type": "internetradio"
  },
  "services": {
    "vtuner": {
      "playbackprotocol": "http"
    }
  }
}

Example: Get service access data for a playable

For SDK music service integrations, this interaction is to cater for the case where the device gets a token expiry response from the 3rd party music service or notices that the expiry time has been reached. This API returns a fresh token on behalf of the user. This interaction is protected by checking the deviceid.

Device request: GET /v1/playables/25ce82bab9f24ddbb4d5863def2068b8/services/tidal

Response:

200 OK
{
  "userid": "userid123",
  "subscription": "premium",
  "country": "gb",
  "client_id": "xxxxxx",
  "access_token": "xxxxxx",
  "playbackprotocol": "https"
}

Refreshing time-limited content URLs

In the case of http(s) streaming where a value of timelimited is provided, this value can be used to calculate when the stream URLs will expire. This could happen for example if playback is paused for some time. When resuming it may be necessary to request new stream URLs for the same items. This can be done by requesting the specific items by their ref values as shown in the following example:

POST /v1/playables/d84af5a1c31b4fb5a186ee6671be53a9/streamurls/tidal
{
  "items": [
    {
       "ref": "46890235"
    },
    {
       "ref": "10220880"
    }
  ]
}

Response:

200 OK
{
  "items": [
    {
       "ref": "46890235",
       "service": "tidal",
       "streamurl": "https://..."
    },
    {
       "ref": "10220880",
       "service": "tidal",
       "streamurl": "https://..."
    }
  ]
}

Note that the playable id is provided in the path of this request so that the service can protect this interaction in the same way: it will fail if the requesting device is not associated with the playable.

Disabling forward/backward buttons

Some content features exposed via Audiogum playables do not allow skipping to next and/or previous items.

In this case the device may disable (switch off) its next and previous buttons based on flags indicating this state in the playables response.

If the field controls is included in the response, with boolean fields skipnext and/or skipprev, false values indicate skipping should be disabled. If such fields are not present, this indicates controls should be allowed.

Example playables response where both next and previous are disabled:

{
  "id": "c847ff50d8ea4b158247f1e47c096d29",
  "userid": "b746b99cdb99455bbd0dfcfe2c5152b4",
  "name": "BBC Radio 1",
  …
  "controls": {
    "skipnext": false,
    "skipprev": false
  },
  …
  "items": [
    …
  ],
  "parameters": {
    …
  },
  "services": {
    …
  }
}

Starting playback at a specified offset

Longer content such as podcasts and audiobooks can have an offset field indicating that the item should start playing at the number of seconds in.

Example of audiobook content where the user has listened to part of the content previously, leading to an offset being sent indicating to start at 975 seconds (i.e. 16 minutes and 15 seconds) into the stream:

{
  "items":[
    {
      "service": "audiobooks",
      "type": "audiobook",
      "ref": "xxx",
      "name": "...",
      "images": { ... },
      "streamurl": "...",
      "duration": 34505000,
      "offset": 975
    }],
  "startindex": 0,
  "total": 1
}

The current position is stored with the content service automatically when the user stops the content and a stop analytics event is received.

Shuffle

For many content services, Audiogum offers a shuffle feature for playables. When activated, the shuffle feature allows clients to continue playback in the usual way and let the Audiogum platform randomise the track sequence.

Before offering a shuffle button, the client should check for the presence of the shuffle capability in the service configuration. If the service supports shuffling, the client may include "shuffle": true when creating a playable:

POST /v1/user/playables
{
  "parameters": {
    "type": "playlist",
    "service": "tidal",
    "ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
   },
   "deviceid": "X28943.12389.YURW2892",
   "shuffle": true
}

The user may wish to begin shuffling after they have listened to some tracks of an album or playlist. In this case, shuffle can be activated for an existing playable. Similarly, the user may decide to stop shuffling after they have listened to some shuffled tracks. In this case, shuffle can be deactivated for an existing playable:

APIPurpose
PUT /v1/user/playables/{id}/shuffleActivates shuffle for a playable that is not currently shuffling
DELETE /v1/user/playables/{id}/shuffleDeactivates shuffle for a playable that is currently shuffling

Note that on activating or deactivating shuffle, the client MUST tell the playback device to discard all buffered tracks. After the current track completes, the playback device should request new tracks in the normal way via GET /v1/playables/{id}.

Loop

Where appropriate Audiogum offers a loop feature for playables. When activated, the loop feature allows clients to continue by playing the first track after the last track has played (loop all) or play a single track within a playable repeatedly (loop one).

Before offering a loop button, the client should check for the presence of the loop capability in the service configuration. If the service supports looping, the client may include "loop": "all" or "loop":"one" when creating a playable:

POST /v1/user/playables
{
  "parameters": {
    "type": "playlist",
    "service": "tidal",
    "ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
   },
   "deviceid": "X28943.12389.YURW2892",
   "loop": "all"
}

It's also possible to supply both "shuffle":true and "loop":"all" (or "loop":"one") when creating a playable, to create a playable that is both shuffled and looped. The shuffle order will remain the same with each loop.

The user may wish to begin looping after they have listened to some tracks of an album or playlist. In this case, loop can be activated for an existing playable. Similarly, the user may decide to stop looping after they have listened for some time. In this case, loop can be deactivated for an existing playable:

APIPurpose
PUT /v1/user/playables/{id}/loopActivates loop for a playable that is not currently looping
DELETE /v1/user/playables/{id}/loopDeactivates loop for a playable that is currently looping

Note that on activating or deactivating loop, the client MUST tell the playback device to discard all buffered tracks. After the current track completes, the playback device should request new tracks in the normal way via GET /v1/playables/{id}.

Dynamic Playlists

Dynamic playlists are a special form of playable that generate a sequence of songs on-demand. They are all achieved using the API as described above, varying only by the parameters.

Unlike a normal playlist the sequence of songs will vary each time a dynamic playlist is generated for the same parameters. Also there is no definite length, the sequence will be continuously generated on demand.

The following variants of dynamic playlist are currently supported, with more features planned for future:

Artist playlist and artist radio

Supply a list of one or more artist ids from Audiogum's catalogue to get a dynamic playlist containing a variety of songs by those artists.

Optionally include "expandartists": true to include recommended similar artists in the playlist.

Example parameters object:

{
  "type": "dynamicplaylist",
  "expandartists": true,
  "artists": [
    {
      "id": "05e12a2e6206411086ffb6a48dddb64d"
    },
    {
      "id": "2a2f37d22d1c4bbdb8db21110e2772dc"
    }
  ]
}

Genre radio

Supply a list of one or more music genres from Audiogum's catalogue to get a dynamic playlist containing songs from artists in those genres.

If the user has artists in those genres in their taste profile, a personalised selection of songs from those artists and recommended similar artists will be used, or otherwise from popular artists in the genres.

Example parameters object:

{
  "type": "dynamicplaylist",
  "genres": [
     { "id": "indie_alternative" },
     { "id": "electronica" }
  ]
}

Play Me - taste-based radio

Supply neither artists nor genres to create a dynamic playlist based on the user's taste profile. This will include songs from a selection of the user's liked artists in addition to personalised recommendations for discovery of new music and re-discovery of old favourites.

Example parameters object:

{
  "type": "dynamicplaylist"
}

Filtered Play Me

Taste-based radio can be optionally tuned with filters to use a subset of the user's taste. Currently genre filters are supported: supply one or more genres from the user's taste profile to filter out artists and recommendations from other genres.

Example parameters object:

{
  "type": "dynamicplaylist",
  "filters": {
     "genres": [
        { "id": "indie_alternative" },
        { "id": "electronica" }
     ]
  }
}

Note that this example behaves differently to the genre radio example above (where genres are specified at the top level of parameters rather than inside filters):

  • In genre radio, the generated playlist attempts to represent each specified genre roughly equally,while in filtered Play Me the proportions of the genres are roughly representative of the user's tasteas per the non-filtered version.
  • In genre radio, the user can specify genres for which they have few or zero liked artists in taste profile.Genre radio will use popular artists in the genre where necessary. In the filtered Play Me case only artistsfrom the users taste profile and recommended similar artists are used, making it always personalised.

Multi-user taste-based radio

Supply a list of user ids to create a dynamic playlist based on multiple user taste profiles. This will include songs from each user's liked artists and also recommendations selected based on suitability for the group of users - i.e. common ground.

Example parameters object:

{
  "type": "dynamicplaylist",
  "users": [
    { "id": "3b2c9eeda03946e5af33d8aecfdc7a47" },
    { "id": "54c06c0cfdc2423aa40d1417096eced1" }
  ]
}

An example implementation of multi-user radio is available in our Friend Mix app.

Service-specific Error responses

See Content Services Errors for more details on error conditions.

Playback in app

The interactions described above assume that a companion app is controlling a connected device such as a speaker which will perform the playback.

It is possible that the app needs to do the playback itself (for example to play on a Bluetooth-only speaker). For this use case the app needs to perform both the roles, i.e. as described in both sections Creating a playable and Using a playable to start playback from device.

For convenience the app does not need to pass a deviceid when creating a playable. When this is omitted, the service assumes the deviceid of the device making the request, based on its authorization token, therefore the same device can request the playable for playback.