Commercial Hardware tools and the eSDK are available only for approved partners

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 with CPath=<path to ZeroConf implementation>. If a ZeroConf implementation is reachable on http://host:port/zeroconf, the TXT record shall be CPath=/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 in \ref appendix.
  • 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. See \ref appendix.
statusString Yes String The string describing the status code. See \ref appendix.
spotifyError Yes Integer The last error code returned by a Spotify API call or the SpCallbackError() callback.

Success example:

{ 
  "status": 101, 
  "statusString": "OK",
  "spotifyError": 0, "other data depending on action"
}

Failure example:

{
  "status": 402,
  "statusString": "ERROR-SPOTIFY-ERROR",
  "spotifyError": -119
}

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.9.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.9.0". May be used to handle compatibility issues.

Return:

Field Required Type Description
version Yes String ZeroConf API version number: "2.9.0"
deviceID Yes String The SpZeroConfVars::device_id field returned by SpZeroConfGetVars(). [*] `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(). [*]
remoteName Yes String The SpZeroConfVars::remote_name field returned by SpZeroConfGetVars(). [*]
deviceType No String The SpZeroConfVars::device_type field returned by SpZeroConfGetVars(). [*]
brandDisplayName Yes String User-facing brand name of the device. [**]
modelDisplayName No String User-facing model name of the device. [**]
libraryVersion Yes String The SpZeroConfVars::library_version field returned by SpZeroConfGetVars(). [*]
resolverVersion Yes String The SpZeroConfVars::resolver_version field returned by SpZeroConfGetVars(), as string. [*]
groupStatus Yes String The SpZeroConfVars::group_status field returned by SpZeroConfGetVars(). [*]
tokenType Yes String The SpZeroConfVars::token_type field returned by SpZeroConfGetVars(). [*]
clientID Yes String The SpZeroConfVars::client_id field returned by SpZeroConfGetVars(). [*]
productID Yes Integer The SpZeroConfVars::product_id field returned by SpZeroConfGetVars(). [*]
scope Yes String The SpZeroConfVars::scope field returned by SpZeroConfGetVars(). [*]
availability Yes String The SpZeroConfVars::availability field returned by SpZeroConfGetVars(), as string. [*]
aliases No Array The SpZeroConfVars::aliases field returned by SpZeroConfGetVars(). [*]
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().

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.

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:

GET http://192.168.10.1:8080/cpath?action=getInfo&version=2.9.0

Response from device without aliases. The field "remoteName" defines the name to be displayed for the device.

{
    "status": 101,
    "statusString": "OK",
    "spotifyError": 0,
    "version": "2.9.0",
    "deviceID": "0007F537F5ED",
    "deviceType": "SPEAKER",
    "remoteName": "John's \"Super\" Speaker",
    "publicKey": "<some-string>",
    "brandDisplayName": "Foo Corp™",
    "modelDisplayName": "X-2000 Portátil",
    "libraryVersion": "master-v2.15.1-g7890abcd",
    "resolverVersion": "1",
    "groupStatus": "GROUP",
    "tokenType": "accesstoken",
    "clientID": "<some-other-string>",
    "productID": 0,
    "scope": "streaming",
    "availability": "",
    "supported_drm_media_formats": [
        {"drm": 0, "formats": 35},
        {"drm": 1, "formats": 35},
        {"drm": 3, "formats": 1168}
    ],
    "supported_capabilities": 1
}

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.

{
    "status": 101,
    "statusString": "OK",
    "spotifyError": 0,
    "version": "2.9.0",
    "deviceID": "0007F537F5ED",
    "deviceType": "SPEAKER",
    "remoteName": "",
    "publicKey": "<some-string>",
    "brandDisplayName": "Foo Corp™",
    "modelDisplayName": "X-2000 Portátil",
    "libraryVersion": "master-v2.15.1-g7890abcd",
    "resolverVersion": "1",
    "groupStatus": "NONE",
    "tokenType": "accesstoken",
    "clientID": "<some-other-string>",
    "productID": 0,
    "scope": "streaming",
    "availability": "",
    "aliases": [
        {
            "name": "alias-one",
            "id": "1",
            "isGroup": "true"
        },
        {
            "name": "alias-two",
            "id": "2",
            "isGroup": "false"
        }
    ],
    "supported_drm_media_formats": [
        {"drm": 0, "formats": 35},
        {"drm": 1, "formats": 35},
        {"drm": 3, "formats": 1168}
    ],
    "supported_capabilities": 1
}

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:

SpError err;
struct SpConfig config;

memset(&config, 0, sizeof(config));

...
config.device_aliases[0].display_name = "My Speaker";
config.device_aliases[1].display_name = "Kitchen + Dining";
config.device_aliases[2].display_name = "Upper floor";

config.display_name = NULL; // No display name is used because aliases are defined
...

err = 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 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.

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.