ZeroConf API
The Spotify Connect ZeroConf API is an HTTP interface used for announcing and securely logging in to a hardware device. It enables a Spotify client, such as an iOS or Android device, to perform a login to a hardware device without the user entering a password.
The server providing the ZeroConf API can either be running inside the eSDK itself, or be implemented by a Spotify hardware partner. If the builtin ZeroConf server is enabled (see SpConfig::zeroconf_serve) and working satisfactorily on the hardware device, no ZeroConf implementation is needed from the Spotify hardware partner. Otherwise, it must be implemented to support the Spotify Connect feature.
Implementing the ZeroConf API, when not using the internal builtin ZeroConf service, requires:
- a HTTP server
- a JSON encoding and decoding library
- a mDNS discovery service compatible with Apple's Bonjour.
The partner must then implement the behavior described in this document and hook it up appropriately with the calls to the Spotify Embedded API and their own integration code.
If SSL is supported by the web server, the ZeroConf API should be accessible via HTTPS. When HTTPS is enabled, it is preferred that non-secure HTTP access is disabled.
This is the ZeroConf process:
- The Spotify desktop/smartphone client and the hardware device connect to the same network.
- The device's ZeroConf API service is discovered by the client using a service discovery protocol.
- The client logs in to the device using the ZeroConf API via HTTP.
- The device is now ready to be used with Spotify Connect!
Discovery Service
The hardware device must use mDNS/DNS-SD in a way compatible with Apple's Bonjour, to advertise its IP address, the port of its HTTP service, and the path to the ZeroConf API implementation on the web server.
The DNS-SD service type is _spotify-connect._tcp
.
- A
SRV
record should be specified with the IP address and port - A
TXT
record should be provided withCPath=<path to ZeroConf implementation>
. If a ZeroConf implementation is reachable onhttp://host:port/zeroconf
, the TXT record shall beCPath=/zeroconf
.
ZeroConf API
See the appendix for error codes and corresponding error strings.
- Requests are made over HTTP using the GET or POST method.
- All requests are answered by a response.
- Every valid request has the action variable.
- The
application/x-www-form-urlencoded
encoding is used for POST requests.
- Responses are JSON objects (with Content-Type:
application/json
). - HTTP status code should match corresponding status from HTTP Status section.
- All strings must support UTF-8 character encoding. All strings returned by the Spotify Embedded SDK will be in UTF-8 encoding.
- If requests are handled on their own thread, keep in mind that the Embedded SDK is not thread-safe. You need to ensure that no two threads invoke SDK APIs at the same time. See threading.
- If ZeroConf is turned off on a device that is inactive (ie a slave in a multiroom configuration), mDNS should be turned off. It's not enough to just turn off the webserver endpoints - they will still be hit by clients, spending unnecessary effort.
Mandatory fields in all responses:
Field | Required | Type | Description |
---|---|---|---|
status | Yes | Integer | A code indicating the result of the operation |
statusString | Yes | String | The string describing the status code |
spotifyError | Yes | Integer | The last error code returned by a Spotify API call or the SpCallbackError() callback |
responseSource | Yes | String | The entity responding on the zeroconf message, i.e. your company name or name of the service. [1] |
Success example:
_10{_10 "status": 101,_10 "statusString": "OK",_10 "spotifyError": 0,_10 "responseSource": "Company name", "other data depending on action"_10}
Failure example:
_10{_10 "status": 402,_10 "statusString": "ERROR-SPOTIFY-ERROR",_10 "spotifyError": -119,_10 "responseSource": "Company name"}_10}
Add User
The API does not use any passwords. Only the user name and credentials blob are needed when logging in to Spotify.
Login should be performed by calling SpConnectionLoginZeroConf()
with the data in this message.
Before sending a response, the application must do the following:
- If a user's credentials are already stored, the existing user must be logged out
using
SpConnectionLogout()
and the stored data must be deleted from persistent storage (same as resetusers resetUsers request). - Verify that SpConnectionLoginZeroConf() returns #kSpErrorOk (and fail the request if it returns something else).
- Verify that login is successful by waiting for the #kSpConnectionNotifyLoggedIn event (and fail the request if SpCallbackError() is invoked).
The encrypted blob received in this ZeroConf message must not be saved. On a successful login, the SpCallbackConnectionNewCredentials() callback will be invoked with the credentials to save to persistent storage.
The user's credentials must not be saved to persistent storage unless the login is successful.
The status must not be returned until a #kSpConnectionNotifyLoggedIn or SpCallbackError() event is received.
If any required field is not specified, the response must be an error of type InvalidArguments, and the login function must not be called.
HTTP method: POST
.
Extra parameters:
Field | Required | Type | Description |
---|---|---|---|
userName | Yes | String | User name to login |
blob | Yes | String | Encrypted credentials blob for the user |
clientKey | Yes | String | Decryption key provided by client |
loginId | No | String | Login ID for this addUser request |
version | No | String | Spotify client ZeroConf API version number, ie "2.10.0". Can be used to handle compatibility issues |
tokenType | Yes | String | Token type provided by the client |
Return: only status.
Get Information
HTTP method: GET
.
Extra parameters:
Field | Required | Type | Description |
---|---|---|---|
version | No | String | Spotify client ZeroConf API version number, ie "2.10.0". May be used to handle compatibility issues |
Return:
Field | Required | Type | Description |
---|---|---|---|
version | Yes | String | ZeroConf API version number: "2.10.0" |
deviceID | Yes | String | The SpZeroConfVars::device_id field returned by SpZeroConfGetVars() . [1] deviceID has to be unique enough to be used for identification on a local network. |
publicKey | Yes | String | The SpZeroConfVars::public_key field returned by SpZeroConfGetVars() [1] |
remoteName | Yes | String | The SpZeroConfVars::remote_name field returned by SpZeroConfGetVars() [1] |
deviceType | No | String | The SpZeroConfVars::device_type field returned by SpZeroConfGetVars() [1] |
brandDisplayName | Yes | String | User-facing brand name of the device [2] |
modelDisplayName | No | String | User-facing model name of the device [2] |
libraryVersion | Yes | String | The SpZeroConfVars::library_version field returned by SpZeroConfGetVars() [1] |
resolverVersion | Yes | String | The SpZeroConfVars::resolver_version field returned by SpZeroConfGetVars() [1] |
groupStatus | Yes | String | The SpZeroConfVars::group_status field returned by SpZeroConfGetVars() [1] |
tokenType | Yes | String | The SpZeroConfVars::token_type field returned by SpZeroConfGetVars() [1] |
clientID | Yes | String | The SpZeroConfVars::client_id field returned by SpZeroConfGetVars() [1] |
productID | Yes | Integer | The SpZeroConfVars::product_id field returned by SpZeroConfGetVars() [1] |
scope | Yes | Integer | The SpZeroConfVars::scope field returned by SpZeroConfGetVars() [1] |
availability | Yes | String | The SpZeroConfVars::availability field returned by SpZeroConfGetVars() [1] |
aliases | No | Array | The SpZeroConfVars::aliases field returned by SpZeroConfGetVars() [1] |
supported_drm_media_formats | No | Array | The SpZeroConfVars::supported_drm_media_formats field returned by SpZeroConfGetVars() |
supported_capabilities | No | Integer | Bitmasked integer representing list of device capabilities. The SpZeroConfVars::supported_capabilities field returned by SpZeroConfGetVars() |
[1]Note The application is responsible for properly escaping special characters in the strings returned by SpZeroConfGetVars before sending them as part of the JSON response. See the JSON standard for the requirements.
[2]Note Other Spotify clients may present brandDisplayName and the optional modelDisplayName to the user in the Connect menu and in other places. These strings may contain UTF-8 characters. The limitations of SpConfig::brand_name and SpConfig::model_name do not apply. However, during the certification process, we will verify that the fields are set to reasonable values that do not confuse users and can be displayed correctly by other Spotify clients.
Example:
Request:
_10GET http://192.168.10.1:8080/cpath?action=getInfo&version=2.10.0
Response from device without aliases. The field "remoteName"
defines the name to be displayed for the device.
_27{_27 "status": 101,_27 "statusString": "OK",_27 "spotifyError": 0,_27 "responseSource": "Company name",_27 "version": "2.10.0",_27 "deviceID": "0007F537F5ED",_27 "deviceType": "SPEAKER",_27 "remoteName": "John's \"Super\" Speaker",_27 "publicKey": "<some-string>",_27 "brandDisplayName": "Foo Corp™",_27 "modelDisplayName": "X-2000 Portátil",_27 "libraryVersion": "master-v2.15.1-g7890abcd",_27 "resolverVersion": "1",_27 "groupStatus": "GROUP",_27 "tokenType": "accesstoken",_27 "clientID": "<some-other-string>",_27 "productID": 0,_27 "scope": "streaming",_27 "availability": "",_27 "supported_drm_media_formats": [_27 {"drm": 0, "formats": 35},_27 {"drm": 1, "formats": 35},_27 {"drm": 3, "formats": 1168}_27 ],_27 "supported_capabilities": 1_27}
Response from a device using device aliases. In this case, the field
remoteName
should be empty in the response as the
displayed name for respective alias is defined in the aliases
field.
_39{_39 "status": 101,_39 "statusString": "OK",_39 "spotifyError": 0,_39 "responseSource": "Company name",_39 "version": "2.10.0",_39 "deviceID": "0007F537F5ED",_39 "deviceType": "SPEAKER",_39 "remoteName": "",_39 "publicKey": "<some-string>",_39 "brandDisplayName": "Foo Corp™",_39 "modelDisplayName": "X-2000 Portátil",_39 "libraryVersion": "master-v2.15.1-g7890abcd",_39 "resolverVersion": "1",_39 "groupStatus": "NONE",_39 "tokenType": "accesstoken",_39 "clientID": "<some-other-string>",_39 "productID": 0,_39 "scope": "streaming",_39 "availability": "",_39 "aliases": [_39 {_39 "name": "alias-one",_39 "id": "1",_39 "isGroup": "true"_39 },_39 {_39 "name": "alias-two",_39 "id": "2",_39 "isGroup": "false"_39 }_39 ],_39 "supported_drm_media_formats": [_39 {"drm": 0, "formats": 35},_39 {"drm": 1, "formats": 35},_39 {"drm": 3, "formats": 1168}_39 ],_39 "supported_capabilities": 1_39}
Reset Users
The currently logged in user (if any) should be logged out using SpConnectionLogout. All user credentials should be deleted from the device's local storage. Any other persistent data related to ZeroConf should be cleared to factory-reset defaults.
HTTP method: POST.
Extra parameters: none.
Return: only status.
Device Aliases
Using ZeroConf, it is possible to announce multiple "virtual devices" from a device. This allows the eSDK device to expose, for instance, multiroom zones as ZeroConf devices.
Note There is no built-in support for multiroom audio forwarding in the eSDK. This feature enables an integration to announce partner supplied multiroom audio routing configurations directly in the Spotify app.
Device aliases will show up as separate devices in the Spotify app. When a
device is selected for playback, you will know which alias from the
device_alias_index
parameter in the
SpCallbackSelectedDeviceAliasChanged
callback.
Device Aliases using built-in ZeroConf
If the built-in ZeroConf server is used, there is already support for device aliases, up
to a maximum of SP_MAX_DEVICE_ALIASES
. Simply configure the alias names
in SpConfig::device_aliases,
for example:
_14SpError err;_14struct SpConfig config;_14_14memset(&config, 0, sizeof(config));_14_14..._14config.device_aliases[0].display_name = "My Speaker";_14config.device_aliases[1].display_name = "Kitchen + Dining";_14config.device_aliases[2].display_name = "Upper floor";_14_14config.display_name = NULL; // No display name is used because aliases are defined_14..._14_14err = SpInit(&config);
When using SpConfig::device_aliases, SpConfig::display_name is not used.
Appendix
HTTP Status codes
The following table shows the different responses returned by the HTTP server to a client's request:
Status name | Status | HTTP status code [1] | Status string | Response to | Usage |
---|---|---|---|---|---|
Ok | 101 | 200 | OK | All | Successful operation |
Bad | 102 | 400 | ERROR-BAD-REQUEST | All | Web server problem or critically malformed request |
Unknown | 103 | 500 | ERROR-UNKNOWN | All | Fallback when no other error applies |
NotImplemented | 104 | 501 | ERROR-NOT-IMPLEMENTED | All | Server does not implement this feature |
LoginFailed | 202 | 200 | ERROR-LOGIN-FAILED | addUser | Spotify returned error when trying to login |
MissingAction | 301 | 400 | ERROR-MISSING-ACTION | All | Web request has no action parameter |
InvalidAction | 302 | 400 | ERROR-INVALID-ACTION | All | Web request has unrecognized action parameter |
InvalidArguments | 303 | 400 | ERROR-INVALID-ARGUMENTS | All | Incorrect or insufficient arguments supplied for requested action |
SpotifyError | 402 | 200 | ERROR-SPOTIFY-ERROR | All | A Spotify API call returned an error not covered by other error messages |
[1]Note It is permissible to send HTTP status code 200 in all cases, as long as a valid JSON reply is returned that contains the status, statusString, and spotifyError strings.