Subscribe
Subscribe to Sonos groups to receive state change events about the user's system. For example, changes to group or player volume; playback status, errors, metadata, and playback session errors. Sonos sends events to the callback URL that you specify for your API key. Sonos sends events securely to your callback URL with an HTTP Post, with a cryptographic secret in the header for each event. This secret is also tied to your API key.
This step is optional. You don't need to register a callback URL or subscribe to events, but if your integration needs to react to changes in the state of the system you should subscribe to events. This will provide the best experience to your users.
Register your callback URL with Sonos
In the integration manager, register your callback URL for each API key. Your callback URL must use HTTPS.
Note that your callback URL is different from the redirect URI used for authorization. See Authorize for details about the redirect URI.
Your event service must...
Create a service to handle Sonos events. Your event service must:
- Support at least HTTP 1.1 with persistent connections ("keep-alive"). This is important to maintain the flow of data going back and forth between Sonos and your client.
- Support secure HTTP with SSL/TLS v1.2.
- Have a valid and trusted CA-signed X.509 certificate for the DNS name.
Your event service should verify the X-Sonos-Event-Signature of each event it receives. See below for details.
Subscribe to events
To subscribe to a Control API HTTP event, send an HTTP POST to the subscription path for the namespace. Sonos identifies your client using your OAuth token. If your client has a registered callback base URL, Sonos responds with an HTTP 200 status code in the header and an empty body to indicate that it has registered your client to receive events. If your client does not have a registered callback base URL, Sonos responds with an HTTP 403 (Forbidden) status code.
Here's a sample subscription request:
POST /groups/RINCON_00012345678001400:0/groupVolume/subscription HTTP/1.1
Host: api.ws.sonos.com/control/api/v1
Content-Type: application/json
Authorization: Bearer <token>
Here's a sample response:
HTTP/1.1 200 OK
Content-Type: application/json
X-Sonos-Type: none
{}
Sonos sends event IDs in the header and objects in the body
Sonos sends an HTTP POST to your callback URL with an additional path to indicate the type of event. The body of the event response will be the Control API event object. For security, event notifications will not follow redirects. Event notification requests will be HTTPS.
Events contain the following HTTP headers:
HTTP Header | Type | Description |
---|---|---|
Content-Type | String | The type of request body. This will always be "application/json". |
X-Sonos-Household-Id | String | A string uniquely identifying the Sonos household. |
X-Sonos-Namespace | String | The Sonos namespace of the event callback. For example, groups . |
X-Sonos-Type | String | The type of event contained in the callback. For example, playbackError . |
X-Sonos-Target-Type | String | The type of object targeted by the original subscribe request. For example, groupId . The X-Sonos-Target-Value header indicates the value. |
X-Sonos-Target-Value | String | Identifier for the target of the original subscribe request. The X-Sonos-Target-Type header indicates the type. |
X-Sonos-Event-Seq-Id | Long | A number that increases in value that you can use to sequentially order events. |
X-Sonos-Event-Signature | String | A signature that provides passive authentication to clients about events originating from Sonos. See Verify X-Sonos-Event-Signature below for details. |
See the subscribe
command for each namespace and Types for details about events.
Sample group volume event
Here's a sample group volume event, sent after a change in the volume state:
POST {YOUR_CALLBACK_BASE_URL} HTTP/1.1
Content-Type: application/json
Content-Length: 41
X-Sonos-Household-Id: Sonos_1234567890
X-Sonos-Event-Seq-Id: 1234
X-Sonos-Event-Signature: 90192js8cjhvd7ebcjsfdfw
X-Sonos-Namespace: groupVolume
X-Sonos-Type: groupVolume
X-Sonos-Target-Type: groupId
X-Sonos-Target-Value: RINCON_00012345678001400:0
{"volume":16,"muted":false,"fixed":false}
Verify X-Sonos-Event-Signature
Sonos sends a cryptographic signature in the X-Sonos-Event-Signature header for every event. Your event server should use this to confirm the authenticity of every event that you receive from Sonos. To do this, regenerate the signature and compare it to the value provided in this header. Discard any requests that don't match this signature verification step.
Regenerate X-Sonos-Event-Signature
To regenerate the signature, concatenate the attributes below, in order, create a SHA-256 hash of this concatenation, and Base64-encode the resulting string.
- X-Sonos-Event-Seq-Id HTTP header
- X-Sonos-Namespace HTTP header
- X-Sonos-Type HTTP header
- X-Sonos-Target-Type HTTP header
- X-Sonos-Target-Value HTTP header
- Your client credentials key
- Your client credentials secret
When generating the signature:
- Encode bytes obtained from strings as UTF-8.
- Use a Base64 encoder that is URL-safe with padding disabled.
Code examples
The code snippets below describe how Sonos generates this signature.
sha256 = new Digest("SHA256");
sha256.update(utf8 bytes headers::eventSequenceId)
.update(utf8 bytes headers::namespace)
.update(utf8 bytes headers::type)
.update(utf8 bytes headers::targetType)
.update(utf8 bytes headers::targetValue)
.update(utf8 bytes clientData::clientId)
.update(utf8 bytes clientData::clientSecret);
signature = Base64String(messageDigest.digest(), padding: false);
import java.security.MessageDigest;
import java.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(headers.get("X-Sonos-Event-Seq-Id").getBytes(UTF_8));
messageDigest.update(headers.get("X-Sonos-Namespace").getBytes(UTF_8));
messageDigest.update(headers.get("X-Sonos-Type").getBytes(UTF_8));
messageDigest.update(headers.get("X-Sonos-Target-Type").getBytes(UTF_8));
messageDigest.update(headers.get("X-Sonos-Target-Value").getBytes(UTF_8));
messageDigest.update(clientData.getClientId().getBytes(UTF_8));
messageDigest.update(clientData.getClientSecret().getBytes(UTF_8));
String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(messageDigest.digest());
require 'digest'
require 'base64'
signature_values = [
request.headers['X-Sonos-Event-Seq-Id'],
request.headers['X-Sonos-Namespace'],
request.headers['X-Sonos-Type'],
request.headers['X-Sonos-Target-Type'],
request.headers['X-sonos-Target-Value'],
CLIENT_ID,
CLIENT_SECRET
]
signature = Digest::SHA256.digest(signature_values.join(''))
base64_signature = Base64.urlsafe_encode64(signature, padding: false)
Unsubscribe from events
To unsubscribe from a Control API HTTP event, send an HTTP DELETE to the subscription path for the namespace. Your client can also return a HTTP 410 to an event notification to stop receiving events for the event's namespace and target.
Here's a sample unsubscribe request:
curl -X "DELETE" "https://api.ws.sonos.com/control/api/v1/players/<playerId>/playerVolume:1/subscription"
-H 'Authorization: Bearer <token>'
-H 'Content-Type: application/json'
Here's a sample response:
HTTP/1.1 200 OK
Connection: close
X-Sonos-Household-Id: <householdId>
Content-Type: application/json
Content-Length: 2
X-Sonos-User-Id: <SonosId>
Date: Wed, 25 Jul 2018 19:03:09 GMT
{}
Respond to events
When you receive an event, send a 200 OK response to let the Sonos cloud know that your client received it. Any response outside of the 200 range will be considered an error, including no response. Sonos also considers a 301 redirect an error as it does not follow redirects for events.
If Sonos isn't able to send an event to your client, it retries every second for three tries. After the third try, if the Sonos cloud receives another error response or no response, it drops the event. Sonos does not backlog or replay events.
Make sure that your client responds to events quickly (within 1 second). To make sure that apps don't accidentally run over the timeout limit, we recommend that you defer any lengthy event processing until after you've sent the 200 OK response.
As a best practice, you should unsubscribe to namespaces before terminating your event service.
Subscription lifetime
Group change events exist in the global namespace. Once you subscribe, you'll always get them. Additionally, you can sent the getGroups command to get them at any time.
Subscriptions live for a maximum of three days. If a target player shuts down and does not connect back within the next 30 seconds, it wipes clean any target subscriptions. If this player was part of a group and the other group members stay connected while the target player is gone, one of the other group members will claim the subscription.
If there are any group changes, you should receive a global event indicating group modifications when Sonos cleans up a subscription. Your client must also resubscribe based on any response or event indicating that a group has moved or is gone.
By default any subscription lives for three days if untouched once created. If your app updates the subscription and resubscribes, the player extends the subscription lifetime another three days.
Subscriptions to the groups and favorites namespaces live for three days regardless of whether the target lives for that long or goes away. They will not get cleaned up after 30 seconds. If there are other players in the household, they will immediately claim the subscription and it will be renewed for another three days. If there are no other players to reclaim the subscriptions and they remain unclaimed and untouched, Sonos removes them after three days.
Updated over 1 year ago