Overview
Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.
The Solstice Active Routing API enables developers, engineers, and consultants to mirror a Solstice display to an arbitrary number of other Solstice displays. It is used by the Solstice Active Learning application.
Active Routing is infrastructure that supports products in a variety of areas such as Active Learning, Multiroom Sharing, and other potential markets that require the duplication of content or collaboration across multiple screens or conference rooms that reside in a single local-area network (LAN).
This is a REST API, that returns JSON responses.
Because Active Routing use cases involve mirroring one display to many, you will need to have at least 2 Solstice Pods to utilize the API. If you don't have an HDMI Multi-viewer, you will also need a display for each Pod.
Python sample code is available to help you get started.
Terminology
- source display for the Solstice pod sending content
- sink display for the Solstice pod receiving content
- A sink has been captured by the source pod when a mirroring connection is made
Getting Started
Step 1: Authenticate
Code samples
curl -k -X POST \
--header "content-type: application/x-www-form-urlencoded" \
https://<podip>:5443/v2/token \
-d "grant_type=password&username=admin&password="
import json
import requests
HOST = '<Replace with source Pod IP address>'
password = ''
headers = {'content-type': 'application/x-www-form-urlencoded'}
payload = f'grant_type=password&username=admin&password={password}'
r = requests.post('https://' + HOST + ':5443/v2/token', headers=headers, data=payload, verify=False)
print(f'Status code: {r.status_code}')
print(f'Response: {r.json()}')
print(r.json())
Your application must authenticate to the source Pod in order to successfully execute API calls. The v2 API uses the Oauth2 "password grant flow", which returns a JWT that can be used in the Authorization
header of subsequent API calls.
Step 2: Configure Source
Code samples
curl -k -X PATCH \
--header "Authorization: Bearer <JWT>" \
--header "Content-Type: application/json" \
https://<podip>:5443/v2/content/activerouting \
-d '{"presence": false, "message": "Sharing!", "background": "#F95127", "foreground": "#FFFFFF"}'
import json
import requests
HOST = '<Replace with source Pod IP address>'
access_token = '<Replace with access token from auth>'
# Configure session parameters
payload = {}
payload['presence'] = False
payload['message'] = 'Sharing!'
payload['background'] = '#F95127'
payload['foreground'] = '#FFFFFF'
headers = {'content-type': 'application/json', 'Authorization': 'Bearer ' + access_token}
r = requests.patch('https://' + HOST + ':5443/v2/content/activerouting', headers=headers, data=json.dumps(payload), verify=False)
print(f'Status code: {r.status_code}')
print(f'Result: {r.json()}')
You must configure the source pod before capturing the sink displays. To do this, pass the Authorization
header and Content-Type
, along with the JSON payload to the /v2/content/activerouting
endpoint.
Step 3: Capture Sink
Code samples
curl -k -X POST \
--header "Authorization: Bearer <JWT>" \
--header "Content-Type: application/json" \
https://<podip>:5443/v2/content/activerouting/connections \
-d '{"sink": "<sinkIp>", "post": "fullscreen", "message": "Testing from API", "background": "#F95127", "foreground": "#FFFFFF", "resolution": "1920x1080"}'
import json
import requests
HOST = '<Replace with source Pod IP address>'
access_token = '<Replace with access token from auth>'
sink = '<Replace with Pod to which you will be mirroring>'
payload = {}
payload['post'] = 'fullscreen'
payload['message'] = 'Testing from API'
payload['foreground'] = '#FFFFFF'
payload['background'] = '#F95127'
payload['resolution'] = '1920x1080'
payload['sink'] = sink
headers = {'content-type': 'application/json', 'Authorization': 'Bearer ' + access_token}
r = requests.post('https://' + HOST + ':5443/v2/content/activerouting/connections', headers=headers, data=json.dumps(payload), verify=False)
print(f'Status code: {r.status_code}')
print(f'Result: {r.json()}')
In the last step, capture the sink to begin mirroring the source display. To do this, pass the Authorization
header and Content-Type
, along with the JSON payload to the /v2/content/activerouting/connections
endpoint.
Active Routing API v1
Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.
Base URLs:
Authentication
-
oAuth2 authentication. Supports password flow https://tools.ietf.org/html/rfc6749#section-4.3.2
-
Flow: password
-
Token URL = /v2/token
-
Scope | Scope Description |
---|
Token
Request a token
Code samples
# You can also use wget
curl -X POST https://replace_with_pod_IP_or_host:5443/v2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json'
import requests
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
}
r = requests.post('https://replace_with_pod_IP_or_host:5443/v2/token', headers = headers)
print(r.json())
POST /token
Request a bearer token for the v2 API
Body parameter
grant_type: password
username: string
password: string
Parameters
Name | In | Type | Required | Description |
---|---|---|---|---|
body | body | TokenRequest | true | Subset of config to update |
Example responses
200 Response
{
"access_token": "string",
"token_type": "jwt"
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | OAuth 2.0 access token response | TokenSuccess |
400 | Bad Request | Bad request (e.g. malformed body) | None |
401 | Unauthorized | Invalid credentials | None |
default | Default | An unexpected error | Errors |
Response Schema
Active Routing
Get current session data
Code samples
# You can also use wget
curl -X GET https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/ \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.get('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/', headers = headers)
print(r.json())
GET /content/activerouting/
Example responses
200 Response
{
"state": "none",
"connections": 0,
"framesPerSecond": 0,
"bytesPerSecond": 0,
"presence": true
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | SESSION data for active routing | ActiveRoutingSession |
401 | Unauthorized | Bad authorization token | Errors |
default | Default | An unexpected error | Errors |
Update current session data
Code samples
# You can also use wget
curl -X PATCH https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/ \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.patch('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/', headers = headers)
print(r.json())
PATCH /content/activerouting/
Body parameter
{
"presence": true,
"message": "string",
"foreground": "string",
"background": "string"
}
Parameters
Name | In | Type | Required | Description |
---|---|---|---|---|
body | body | ActiveRoutingConfigure | true | CONFIGURE active routing session |
Example responses
400 Response
{
"errors": [
{
"error": "invalid_request",
"description": "string"
}
]
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | Active Routing configuration updated | None |
400 | Bad Request | Bad request (e.g. malformed request body) | Errors |
401 | Unauthorized | Bad authorization token | Errors |
default | Default | An unexpected error | Errors |
Update overlay text on display
Code samples
# You can also use wget
curl -X POST https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/overlaytext \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.post('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/overlaytext', headers = headers)
print(r.json())
POST /content/activerouting/overlaytext
Body parameter
{
"text": "string",
"color": "string"
}
Parameters
Name | In | Type | Required | Description |
---|---|---|---|---|
body | body | ActiveRoutingOverlay | true | OVERLAY text onto active routing display |
Example responses
401 Response
{
"errors": [
{
"error": "invalid_request",
"description": "string"
}
]
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | Text overlaid onto display | None |
401 | Unauthorized | Bad authorization token | Errors |
default | Default | An unexpected error | Errors |
Get licensing information
Code samples
# You can also use wget
curl -X GET https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/licensing \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.get('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/licensing', headers = headers)
print(r.json())
GET /content/activerouting/licensing
Example responses
200 Response
{
"trial": "string",
"subscription": "string",
"licensed": true
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | Retrieve LICENSING information for active routing | ActiveRoutingLicensing |
401 | Unauthorized | Bad authorization token | Errors |
default | Default | An unexpected error | Errors |
Get active routing connections
Code samples
# You can also use wget
curl -X GET https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.get('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections', headers = headers)
print(r.json())
GET /content/activerouting/connections
Example responses
200 Response
{
"connections": {
"property1": {
"guid": "string",
"ip": "192.168.0.1",
"name": "string"
},
"property2": {
"guid": "string",
"ip": "192.168.0.1",
"name": "string"
}
}
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | ENUMERATE all active routing connections | ActiveRoutingEnumerate |
401 | Unauthorized | Bad authorization token | Errors |
default | Default | An unexpected error | Errors |
Create an active routing connection
Code samples
# You can also use wget
curl -X POST https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.post('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections', headers = headers)
print(r.json())
POST /content/activerouting/connections
Body parameter
{
"sink": "192.168.0.1",
"resolution": "string",
"post": "normal",
"message": "string",
"foreground": "string",
"background": "string",
"audio": true
}
Parameters
Name | In | Type | Required | Description |
---|---|---|---|---|
body | body | ActiveRoutingConnect | true | CONNECT active routing session (address, resolution, post type) |
Example responses
200 Response
{
"guid": "string"
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | Creates a new active routing connection and returns guid. | ActiveRoutingGuid |
400 | Bad Request | Bad request (e.g. malformed request body) | Errors |
401 | Unauthorized | Bad authorization token | Errors |
default | Default | An unexpected error | Errors |
Delete all active routing connections
Code samples
# You can also use wget
curl -X DELETE https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.delete('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections', headers = headers)
print(r.json())
DELETE /content/activerouting/connections
Example responses
200 Response
{
"message": "string",
"reboot_required": true,
"restart_required": true
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | TERMINATE all active routing connections | Success |
401 | Unauthorized | Bad authorization token | Errors |
default | Default | An unexpected error | Errors |
Delete a single active routing connection
Code samples
# You can also use wget
curl -X DELETE https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections/{guid} \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}'
import requests
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer {access-token}'
}
r = requests.delete('https://replace_with_pod_IP_or_host:5443/v2/content/activerouting/connections/{guid}', headers = headers)
print(r.json())
DELETE /content/activerouting/connections/{guid}
Parameters
Name | In | Type | Required | Description |
---|---|---|---|---|
guid | path | string(uuid) | true | GUID of the active routing connection |
Example responses
200 Response
{
"message": "string",
"reboot_required": true,
"restart_required": true
}
Responses
Status | Meaning | Description | Schema |
---|---|---|---|
200 | OK | DISCONNECT active routing connection | Success |
401 | Unauthorized | Bad authorization token | Errors |
404 | Not Found | Active routing connection for that GUID could not be found. | None |
default | Default | An unexpected error | Errors |
Schemas
ActiveRoutingConfigure
{
"presence": true,
"message": "string",
"foreground": "string",
"background": "string"
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
presence | boolean | true | none | Display the presence bar on mirrored pods |
message | string | true | none | Text to be displayed in the notch on source. |
foreground | string | true | none | Text color for message displayed in the notch on source. |
background | string | true | none | Background color of notch on source. Supported colors: #B8BDBF, #7080D4, #7AC4E5, #00BE89, #FF423D, #FB8048, #F3B11B, #F36F8F, #945ECF, #8F9FA5, #61B424, #55A3D8 |
ActiveRoutingConnect
{
"sink": "192.168.0.1",
"resolution": "string",
"post": "normal",
"message": "string",
"foreground": "string",
"background": "string",
"audio": true
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
sink | string(ipv4) | true | none | The display to which you are mirroring. |
resolution | string | true | none | Supported value is '1920x1080' |
post | ActiveRoutingPostType | true | none | Determines whether the sink display is fullscreen or normal. Fullscreen is required to display the colored notch. |
message | string | true | none | The text to be displayed in the notch. |
foreground | string | true | none | Text color for message displayed in the notch. |
background | string | true | none | Background color of notch. Supported colors: #B8BDBF, #7080D4, #7AC4E5, #00BE89, #FF423D, #FB8048, #F3B11B, #F36F8F, #945ECF, #8F9FA5, #61B424, #55A3D8 |
audio | boolean | false | none | Chooses whether mirroring includes audio or not. (optional) |
ActiveRoutingEnumerate
{
"connections": {
"property1": {
"guid": "string",
"ip": "192.168.0.1",
"name": "string"
},
"property2": {
"guid": "string",
"ip": "192.168.0.1",
"name": "string"
}
}
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
connections | object | true | none | none |
» additionalProperties | ActiveRoutingItem | false | none | none |
ActiveRoutingGuid
{
"guid": "string"
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
guid | string(uuid) | true | none | none |
ActiveRoutingItem
{
"guid": "string",
"ip": "192.168.0.1",
"name": "string"
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
guid | string(uuid) | true | none | none |
ip | string(ipv4) | true | none | none |
name | string | true | none | none |
ActiveRoutingLicensing
{
"trial": "string",
"subscription": "string",
"licensed": true
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
trial | string | true | none | none |
subscription | string | true | none | none |
licensed | boolean | true | none | none |
ActiveRoutingOverlay
{
"text": "string",
"color": "string"
}
Properties used to define text displayed over top of mirrored content.
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
text | string | true | none | none |
color | string | true | none | none |
ActiveRoutingPostType
"normal"
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
anonymous | string | false | none | none |
Enumerated Values
Property | Value |
---|---|
anonymous | normal |
anonymous | fullscreen |
ActiveRoutingSession
{
"state": "none",
"connections": 0,
"framesPerSecond": 0,
"bytesPerSecond": 0,
"presence": true
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
state | ActiveRoutingSessionState | true | none | Allowed state values |
connections | integer(int32) | true | none | Number of connections in session |
framesPerSecond | integer(int32) | true | none | Frames per second for current session |
bytesPerSecond | integer(int32) | true | none | Bytes per second for current session |
presence | boolean | true | none | Is presence bar being shared in session |
ActiveRoutingSessionState
"none"
Values that are allowed for session state.
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
anonymous | string | false | none | Values that are allowed for session state. |
Enumerated Values
Property | Value |
---|---|
anonymous | none |
anonymous | source |
anonymous | sink |
Error
{
"error": "invalid_request",
"description": "string"
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
error | ErrorType | true | none | internal - Internal error; path_failed - Patch element failed to apply at path specified in description ; invalid_request - Invalid request body or missing header; invalid_token - Invalid or missing authorization token in request |
description | string | false | none | none |
ErrorType
"invalid_request"
internal
- Internal error; path_failed
- Patch element failed to apply at path specified in description
; invalid_request
- Invalid request body or missing header; invalid_token
- Invalid or missing authorization token in request
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
anonymous | string | false | none | internal - Internal error; path_failed - Patch element failed to apply at path specified in description ; invalid_request - Invalid request body or missing header; invalid_token - Invalid or missing authorization token in request |
Enumerated Values
Property | Value |
---|---|
anonymous | invalid_request |
anonymous | not_found |
anonymous | resource_conflict |
anonymous | path_failed |
anonymous | internal |
anonymous | not_licensed |
anonymous | invalid_token |
Errors
{
"errors": [
{
"error": "invalid_request",
"description": "string"
}
]
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
errors | [Error] | false | none | none |
GrantType
"password"
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
anonymous | string | false | none | none |
Enumerated Values
Property | Value |
---|---|
anonymous | password |
Success
{
"message": "string",
"reboot_required": true,
"restart_required": true
}
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
message | string | false | none | none |
reboot_required | boolean | false | none | none |
restart_required | boolean | false | none | none |
TokenErrorType
"invalid_request"
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
anonymous | string | false | none | none |
Enumerated Values
Property | Value |
---|---|
anonymous | invalid_request |
anonymous | unauthorized_client |
TokenRequest
{
"grant_type": "password",
"username": "string",
"password": "string"
}
See https://tools.ietf.org/html/rfc6749#section-4.3.2
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
grant_type | GrantType | true | none | none |
username | string | true | none | none |
password | string | true | none | none |
TokenSuccess
{
"access_token": "string",
"token_type": "jwt"
}
See https://tools.ietf.org/html/rfc6749#section-5.1
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
access_token | string | true | none | none |
token_type | TokenType | true | none | none |
TokenType
"jwt"
Properties
Name | Type | Required | Restrictions | Description |
---|---|---|---|---|
anonymous | string | false | none | none |
Enumerated Values
Property | Value |
---|---|
anonymous | jwt |