Sennheiser Sound Control (SSCv2) Specification - Version 2.3

Abstract

Specification for a secure, extendible, standards-based, versatile restful API via https transport, suitable for command, control, monitoring, metering, and scripting of networked audio devices; Utilizing JavaScript Object Notation (JSON) for data serialization.

History

  • 2022, August 2: Version 0.1 (Draft)
  • 2022, September 9: Version 0.8 (Draft)
  • 2022, October 10: Version 2.0
  • 2022, December 2: Version 2.1
  • 2023, March 1: Version 2.2
  • 2023, October 27: Version 2.3

Introduction

Modern professional audio devices are designed as building blocks for large, complex systems and are more and more integrated into the IT infrastructure of our customers. Controlling and monitoring the audio devices using IP-based standard protocols is mandatory for easy integration by third parties, which cannot be expected to learn a new protocol for every vendor. When exposing an audio device over the network security considerations to prevent malicious actors from misconfiguring, damaging or subverting the audio device must be taken into account. These considerations are mandatory to protect the device and the network of the customer.

With the dominating success of HTTP as the backbone of the Internet, HTTP and the principle of RESTful APIs are more and more dominating on-premise automation solutions. As HTTP addresses the issue of authentication and encryption of the network communication in a very adversarial environment, the secured variant, HTTPS, is a very mature and trusted solution for secure, standardized data exchange.

When considering the needs for an IP-based, standards-compliant solution it is imminent, that these needs are not compatible with the needs of inter-process communication, low-throughput links or configuration file formats. The following points are key for an IP-based communication protocol, which is exposed to be used by customers

  • Standards compliant for transport and data formats to lower the bar of entry
  • Standards compliant for employed security mechanisms
  • Extensible/adaptable to allow for new product requirements
  • Possible to be easily integrated in an existing network infrastructure

This document describes the specific adaption of HTTPS and the principle of RESTful APIs to Sennheiser use, "Sennheiser Sound Control v2", SSCv2. The main other ingredient is JavaScript Object Notation (JSON), which has become the default data encoding used in HTTPS-based RESTful APIs.

Note that the protocol is intended for command and control over IP-based networks only. Network audio streaming, inter-process communication, communication over low-throughput links, usage as file format are entirely out of its scope.

SSC Overview

The following standards are important for the understanding of SSCv2, as they form the basic building blocks the protocol is built upon.

HTTPS Overview

The Hypertext Transfer Protocol (HTTP) is the foundational protocol for data exchange in the World Wide Web. It is designed as an application-level protocol for distributed, collaborative, hypertext information systems. Finalized as version 1.0 in 1996 and updated to version 1.1 in 1997, HTTP has been the backbone of the World Wide Webs success since more than 20 years. It was updated to version 2 in 2015 and 3 in 2022. HTTP/3 changes the underlying transport protocol from TCP to UDP, while still retaining session semantics. From a user perspective there is no change in perceived session attributes, just an increase in throughput and a reduced latency. While these updates are enhancing the protocol, usage of 1.1 is still widespread.

To add security to the HTTP/1.1 protocol Transport Layer Security (TLS) is used, which adds security without changing the protocol itself, the resulting secure protocol is commonly referred as HTTPS.

