Notification API
The PCast™ streaming platform can notify your backend via web hook on certain stream events.
- Stream publishing starts
- Stream publishing stops
- Live HLS and DASH streams available
- Video on demand asset available
Register Notification Endpoint
Request
You can set the callback endpoint for your application using a PUT
request.
PUT /pcast/application/<applicationId>/callback HTTP/1.1
Host: pcast.phenixrts.com
Accept: application/json
Content-Type: application/json
Content-Length: 194
Authorization: Basic YXBwbGljYXRpb25JZDpzZWNyZXQ=
{
"callback": {
"protocol": "<protocol>",
"host": "<host>",
"port": <port>,
"method": "<method>",
"path": "<path>",
"query": "<query>"
}
}
The Content-Length header is automatically populated by curl. If using another method to access the API, you must calculate and populate the Content-Length header.
Request Fields
Field | Description |
---|---|
credentials (optional) | The preferred way of passing the credentials is with HTTP authentication header. Alternatively they can be passed in the request body (deprecated). |
callback (required) | The object defining the callback endpoint. See object structure below. |
Callback Object
Field | Default Value | Description |
---|---|---|
protocol (optional) | "http" | The protocol to use for notifications: "http" or "https" |
host (required) | The host to use for notifications (E.g. "mywebsite.com") | |
port (optional) | 80 (HTTP) or 443 (HTTPS) | A numerical (not a string) value defining the port to use for notifications |
method (optional) | "POST" | The HTTP method to use for notifications: "GET", "POST" or "PUT" |
path (optional) | "/" | The path to use for notifications (E.g. "/phenix/callback") |
query (optional) | empty | The query parameters (E.g. "param1=true&param2=false&param3") |
Response
The platform will return a successful response that contains a "status" field. The HTTP status code is set according to the status
field.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 107
{"status":"ok","endpoint":"POST https://webhook.site:443/1234abcd-1234-6789-0123-abcdef123456?param1=true"}
Status Codes
HTTP | Status | Retry | Description |
---|---|---|---|
200 OK | ok | never | Stream was successfully terminated. |
400 Bad Request | varies | never | Indicates an issue with the request itself. |
401 Unauthorized | unauthorized | never | The streaming platform was not able to authorize the provided credentials. |
4XX | varies | never | Indicates an issue with the request itself. |
503 Service Unavailable | capacity | once | The system is temporarily overloaded. Please try again later. |
5XX | varies | once immediately, then exponential backoff | A transient server error. |
Response Fields
Field | Description |
---|---|
status | The status code |
endpoint | The resolved end point to confirm your settings were applied as intended. |
Notification Types
The streaming platform notifies the callback endpoint when certain platform events occur.
Event Name | Description |
---|---|
Stream Started | Triggers when a new stream is published |
Stream Ended | Triggers when a published stream ends |
Live Available | Triggers when a live playlist of a published stream is ready |
On-Demand Available | Triggers when a on-demand playlist of a published stream is ready |
We will be using the
callback.method
ofPOST
in the examples for each notification type below. However "GET", "POST" or "PUT" are fully supported.
To debug notifications, consider the webhook.site online tool.
Details about the event, such as channelId and sessionId, will be in tags
in the content of the notification.
Stream Started Notification
POST <callback.path> HTTP/1.1
Host: <callback.host>:<callback.port>
Content-Type: application/json
Content-Length: 250
{
"apiVersion": 0,
"entity", "stream",
"what", "started",
"data": {
"streamId": "<streamId>",
"tags": [
"<tag>"
]
},
"sessionId": "<sessionId>",
"timestamp": "2019-01-23T21:12:55.384Z"
}
The Content-Length header is automatically populated by curl. If using another method to access the API, you must calculate and populate the Content-Length header.
Request Fields
Field | Description |
---|---|
apiVersion | The API version the message conforms to |
entity | Belongs to the "stream" entity |
what | The type is "started" indicating that the stream started |
data | The notification payload |
data.streamId | The ID of the stream that started |
data.tags | Array of string tags provided when publishing the stream. The order of tags may be different than the order of tags provided during publishing. |
sessionId | The session ID of the client this stream belongs to |
timestamp | The RFC3339 timestamp of when the request was initiated in Zulu time |
Stream Ended Notification
POST <callback.path> HTTP/1.1
Host: <callback.host>:<callback.port>
Content-Type: application/json
Content-Length: 304
{
"apiVersion": 0,
"entity", "stream",
"what", "ended",
"data": {
"streamId": "<streamId>",
"tags": [
"<tag>"
]
"duration": 11457,
"reason": "<reason>"
},
"sessionId": "<sessionId>",
"timestamp": "2019-01-23T21:12:55.384Z"
}
The Content-Length header is automatically populated by curl. If using another method to access the API, you must calculate and populate the Content-Length header.
Request Fields
Field | Description |
---|---|
apiVersion | The API version the message conforms to |
entity | Belongs to the "stream" entity |
what | The type is "ended" indicating that the stream ended |
data | The notification payload |
data.streamId | The ID of the stream that started |
data.tags | Array of string tags provided when publishing the stream |
data.duration | Duration of the stream in milliseconds |
data.reason | The reason the stream ended which may be an empty string or a custom reason |
sessionId | The session ID of the client this stream belongs to |
timestamp | The RFC3339 timestamp of when the request was initiated in Zulu time |
Live Available Notification
Reach devices with adaptive multi-bitrate live streaming capabilities. Currently HLS and MPEG-DASH playlists in adaptive multi-bitrate and various quality levels are supported.
This also makes the stream available inside all our SDKs using the streaming
capability when subscribing.
There is one event for each available playlists, e.g. adaptive multi-bitrate and each quality level.
POST <callback.path> HTTP/1.1
Host: <callback.host>:<callback.port>
Content-Type: application/json
Content-Length: 222
{
"apiVersion": 0,
"entity", "stream",
"what", "live",
"data": {
"streamId": "<streamId>",
"uri": "<uri>"
},
"sessionId": "<sessionId>",
"timestamp": "2019-01-23T21:12:55.384Z"
}
The Content-Length header is automatically populated by curl. If using another method to access the API, you must calculate and populate the Content-Length header.
Request Fields
Field | Description |
---|---|
apiVersion | The API version the message conforms to |
entity | Belongs to the "stream" entity |
what | Type "live" indicates a live playlist is available |
data | The notification payload |
data.streamId | The ID of the stream that started |
data.uri | The URI of the playlist. File extensions: HLS -> .m3u8, MPEG-DASH -> .mpd |
data.isPrimary | Whether this is a primary playlist containing variant playlists |
data.isVariant | Whether this is a variant playlist (Note: This flag is currently wrong. Use isPrimary until it is fixed.) |
sessionId | The session ID of the client this stream belongs to |
timestamp | The RFC3339 timestamp of when the request was initiated in Zulu time |
On-demand Available Notification
Provide adaptive multi-bitrate on-demand streaming capabilities. Currently HLS and MPEG-DASH playlists as adaptive multi-bitrate and specific quality levels are supported.
This also makes the stream available inside all our SDKs using the "on-demand" capability when subscribing.
There is one event for each available playlists, e.g. adaptive multi-bitrate and each quality level.
POST <callback.path> HTTP/1.1
Host: <callback.host>:<callback.port>
Content-Type: application/json
Content-Length: 227
{
"apiVersion": 0,
"entity", "stream",
"what", "on-demand",
"data": {
"streamId": "<streamId>",
"uri": "<uri>",
"isPrimary": true,
"isVariant": false
},
"sessionId": "<sessionId>",
"timestamp": "2019-01-23T21:12:55.384Z"
}
The Content-Length header is automatically populated by curl. If using another method to access the API, you must calculate and populate the Content-Length header.
Request Fields
Field | Description |
---|---|
apiVersion | The API version the message conforms to |
entity | Belongs to the "stream" entity |
what | Type "on-demand" indicates a on-demand playlist is available |
data | The notification payload |
data.streamId | The ID of the stream that started |
data.uri | The URI of the playlist. File extensions: HLS -> .m3u8, MPEG-DASH -> .mpd |
data.isPrimary | Whether this is a primary playlist containing variant playlists |
data.isVariant | Whether this is a variant playlist (Note: This flag is currently wrong. Use isPrimary until it is fixed.) |
sessionId | The session ID of the client this stream belongs to |
timestamp | The RFC3339 timestamp of when the request was initiated in Zulu time |
Note on 5XX Errors
The notification service will automatically retry any 5XX HTTP errors using an exponential back-off algorithm. The first back-off is 3 seconds, the second one is 6 seconds and so on. At most 9 retries will be performed which amounts to a maximum of 25 minutes until a request is dropped.
Order of Notifications
Due to the nature of distributed environments and various effects including retries of transient errors, it's possible that notifications are delivered out of natural order. For example, it is possible that you receive a stream ended notification before the corresponding stream started notification. Please make sure that your business logic can handle out-of-order notifications.
Note On API version
It is expected that your application can handle any new fields inside a message and any new notification types without a corresponding increment of the "apiVersion" field.
Web Hook Security
HMAC Authentication
You can authenticate notifications by using the HMAC-SHA256 authentication scheme. HMAC refers to hash-based message authentication code.
To authenticate a notification, you must assert that it contains valid X-PCast-Content-Sha256 and Authorization headers.
Notification Headers
Header | Description |
---|---|
Authorization | Authentication information required by the HMAC-SHA256 scheme. |
Content-Type | Indicates the original media type before any content encoding was applied for sending. |
X-PCast-Content-Sha256 | Base64 encoded SHA256 hash of the request body. |
X-PCast-Timestamp | The RFC3339 timestamp of when the request was initiated in UNIX time. |
Validate X-PCast-Content-Sha256
To validate X-PCast-Content-Sha256
header you need to stringify the request body, and generate a SHA-256 hash from it. Then you need to compare generated hash with the hash in X-PCast-Content-Sha256
.
//Validate X-PCast-Content-Sha256
function isContentSha256Valid(requestStringBody, contentSha256Header) {
var generatedContentHash = crypto
.createHash('sha256')
.update(requestStringBody)
.digest('base64');
return contentSha256Header === generatedContentHash;
}
Validate Authorization Header
The Authorization
header consists of the HMAC hash of the request prefixed with HMAC-SHA256
.
Authorization = 'HMAC-SHA256 ' + hash (HMAC created using the SHA-256 hash algorithm with the application secret as key).
To validate the HMAC hash of the Authorization
header you must compare it with the one regenerated from the canonical representation of the request using your application secret as a key.
String-To-Sign = HTTP_METHOD + path_and_query + content_sha256_header + content_type_header + pcast_timestamp_header
// Generate string to sign
function getStringToSign(
method,
path,
contentSha256,
contentType,
unixTimestamp
) {
return method + path + contentSha256 + contentType + unixTimestamp;
}
// Validate Authorization header
function isAuthorizationHeaderValid(secret, stringToSign, authorizationHeader) {
var generatedHmacSha256Hash =
'HMAC-SHA256 ' +
crypto.createHmac('sha256', secret).update(stringToSign).digest('base64');
return generatedHmacSha256Hash === authorizationHeader;
}