HTTP/1.1 is standardized in the RFCs 7230 to 7240 (https://www.ietf.org/rfc/rfc7230.txt).

The SSC specification mandates the use of HTTP/1.1 and TLS 1.2.

Server Sent Events

HTTP is based on the Request-Response-Pattern, meaning every answer from the server must be triggered by a request by the client. The response happens immediately. Although there are some approaches with delayed answer (e.g. the server sending a reply indicating it is not finished until the answer is ready), this still follows the Request-Response-Model. To allow for unsolicited messages after an initial subscription Server Sent Events (SSE) are specified. They define a mechanism for clients to tell the server they want to be able to receive asynchronous messages and for the server to send these to them without a dedicated request for each response.

Server Sent Events are developed as a living standard by the Web Hypertext Application Technology Working Group (WHATWG). It can be found at: https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events

JavaScript Object Notation Overview

JavaScript Object Notation (JSON) is a lightweight data-interchange format. It is derived from the object literals of JavaScript, as defined in the ECMAScript Programming Language Standard, Third Edition.. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of all languages, including C, C++, C#, Java, JavaScript, Ruby, Python, and many others. These properties make JSON an ideal data-interchange language.

The central website for JSON information is http://json.org. JSON is formally specified in RFC 8259 (http://www.ietf.org/rfc/rfc8259.txt).

JSON's design goals are to be minimal, portable, textual, and a subset of JavaScript.

RESTful API Overview

Representational State Transfer (REST) is a software paradigm, which has become the main approach to create stateless, reliable web APIs. These web APIs are typically based on HTTP methods to access URLs and use JSON as data encoding.

A RESTful API defines resources (made accessible using an URI), encapsulating entities. A client is able to interact with the resources by accessing their URI. A big emphasis is put on the design of the API - the designer needs to carve resources and their entities in a way, which groups related entities in logical resources.

While RESTful is aiming to be completely stateless, the SSC protocol adds some state to its API (for example to allow subscriptions).

The initial definition of the REST principle is from Roy Fieldings dissertation, found at https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm.

OpenAPI

The OpenAPI specification is a specification for machine-readable descriptions of RESTful web services. The descriptions can be used not only to document, but also for the generation of code providing or consuming the interface or to assist in automatic test creation. Created in 2010 as the open-source Swagger specification, it was moved over to the OpenAPI initiative in 2015 and renamed to OpenAPI specification in 2016. The specification has established itself as the main standard for describing RESTful web services.

The standardization of OpenAPI is coordinated on https://www.openapis.org/.

OpenAPI allows for API specification in either JSON or YAML format and provides converters to easily convert between the formats. For the examples in this specification the YAML format was chosen, as this is better suited to human exchange. When providing the OpenAPI description of a device as part of the API, the use of JSON is mandated, to have the API stick to one data exchange format.

Conventions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP14/RFC 2119, "Key words for use in RFCs to Indicate Requirement Levels" (https://datatracker.ietf.org/doc/html/rfc2119).

HTTP related terminology is used as in RFC 9110 (https://datatracker.ietf.org/doc/html/rfc9110). JSON related terminology can be found in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259). REST is not a formal standard, information can be found in this dissertation (https://roy.gbiv.com/pubs/dissertation/top.htm).

SSCv2 Terminology

Control resource

  • Resource, which, in the context of dynamic resources, can be subscribed to to get an implicit subscription to all resources controlled by this resource; see Dynamic Resources.

SSCv2 Message

  • JSON object that is either transferred as HTTP request/response or as server sent event (SSE)

SSCv2 Server

  • device or application that receives SSCv2 messages via HTTP request, and replies to them

SSCv2 Client

  • device or application that sends SSCv2 messages via HTTP request

SSCv2 Command

  • HTTP request sent by an SSCv2 client (includes URI, Header)

SSCv2 Address

  • Path component of the URI of an HTTP endpoint implemented by an SSCv2 Server; will be used synonymously to resource

SSCv2 Address Tree

  • hierarchical tree comprising all the SSCv2 Addresses of an SSCv2 Server

SSCv2 Command Arguments

  • JSON object sent as HTTP body and/or query argument of an SSCv2 command

SSCv2 Command Reply

  • HTTP response sent by SSCv2 Server as reply to an SSCv2 command

SSCv2 Session

  • association of a specific SSCv2 Client to an SSCv2 Server

SSCv2 Data Structure Specification

SSCv2 constraints of underlying protocols

Case sensitivity

Case sensitivity is a contested topic. SSCv2 is following the standards it uses in this regard:

  • The path component of an URI MUST be implemented case sensitive, as per RFC 3986, 6.2.2.1, "Case normalization". An SSCv2 schema still SHOULD avoid specifying paths which are ambiguous on case.
  • As JSON specifies its strings to follow C conventions and nearly all JSON implementations are case sensitive, the JSON payload MUST be interpreted by SSCv2 Clients and SSCv2 Servers as case sensitive. An SSCv2 schema still SHOULD avoid specifying entities which are ambiguous on case.

HTTP(S)

The following optional parts of HTTP(S) have been made mandatory or are recommended for SSCv2:

  • An SSCv2 Server MUST implement HTTPS (=encrypted connections)
  • An SSCv2 Server MUST NOT allow HTTP (=unencrypted connections)
  • An SSCv2 Server MUST implement HTTP/1.1
  • An SSCv2 Server SHOULD implement persistent connections to limit the amount of new connections needed
  • An SSCv2 Client SHOULD use persistent connections

Note for development: An SSCv2 Server MAY implement a development build allowing for HTTP (e.g. to help with debugging), but MUST ensure that HTTP is not possible in release versions.

Default HTTP status codes

For some generic actions there have been HTTP status codes made mandatory:

  • 400 (Bad Request): When a client is authorized to access a resource, but has a format error in its request, the SSCv2 Server MUST reply with 400 - "Bad request". Examples for error 400 are messages with broken JSON, but also messages, which violate limits for entities or use enum-values, which are not specified.

  • 401 (Unauthorized): When a client sends a request without being authenticated (see "Specification for Authenticated Sennheiser Sound Control (SSC) Clients"), the SSCv2 Server MUST reply with 401 - "Unauthorized".

  • 403 (Forbidden): When a client is authenticated, but not authorized to access the resource, the SSCv2 Server MUST reply with 403 - "Forbidden".

  • 404 (Not Found): When a client is authorized and tries to access a resource which is not existing, the SSCv2 Server MUST reply with 404 - "Not Found".

  • 405 (Not Allowed): When a client is authorized to access a resource, but the HTTP request method is not allowed by the SSCv2 Server, the SSCv2 Server MUST reply with 405 - "Method not allowed".

  • 409 (Conflict): When a client is authorized to access a resource, the format checks out, but the device can not honor the request because of internal device state, the SSCv2 Server MUST reply with 409 - "Conflict". An example for this would be trying to configure an IP address, while the device is using DHCP, without also switching to use fixed addresses.

  • 422 (Unprocessable Entity): When a client is authorized to access a resource, the format checks out, but there are semantic/logical errors, the SSCv2 Server MUST reply with 422 - "Unprocessable Entity". An example for a logical error would be using a sessionUUID of a subscription which does not exist.

This list is non-exhaustive and an SSC Server implementation is free to choose additional HTTP status codes if fitting.

An SSCv2 Server MUST first check for authentication, then for authorization. After that it is free to check in any order deemed fitting.

Error handling

An SSCv2 Server SHOULD add additional error arguments, if this helps to understand the error. The format of the arguments MUST be specified and explained in the OpenAPI schema.

JSON

The following optional parts of JSON have been made mandatory or are recommended for SSCv2:

  • An SSCv2 Server MUST return all text-based entities as a JSON object. While JSON allows for singular values to be returned in a plain way, this is a newer addition to the JSON standard and might not be supported by all JSON libraries.

JSON Message Transaction Syntax

The SSCv2 Message exchange is described here as transaction using the following syntax:

  • Prefix „TX:“ indicates an SSCv2 Message an SSCv2 Client is sending to an SSCv2 Server. There are up to four parameters to a TX:
    • HTTP verb (GET, PUT, POST are used in these examples)
    • HTTP resource identifier (Path)
    • HTTP query parameters
    • optional HTTP body (if present, encoded as JSON object)
  • Prefix „RX:“ indicates an SSCv2 Message that the SSCv2 Server will send back to the Client. It consists of two parameters:
    • HTTP status code
    • optional HTTP body (if present, encoded as JSON object)

Getter commands, which request a property from the SSCv2 Server, are realized by using the HTTP GET verb. A GET SHOULD always be replied with the complete object in JSON format using a Content-Type of application/json. Exceptions, like returning binary data, MAY be chosen, but MUST be justified and document why it is impossible to use JSON. The SSCv2 Server MUST set the correct Content-Type accordingly.

Read

TX:
  Verb: GET
  Path: api/out1/xlr2
RX:
  Status Code: 200
  Body: {"gain": -10, "mute": false}

A transaction to set the gain of "xlr2" of "out1" to -10 looks like this:

TX:
  Verb: PUT
  Path: api/out1/xlr2
  Body: {"gain": -10}
RX:
  Status Code: 200

Note that the command results in an empty reply message using the status code 200 ("ok"). The SSCv2 Server MAY specify a reply containing a body, if the result of changing the entity is not the verbatim input data (e.g. a new resource is created based on the input data). Although the resource identifier out1/xlr2 may consist of more parameters, only the gain was modified. It is also possible to modify multiple parameters of a property:

TX:
  Verb: PUT
  Path: api/out1/xlr2
  Body: {"gain": -10, "mute": false}
RX:
  Status Code: 200

The input MUST NOT be converted automatically if it is found in violation to the expected input. Any invalid access, e.g., writing to a read-only property, or if the argument is out of range, MUST result in an error. The SSCv2 Server MAY add additional information explaining the error to the error message's body.

TX:
  Verb: PUT
  Path: api/out1/xlr2
  Body: {"gain": -1000}
RX:
  Status Code: 400
  Body: TBD

An SSCv2 Server MUST implement setting multiple parameters as an transactional operation; This means a partial failure of a command MUST lead to the SSCv2 Server not applying any of the contained arguments.

TX:
  Verb: GET
  Path: api/out1/xlr2
RX:
  Status Code: 200
  Body: {"gain": -10, "mute": false}
TX:
  Verb: PUT
  Path: api/out1/xlr2
  Body: {"gain": -5, "mute": filenotfound}
RX:
  Status Code: 400
  Body: TBD
TX:
  Verb: GET
  Path: api/out1/xlr2
RX:
  Status Code: 200
  Body: {"gain": -10, "mute": false}

Note that the gain parameter was not changed to -5 since the requested mute parameter was invalid.

TX:
  Verb: POST
  Path: api/device/restart
RX:
  Status Code: 200

TX:
  Verb: POST
  Path: api/device/restore
  Body: {"type": "Factory Defaults"}
RX:
  Status Code: 200
  Body: {"type": "Factory Defaults"}

SSCv2 JSON Message Syntax

SSCv2 Messages

A Message is the protocol unit of transmission. There are two ways of message exchange:

  • Synchronous: The client sends a request and the server immediately sends a response
  • Asynchronous: The client subscribes for parameter changes and the server notifies the client on parameter changes via SSE.

An SSCv2 Message MUST be sent as a single closed JSON form describing a JSON object. This means that every SSCv2 Message is enclosed in a pair of curly brackets { }. There MUST NOT be more than one SSCv2 message per HTTP request.

SSCv2 Addresses

Every SSCv2 Server implements a set of SSCv2 Addresses. SSCv2 Addresses are the potential destinations of SSCv2 Messages received by the SSCv2 Server, and correspond to each of the points of control that the device makes available. The SSCv2 Server MUST respond to each received SSCv2 Command by sending an SSCv2 Command Reply to the originating SSCv2 Client.

Each SSCv2 Address is addressable by a unified resource identifier (URI). An SSCv2 Server’s SSCv2 Address are arranged in a tree structure called an SSCv2 Address Tree. The leaves of this tree are the SSCv2 Addresses. An SSCv2 Server’s SSCv2 Address Tree MAY be dynamic; that is, its nodes and leaves MAY change over time.

The SSCv2 Address follows the URI specification, but MUST NOT use percent encoding. Hence only US-ASCII MUST be used to form an SSCv2 Address. While there is no limit in HTTP to the length of an URL and it recommends to support at least 8000 chars, an SSCv2 Address SHOULD not exceed 2048 characters for practicality purposes.

Temporal Semantics

By default, the SSCv2 Server SHOULD execute the SSCv2 Command immediately, i.e., as soon as possible after receipt of the message.

An SSCv2 Server MAY expose a representation of its current absolute device time. The optional SSCv2 Command /device/time MAY be used to query and optionally set the device time. SSCv2 does not provide a mechanism for clock synchronization; if an SSCv2 Server utilizes a mechanism like NTP or PTP to sync to the absolute time it MUST implement /device/time as read-only parameter. See also the description of this resource in General SSCv2 Address Schema.

Commands with long execution times

If the SSCv2 Server expects the execution of a command to exceed 5 seconds it MUST immediately send an HTTP response with status code 102 - processing. When the command execution is finished the SSCv2 Server finalizes the request by sending the response containing the final result and appropriate status code (e.g. 200 - ok or corresponding error).

SSCv2 Methods addressing array values

A resource MAY have an array of elementary data as entity. The array MAY be empty. The SSCv2 Command acting upon the resource MUST NOT switch between returning arrays and elementary data.

All the elements of the array MUST have the same elementary data type.

In previous version of this standard the access of array (special) ranges was required, this SHOULD NOT be used anymore.

Accessing complete arrays

Reading an array is done in the same way as reading an elementary value.

Writing an array is done in the same way as writing an elementary value with the array as payload.

TX:
  Verb: GET
  Path: api/presets/bank1
RX:
  Status Code: 200
  Body: { "carriers": [470000,470400,470800,471200,471600] }
TX:
  Verb: PUT
  Path: api/presets/bank1
  Body: { "carriers": [470000,470400,470800,471250,471600] }
RX:
  Status Code: 200

SSCv2 Sessions

An SSCv2 Session is defined by the association of a specific SSCv2 Client with an SSCv2 Server. The SSCv2 Server MUST keep state information specific to each SSCv2 Client (e.g., state relating to SSCv2 subscriptions, or authorization). The SSCv2 Server MUST couple the state to the SSCv2 Session. When the SSCv2 Session terminates, session state MUST be cleared (e.g., all SSCv2 subscriptions of the client are cancelled).

An SSCv2 Session begins with establishing the HTTPS connection from a specific SSCv2 Client to an SSCv2 Server.

The SSCv2 Session MUST last for the duration of the connection. The SSCv2 Session MUST be terminated when the connection is closed by either side.

The SSCv2 Server SHOULD keep state information that is explicitly specific to the SSCv2 Session in SSCv2 Addresses that are below /api/ssc/state, e.g., information about SSCv2 subscriptions (see also section SSCv2 subscriptions - /api/ssc/state/subscriptions). Additionally, SSCv2 Server state specific for an SSCv2 Session MAY affect the results of SSCv2 Commands (e.g. it might reject specific SSCv2 Commands if an SSCv2 Client lacks sufficient access permission).

The SSCv2 Server MUST NOT coalesce SSCv2 Sessions together based on authorization credentials. Multiple SSCv2 sessions per authorization credential MUST be the default.

General SSCv2 Address Schema

The SSCv2 Address Tree MUST be located at /api. This is mandatory, so an SSCv2 implementation is able to be incorporated into a web server setup easily if needed.

Some parts of the SSCv2 Address Tree are mandated by this specification for purposes of meta-protocol information, generic device-independent features, and device capability description. Any SSCv2 Server MUST implement these addresses. The reserved parts of the address tree are rooted in the SSCv2 Addresses: /api/ssc /api/device

The addresses and commands rooted in these reserved addresses are described in the following sections. This specification should be revised if additional addresses in the address tree are made mandatory.

The schema descriptions given in the following are written in the YAML notation of the OpenAPI Specification (OAS). This format allows a very detailed and structured description of the HTTP verbs, the addresses, the payload and the responses. Nevertheless, in this document only the most important points of the address schema are covered for the sake of readability. Therefore, for some addresses the responses might be shortened and not covering all edge cases or error messages.

SSCv2 Protocol version - /api/ssc/version

Read-only value. Reports the SSCv2 protocol version and the schema version implemented in the server.

/api/ssc/version:
  get:
    summary: Get the versions relevant for SSCv2 implemented
    description: An SSCv2 Command to retrieve the SSCv2 protocol and schema versions implemented in the SSCv2 Server
    responses:
      '200':
        description: Successful request
        content:
          application/json:
            schema:
              type: object
              properties:
                protocol:
                  type: string
                  example: 2.0
                schema:
                  type: string
                  example: 1.5 
      '500':
        description: SSCv2 Server encountered internal error

Session-specific SSCv2 Address Space - /api/ssc/state

SSCv2 Addresses under the /api/ssc/state address are specific to the SSCv2 Session between SSCv2 Client and SSCv2 Server. This means that it is possible that different SSCv2 Clients invoke the same SSCv2 Command with different arguments, and the immediate reply as well as the resulting state of the SSCv2 Server will differ for each SSCv2 Client.

This behavior differs from the normal behavior of an SSCv2 Server, where the server state is shared between all SSCv2 Clients.

SSCv2 subscriptions - /api/ssc/state/subscriptions

The SSCv2 Server MUST implement subscriptions using Server Sent Events (SSE), formatting messages according to the EventSource specification. From the EventSource specification only the parameters dataand event are allowed to be used. For event only the following events MUST be used:

  • open, on the start of a subscription
  • message, when sending resource updates. As this is the default, this MAY be omitted in the event stream
  • close, when the SSCv2 client actively closed the subscription using DELETE on /api/ssc/state/subscriptions/{sessionUUID} or the SSCv2 Server closes the subscription as a side effect of an SSCv2 Command.

An SSCv2 Server MUST send a close-event, when the subscription is closed by the Server and the Client is still present, e.g. when initiating a reboot or changing the ip address used by the subscription.

A full EventSource compatible update looks like the following:

event: <eventtype>\ndata: <updatejson>\n\n

\n denotes the ASCII-character for a linefeed.

The subscription is initialized by the client by issuing a GET request. The SSCv2 Server either accepts or refuses the subscription request based on the credentials used. When accepting the SSCv2 Server MUST reply with a message setting the Content-Type to text/event-stream and the header field Content-Location containing a the path /api/ssc/state/subscriptions/{sessionUUID} set to an internally generated ID on subscription and associated with the HTTP session. The SSCv2 Server MUST sent a first message to the client, containing the initial open-event. The SSCv2 subscription starts empty without any subscribed resource and the SSCv2 Client is able to subscribe to resources using /api/ssc/state/subscriptions/{sessionUUID} and /api/ssc/state/subscriptions/{sessionUUID}/add respectively.

The SSCv2 Server MAY refuse subscription requests, subject to device-specific policy or implementation specific limitations. The SSCv2 Server MUST reply on the subscription request immediately either by acknowledging the request, or by sending an error reply.

Each subscription notification MUST be equal to a GET request to the resource without any parameters.

An SSCv2 Server MUST treat subscriptions to resources not specifying GET requests as an error. If the user is authenticated, but not authorized to access a resource, the SSCv2 Server MUST treat this as an error.

An SSCv2 Server MUST NOT allow usage of array ranges for /api/ssc/state/subscriptions.

An SSCv2 Server MUST add the created resource URL (/api/ssc/state/subscriptions/{sessionUUID}) to the Content-Location-Header of the 200 - "OK" reply.

An SSCv2 Server SHOULD send out notifications as soon as possible.

An SSCv2 Server MUST generate the sessionUUID in a way which prevents guessing already existing sessionUUIDs - for example by using hashes of at least 8 Bytes of random or implementing RFC4122 (https://www.rfc-editor.org/rfc/rfc4122.html).

An SSCv2 Server MUST ensure that all accesses to a subscription (getting status, add, remove, replace) are using same access credentials as the initial creation of the subscription. In case of mismatching credentials the SSCv2 Server MUST reply with 403 - Forbidden, without processing the request.

An SSCv2 Server MUST send immediately the initial open-event, formatting the attached data according to the following json-template. {sessionUUID} MUST be replaced with the UUID of the newly initialized session.

{
  "path": "/api/ssc/state/subscriptions/{sessionUUID}",
  "sessionUUID": "{sessionUUID}"
}

An SSCv2 Client MUST NOT send further regular HTTP requests to the SSCv2 Server using the connection used to request the subscription. After the initial subscription request the connection can only be used to receive events from the SSCv2 Server. The response header reports the sessionUUID for later relation to modify of the subscription and the body is the stream of the values of the subscribed properties at the time of subscription and all the following updates in the time during the subscription as shown in OpenAPI example below. For an example of the answer of the stream, see Section Subscription notification syntax below.

OpenAPI snippet

/api/ssc/state/subscriptions:
  get:
    summary: Start a subscription
    description: An SSCv2 Command to start a subscription
    responses:
      '200':
        description: Successful request
        headers:
          Content-Location:
            description: Location of the resource to be used to change the subscribed resources
            schema:
              type: string
        content:
          text/event-stream:
            schema:
              type: string
              description: The initial subscription stream.
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not authorized to subscribe to resources
      '500':
        description: SSCv2 Server encountered internal error

Getting subscription status

If an SSCv2 Client wants to request the subscription status attached to a sessionUUID, it MUST use a GET request to /api/ssc/state/subscriptions using this sessionUUID. An SSCv2 Server MUST check that the permission of the SSCv2 Client requesting is adequate for requesting the subscription list associated with the sessionUUID. For example with an control user and an API user, the control user SHOULD be allowed to request the subscription list for any user, while the API user is only allowed to get lists created with the API user. The different users are specified in the "Specification for Authenticated Sennheiser Sound Control (SSC) Clients".

An SSCv2 Server MUST ensure that this subscription has been created by the same user as the user sending the request.

An SSCv2 Server MUST treat a non-existing sessionUUID as an error.

OpenAPI snippet

/api/ssc/state/subscriptions/{sessionUUID}:
  get:
    summary: Get the subscription list
    description: An SSCv2 Command to retrieve the list of subscriptions associated with the sessionUUID
    parameters:
        - in: path
          name: sessionUUID
          schema:
            type: string
          required: true
    responses:
      '200':
        description: Successful request
        content:
          application/json:
            schema:
              type: array
              items:
                type: string
                example: /api/device/site
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not allowed to get subscription list for this sessionUUID
      '422':
        description: sessionUUID did not exist
      '500':
        description: SSCv2 Server encountered internal error

Changing subscribed resources

It is possible to either change the full set of resources of a subscription, or to add/remove resources from the currently subscribed set.

Changing the full set of subscribed resources

To change the full set of subscribed resources, the SSCv2 Client MUST send a complete list of the resources it wants to be subscribed to after changing the subscription to /api/ssc/state/subscriptions/{sessionUUID}. Akin to getting the subscription status an existing subscription is updated by using the sessionUUID query parameter. The SSCv2 Server MUST treat updating the list as a transaction and MUST check that the permission of the SSCv2 Client requesting is adequate for changing the subscription list associated with the sessionUUID. The SSCv2 Server MUST remember that the requesting client is to be notified about value changes of the subscribed addresses. After accepting and setting up the subscription the SSCv2 Server MUST send the current state of each subscribed resource which is new to the set of subscribed resources.

An SSCv2 Server MUST treat a non-existing sessionUUID as an error.

An SSCv2 Server MUST NOT treat an empty subscription list as cancelling and MUST NOT terminate the subscription.

If the subscription is extended to include more addresses, the initial values of the newly added and removed resources MUST be reported as for a new subscription.

OpenAPI snippet

/api/ssc/state/subscriptions/{sessionUUID}:
  put:
    summary: Set/change the subscription list
    description: An SSCv2 Command to set/change the list of subscriptions associated with the sessionUUID
    parameters:
        - in: path
          name: sessionUUID
          schema:
            type: string
          required: true
    requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                type: string
                example: /api/device/site
    responses:
      '200':
        description: Successful request
      '400':
        description: The request for subscription was invalid
        content:
            application/json:
              schema:
                type: object
                properties:
                  path:
                    type: string
                    example: /api/ssc/version
                  error:
                    type: integer
                    example: 403
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not allowed to change subscription list for this sessionUUID
      '422':
        description: sessionUUID did not exist
      '500':
        description: SSCv2 Server encountered internal error

Adding resources to subscriptions

To add one or more resources to an existing list of subscribed resources, an SSCv2 client MAY use the resource /api/ssc/state/subscriptions/{sessionUUID}/add. The SSCv2 Server MUST check, whether the SSCv2 Client is authorized to access these resources, if this subscription has been created by the same user, and whether these resources exist. If these checks succeed, the SSCv2 Server MUST add the resources to the list of subscribed resources of the existing subscription.

If one resource in the set of resources the SSCv2 Client wants to add is not allowed, the SSCv2 Server MUST refuse the complete request and the current set of subscribed resources for the subscription MUST be unchanged.

An SSCv2 Server MUST treat a non-existing sessionUUID as an error.

An SSCv2 Server MUST treat an empty resource list as no action and reply with 200 - OK.

An SSCv2 Server MUST ignore a resource already in the list of subscribed resources. If all resources in the resource list are already in the list of subscribed resources, the SSCv2 Server MUST still reply with 200 - OK.

An SSCv2 Server MUST report the initial values of the newly added resources using the existing subscription, at the moment the subscription addition request is granted.

OpenAPI snippet

/api/ssc/state/subscriptions/{sessionUUID}/add:
  put:
    summary: Add resource(s) the subscription list
    description: An SSCv2 Command to add a set of resources to the list of subscriptions associated with the sessionUUID
    parameters:
        - in: path
          name: sessionUUID
          schema:
            type: string
          required: true
    requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                type: string
                example: /api/device/site
    responses:
      '200':
        description: Successful request
      '400':
        description: The request to add to the subscription was invalid
        content:
            application/json:
              schema:
                type: object
                properties:
                  path:
                    type: string
                    example: /api/ssc/version
                  error:
                    type: integer
                    example: 403
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not allowed to add to the subscription list for this sessionUUID
      '422':
        description: sessionUUID did not exist
      '500':
        description: SSCv2 Server encountered internal error

Removing resources from subscriptions

To remove one or more resources from an existing list of subscribed resources, an SSCv2 client MAY use the resource /api/ssc/state/subscriptions/{sessionUUID}/remove. The SSCv2 Server MUST check, whether these resources are currently subscribed and if this subscription has been created by the same user. If these checks succeed, the SSCv2 Server MUST remove the resources from the list of subscribed resources of the existing subscription.

If one resource in the set of resources the SSCv2 Client wants to remove, is not currently subscribed, the SSCv2 Server MUST refuse the complete request and the current set of subscribed resources for the subscription MUST be unchanged.

An SSCv2 Server MUST treat a non-existing sessionUUID as an error.

An SSCv2 Server MUST treat an empty resource list as no action and reply with 200 - OK.

An SSCv2 Server MUST NOT terminate the subscription, if the list of subscribed resources is empty after the removal of resources.

The only error-value allowed in responses is 404, meaning "resource not in list of subscribed resources".

OpenAPI snippet

/api/ssc/state/subscriptions/{sessionUUID}/remove:
  put:
    summary: Remove resource(s) from the subscription list
    description: An SSCv2 Command to remove a set of resources from the list of subscriptions associated with the sessionUUID
    parameters:
        - in: path
          name: sessionUUID
          schema:
            type: string
          required: true
    requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                type: string
                example: /api/device/site
    responses:
      '200':
        description: Successful request
      '400':
        description: The request to remove to the subscription was invalid
        content:
            application/json:
              schema:
                type: object
                properties:
                  path:
                    type: string
                    example: /api/ssc/version
                  error:
                    type: integer
                    example: 404
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not allowed to remove from the subscription list for this sessionUUID
      '422':
        description: sessionUUID did not exist
      '500':
        description: SSCv2 Server encountered internal error

Subscription cancelling

An SSCv2 Client can end a subscription either implicitly or explicitly.

  • To end a subscription implicitly, the SSCv2 Client just closes the connection receiving the subscription data.
  • To end a subscription explicitly, an SSCv2 Client uses the DELETE request method to the resource /api/ssc/state/subscriptions/{sessionUUID}. When using the explicit method, the SSCv2 Server MUST send a close-event to the subscription, before it closes the connection. The data sent in the close-event is the same as in the open-event.

The SSCv2 Server MUST terminate a subscription in these cases:

  • the subscribed client cancels the subscription explicitly
  • the SSCv2 Client closes the connection
  • the transport layer of the SSCv2 connection signals a fatal communication error
  • the password of the user used to create the subscription was changed

When a subscription is closed, an SSCv2 Server MUST remove the associated sessionUUID.

When a subscription is closed explicitly, the SSCv2 Server MUST ensure that this subscription has been created by the same user as the user sending the request.

An SSCv2 Server MUST ensure to only close the subscriptions created using the credentials whose password has been changed. An SSCv2 Server MUST close subscriptions regardless whether the password was actually changed or the already present password was set again.

OpenAPI snippet

/api/ssc/state/subscriptions/{sessionUUID}:
  delete:
    summary: End an existing subscription
    description: An SSCv2 Command to end the subscription associated with the sessionUUID
    parameters:
        - in: path
          name: sessionUUID
          schema:
            type: string
          required: true
    responses:
      '200':
        description: Successful request
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not allowed to end subscription for this sessionUUID
      '422':
        description: sessionUUID did not exist
      '500':
        description: SSCv2 Server encountered internal error

Subscribing to multiple addresses

The SSCv2 Client MAY request to subscribe to multiple resources in a single request by design.

The SSCv2 Server MUST treat all those subscription requests as one transaction. If the SSCv2 Server is not able to satisfy subscription to one of the resources it MUST send an error and refuse all resources. If there are already subscribed resources present, the previous subscribed state MUST remain unchanged.

Error handling

When the SSCv2 Server refuses a request for a subscription or a set of subscriptions, it MUST return an object containing the resource which was not subscribeable and the reason for the error. The SSCv2 Server MUST stop processing a request for a set of subscriptions after encountering the first invalid subscription in the request. The following error codes MUST be used:

  • 403 if subscribing to the resource is not allowed for the user
  • 404 if the resource does not exist

Subscription notification syntax

A subscription notification is JSON object as Server Sent Event (SSE) data, using the resource triggering the notification as a key and the entities of the resource wrapped in a JSON object as value. An SSCv2 Server MAY combine notifications for multiple resources in one JSON object, if the update occurs at the same time. Since a newly created subscription of multiple addresses MUST report the values of the resources this MAY be combined into a bigger JSON object. If this combination of the different resources is not supported, all notifications MUST be send in the stream as individual JSON objects.

The following example shows the stream of a notification after a initial subscription and a later notification of the change of the name:

{
    "/api/device/site":  
  {
        "name": "MyDevice",
        "location": "Chemistry Building 5, Room 15",
        "site": "Left side near the big pillar"
    }
}
{
  "/api/device/site":  
  {
    "name": "MyRenamedDevice",
    "location": "Chemistry Building 5, Room 15",
    "site": "Left side near the big pillar"
    }
}

The address /api/device/site was subscribed, the initial values were reported. After some time the name was changed and the resulting update was sent as a second JSON object.

Generic Device Information and Settings: Address Tree - /api/device

The device settings are parameters that are common to any compliant device on the network that are relevant to the device as a whole.

/api/device/identity

A read-only resource containing standard information about the device in question. The following entities are mandatory:

  • product: Product identification, should be identical to the designation on the label on the product itself.
  • serial: Unique product number, MUST be unique for this product.
  • vendor: Vendor string, for Sennheiser-products it is „Sennheiser“.

OpenApi snippet:

/api/device/identity:
  get:
    summary: Get the device identity
    description: An SSCv2 Command to retrieve the device identity
    responses:
      '200':
        description: Successful request
        content:
          application/json:
            schema:
              type: object
              properties:
                product:
                  type: string
                  example: TeamConnect Ceiling 2
                hardwareRevision:
                  type: string
                  example: DVT
                serial:
                  type: string
                  example: AB12DEF345
                vendor:
                  type: string
                  example: Sennheiser electronic GmbH & Co. KG
                  enum:
                    - Sennheiser electronic GmbH & Co. KG
      '500':
        description: SSCv2 Server encountered internal error

/api/device/site

User-changeable values helping the user to individualize the device and the most convenient way for the customer to identify the device. The resource MUST contain and allow for configuration the following entities:

  • deviceName: User-settable persistent device name. This name should be the preferred, and most convenient way for the customer to identify devices. If the device has a display, this name SHOULD be displayed there, preferably in the network context; if it has a menu, this name SHOULD be configurable or be derived from /api/device/identity or/and and the location entity of this resource. This name is to be understood as device identification in a network environment.
  • location: User-settable persistent installation location, like e.g. Chemistry Building 5, Room 15. Free-form string entity.
  • position: User-settable persistent position in the installation location in case there are multiple devices at the installation location. Free-form string entity.

The maximum length of string entities CAN be decided by a device and MUST be communicated using the OpenAPI schema for the device.

OpenApi snippet:

/api/device/site:
  get:
    summary: Get the device site information
    description: An SSCv2 Command to retrieve the device site information
    responses:
      '200':
        description: Successful request
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  example: MyDevice
                location:
                  type: string
                  example: Chemistry Building 5, Room 15
                position:
                  type: string
                  example: Left side near the big pillar
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not allowed to access this resource
      '500':
        description: SSCv2 Server encountered internal error
  put:
    summary: Write the device site information
    description: An SSCv2 Command to set the device site information
    requestBody:
      content:
        application/json:
          schema:
            type: object
            properties:
              deviceName:
                type: string
                maximum: 60
                example: MyDevice
              location:
                type: string
                maximum: 240
                example: Chemistry Building 5, Room 15
              position:
                type: string
                maximum: 240
                example: Left side near the big pillar
    responses:
      '200':
        description: Successful operation
      '400':
        description: Bad Request
      '401':
        description: Unauthorized since no user credentials are passed
      '403':
        description: User is not allowed to access this resource
      '500':
        description: Internal Server Error

SSCv2 Guidelines

Dynamic Resources

An SSCv2 Server MAY allow dynamic resources (for example allowing the user of SSCv2 to instantiate DSP modules on demand). The planning of the resources SHOULD follow a "Create, Read, Update, Delete"-approach (CRUD).

A good approach is to define a control resource, which allows for getting a full list of all instantiated dynamic resources associated with this resource (GET) and for creating new resources (POST). An SSCv2 Server MUST always return the newly created dynamic resource as Location-HTTP-header using a status code of 201 - "Created". An SSCv2 Client can then update (PUT) the newly instantiated dynamic resource using the location it received on creation or deinstantiate it using a DELETE request.

A resource controlled by a control resource is called a subresource.

The following rules are defined for control resources:

  • The subresource MUST be one level below the control resource
  • The subresource MUST be variable and specified by a singular template expression (e.g. variable , full path /api/mts/)
  • There MUST NOT be more than one level of resources below a subresource
  • Control resources MUST NOT be nested
  • The control resource MUST be marked with the resource type ControlResource (see x-sennheiser-sscv2-resourcetype, OpenApi vendor extensions)
  • The controlled subresource MUST be specified in the control resource (see x-sennheiser-sscv2-subresource, OpenApi vendor extensions)
  • The elements of the control resource response content SHOULD contain a property which is identical (in both name and type/format) to the resource location path parameter of the subresources. This helps mapping the singular GET request on the control resource and the GET requests/subscription updates of the subresources.

Dynamic Resource Subscriptions

For Dynamic Resources subscriptions are handled in a special manner. A subscription to the the control resource will be recorded in the list of subscribed resources, but implicitly it is a subscription to all controlled subresources, currently existing and created later. A client will receive updates for all subresources and resources below them instead of changes of the control resource. An SSCv2 Server implementing subscriptions for Dynamic Resources adheres to the following rules:

  • A subscription to a control resource MUST only record the subscription to the control resource in the list of subscribed resources
  • On subscription to the control resource the SSCv2 Server MUST send an update for all existing subresources controlled by this control resource, as well as for all resources below the subresources
  • If a client subscribes to a control resource and explicitly to a specific subresource or a resource below a subresource, the SSCv2 Server MUST treat this as two different subscriptions and send two updates for the same resource on update

Dynamic Resource Creation

On creation of a dynamic resource the SSCv2 Server MUST notify the clients subscribed to the control resource. This is done by sending a notification to all clients subscribed to the control resource, using the subresource as key and the empty JSON object "{}" as value. This MUST then be immediately followed by an update with the current values of the subresource.

The following example shows the SSE data notifications for the creation of the resource /api/device/dsp/modules/agc/10

{
    "/api/device/dsp/modules/agc/10":  {}
}
{
    "/api/device/dsp/modules/agc/10":  { "value1": "a", "value2": "b", "value3": "c" }
}

If the subresource has resources below it, the creation notification MUST only be sent for the subresource. When the subresource is created, all resources below it are implicitly created.

Dynamic Resource Deletion

The SSCv2 Server MUST notify the clients subscribed to the control resource when a specific subresource is deleted. This is done by sending a notification to all clients subscribed to the control resource, using the subresource as key and the JSON keyword "null" as value.

The following example shows the SSE data notification for the removal of the resource /api/device/dsp/modules/agc/10

{
    "/api/device/dsp/modules/agc/10":  null
}

If the subresource has resources below it, the deletion notification MUST only be sent for the subresource. When the subresource is deleted, all resources below it are implicitly deleted.

Example for dynamic resources

The following example shows a pattern allowing for ordered creation, monitoring, updating and deletion of dynamic resources:

/api/device/dsp/modules/agc:
  get:
    summary: Returns list of instantiated AGC modules
    description: Returns list of instantiated AGC modules
    x-sennheiser-sscv2-resourcetype: [ "ControlResource" ]
    x-sennheiser-sscv2-subresource: "/api/device/dsp/modules/agc/{instanceId}"
    responses:
      '200':
        description: Successful request
        content:
          application/json:
            schema:
              type: array
              items:
                type: object
                properties:
                  instanceId:
                    type: string
                  value1:
                    type: string
                  value2:
                    type: string
                  value3:
                    type: string
      '403':
        description: Access permission not sufficient
  post:
    summary: Instantiate a new AGC module
    description: Instantiate a new AGC module
    requestBody:
      content:
        application/json:
          schema:
            type: object
            properties:
              value1:
                type: string
              value2:
                type: string
              value3:
                type: string
    responses:
      '201':
        headers:
          Location:
            description: Location of the newly created resource
            schema:
              type: string
        description: Successful creation
      '403':
        description: Access permission not sufficient

/api/device/dsp/modules/agc/{instanceId}:
  parameters:
    - name: instanceId
      in: path
      description: Instance to update
      required: true
      schema:
        type: string
  get:
    summary: Return the currently configured values
    description: Return the currently configured values of the AGC module at instanceId
    responses:
      '200':
        description: Successful request
        content:
          application/json:
            schema:
              properties:
                value1:
                  type: string
                value2:
                  type: string
                value3:
                  type: string
      '403':
        description: Access permission not sufficient
  put:
    summary: Change AGC module at instanceId
    description: Change AGC module at instanceId
    requestBody:
      content:
        application/json:
          schema:
            type: object
            properties:
              value1:
                type: string
              value2:
                type: string
              value3:
                type: string
    responses:
      '200':
        description: Successful request
      '403':
        description: Access permission not sufficient
  delete:
    summary: Delete and Deinstantiate the AGC module at instanceId
    responses:
      '200':
        description: Successful deletion
      '403':
        description: Access permission not sufficient

OpenApi vendor extensions

Special functionality of the SSCv2 API, which is exceeding the generic OpenAPI specification, is marked by the following OpenApi vendor extensions.

x-sennheiser-sscv2-resourcetype

This resource is used to signal special resources, which have behavior exceeding normal REST semantics. The following resource types are specified:

  • FastResource: A resource, which is expected to update frequently. This allows a consumer of the Api to shape the traffic by only subscribing to this resource, when working with the data.

  • ControlResource: A resource, which is following the special semantics laid out in Dynamic Resources. Subscribing to this resource will not trigger subscription updates for the ControlResource, but updates for every subresource instead.

  • RebootingResource: A resource, which will trigger a reboot on PUT/POST interaction.

The resource type vendor extensions MUST be used on specific operations.

Please Note, that prerelease versions of the 2.3 specification standardized usage of a tag "FastResource". Consumers of SSCv2 api specifications MUST treat a tag "FastResource" identically to the respective x-sennheiser-sscv2-resourcetype vendor extension, if they encounter it, while creators of SSCv2 api specifications MUST NOT use the tag FastResource for new specifications.

x-sennheiser-sscv2-subresource

This vendor extension MUST only be used, when the resource is of the resource type ControlResource. It MUST contain the dynamic resource controlled by this control-resource and is used as a hint for code generation.