Application Webhooks
What are Webhooks?
Webhooks are powerful tools that allow real-time communication between LiveSwitch and your external systems. They act as customizable event listeners, automatically sending notifications to your specified web server whenever key actions occur within LiveSwitch. This enables you to seamlessly integrate LiveSwitch events into your own applications and workflows.
Here's how it works:
- You define a webhook by specifying a URL for your server endpoint.
- LiveSwitch monitors for specific events you're interested in.
- When an event occurs, LiveSwitch sends an HTTP POST request to your URL.
- The request body contains detailed event information in JSON format.
- Your server can then process this data and respond accordingly.
With webhooks, you can build responsive, event-driven applications that leverage LiveSwitch's capabilities while seamlessly integrating with your existing infrastructure.
Webhooks are most commonly used to notify customers when a session's recording is ready for download. After a session ends, the system processes the recording by mixing audio and video from all participants into a single file. Once the mixed recording is ready, the system sends a notification via the registered webhook, allowing customers to download the complete recording without manually checking for updates.
Application Webhooks allow hooking into application-level events.
Webhook Validation
All Application and Channel webhooks include the header X-ApplicationSignature
.
To verify the source of a webhook, the receiver creates a HMAC-SHA256 hash of the webhook's JSON body. That hash uses the LiveSwitch Application's shared secret as its secret key. The receiver then converts the HMAC-SHA256 hash to Base64. The Base64 value must match the value in the X-ApplicationSignature
header for the webhook to be validated.
Webhook Validation Example
The following example is from a Node application using Express and TypeScript. This method accepts a raw JSON body and the request signature. To find the associated shared secret, the Application ID is extracted from the request body. Once the application finds the shared secret, it uses the raw request body as input to generate a HMAC-SHA256 value. If this value matches the requestSignature
, then the webhook is validated.
Important
Store your shared secret in a secure location.
/*
* Core validation code looks up the application secret
* based on the applicationId found in the request body,
* then computes a SHA256 hash to ensure it matches
* the signature found in the x-applicationsignature
* header of the request.
*/
function validate(rawRequestBody:string, requestSignature:string):boolean {
// Parse the raw request in order to extract the applicationId
const webhook = JSON.parse(rawRequestBody);
const applicationId = getApplicationId(webhook);
// If we don't have a signature (X-ApplicationSignature wasn't present)
// AND there is no detected applicationId - then we've received a
// deployment webhook and we cannot verify with an application
// shared secret.
if (!requestSignature && !applicationId) {
console.log("Received unsigned deployment webhook");
return true;
}
// If there is an applicationId found, but no request signature
// this is not a valid webhook request.
if (applicationId && !requestSignature) {
console.error("Received an unsigned application webhook");
return false;
}
// If no applicationId was found, or we do not know the
// shared secret to use for the applicationId, then
// we cannot vaildate the request.
if (!applicationId || !applicationSecrets[applicationId]) {
console.error("Unable to determine application secret");
return false;
}
// For demo purposes we lookup the shared secret from the
// hardcoded applicationId map - for production use this
// should come from configuration. Shared secrets should
// never be stored in source code.
const secret = applicationSecrets[applicationId];
// Create a HMAC-SHA256 hash function with the application's
// shared secret
const hmac = crypto.createHmac("sha256", secret);
// Use the raw request body as input to the function.
// This must be the raw incoming request body. If it
// has been parsed as json in anyway prior you will
// likely not end up with the original value that was
// signed on the server.
hmac.update(rawRequestBody);
// Run the HMAC-SHA256 function and get the result
// as base64
const base64HmacSha256Hash = hmac.digest("base64");
// Trim all trailing `=` characters. All signatures
// will always be in base64 with the trailing padding
// `=` characters trimmed from the end of the string.
const base64HmacSha256HashTrimmed = trimEnd(base64HmacSha256Hash, "=");
// If the trimmed base64 hash does not exactly match
// the value from the X-ApplicationSignature header
// then this is an invald request.
if (base64HmacSha256HashTrimmed !== requestSignature) {
console.error("signature does not match computed hash");
return false;
}
// If the header matches our computed value, then
// we have succesfully validated the request.
return true;
}
Take note of the following when validating webhooks:
- The input of the HMAC-SHA256 function must be the raw JSON request body. If the request went through any parsers that might have changed the JSON, then the input value might be different than what the server used to generate the hash. For example, if the webhook payload included a field like
frameRate: 30.0
that parses toframeRate:30
, the generated HMAC-SHA256 hash will be affected. If you use Express with its built-in JSON middleware, then the request body might be parsed. In this case, use a custom parser to access the unparsed JSON body. - The value in
X-ApplicationSignature
is Base64, with all trailing=
padding characters stripped. After computing the Base64 hash, strip the trailing=
characters before comparing it with theX-ApplicationSignature
header.
Client
Client Registered
This event triggers when a new client connects to an Application ID.
Client Registered sample JSON object
{
"timestamp": 1562614545646,
"origin": "client",
"type": "client.registered",
"client": {
"version": 0.0.0.0,
"applicationId": "my-app-id",
"userId": "d8c5a6527deb4cc6bf4afb908e682e5d",
"userAlias": "Anonymous",
"deviceId": "9ffa994a648940b997ac79db343d0f7d",
"tag": 1,
"sourceLanguage": "typescript",
"machineName": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36",
"coreCount": 6,
"physicalMemory": "8589934592",
"operatingSystem": "Windows 10",
"architecture": "x64",
"id": "28c9d925bd0043b5aa89e8cd75a5317e",
}
}
Client Unregistered
This event triggers when a client unregisters from the gateway by leaving a channel.
Client Unregistered sample JSON object
{
"timestamp": 1562614545646,
"origin": "client",
"type": "client.unregistered",
"client": {
"version": 0.0.0.0,
"applicationId": "my-app-id",
"userId": "d8c5a6527deb4cc6bf4afb908e682e5d",
"userAlias": "Anonymous",
"deviceId": "9ffa994a648940b997ac79db343d0f7d",
"tag": 1,
"report": [...],
"id": "28c9d925bd0043b5aa89e8cd75a5317e"
}
}
Client Updated
This event triggers when a client's information is updated on the gateway (a username, also known as userAlias, change, for example).
Client Updated sample JSON object
{
"timestamp": 1562614545646,
"origin": "client",
"type": "client.updated",
"client": {
"applicationId": "my-app-id",
"userId": "d8c5a6527deb4cc6bf4afb908e682e5d",
"userAlias": "stephen",
"deviceId": "9ffa994a648940b997ac79db343d0f7d",
"deviceAlias": "camera 1",
"id": "my-app-id/4531e51218b24fb58d5dc62087a93439",
"tag": "1",
"region": "en-us",
"coreCount": 6,
"architecture": "x64",
"machineName": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36",
"operatingSystem": "Windows 10",
"operatingSystemVersion": "1809",
"physicalMemory": "8589934592",
"sourceLanguage": "typescript",
"version": "0.0.0.0"
}
}
Client Message
This event triggers when a user sends a message to the server.
Client Message sample JSON object
{
"timestamp":1627339445785,
"origin":"client",
"type":"client.message",
"client":{
"id":"d4c1d83ea4c64ec587703bc4867ea1d2",
"applicationId":"2250d2f7fd4a4750ac90df8d5a9f25da",
"userId":"e0117c2672c44f4c80de4326ec520d8b",
"deviceId":"56491ac67cba46e69441b8bd9b9917da"
},
"message":{
"payload":"Sample client message to server"
}
}
Server
Server Message
This event triggers when a server sends a message to an entire application or a specific user, device, or client in the application. Server messaging is a broadcast mechanism for administrative communications. Its primary use cases included: a) notifying users of scheduled maintenance, b) alerting users to system issues, c) announcing important events or updates.
Server Message sample JSON object
{
"timestamp":1627339445317,
"origin":"gateway",
"type":"server.message",
"client":{
"applicationId":"2250d2f7fd4a4750ac90df8d5a9f25da",
"userId":"server-user-id",
"userAlias":"server-user-alias",
"deviceId":"server-device-id",
"deviceAlias":"server-device-alias",
"tag":"server-client-tag",
"roles":[...],
"id":"00000000000000000000000000000000",
},
"message":{
"userId":"e0117c2672c44f4c80de4326ec520d8b", // To a specific user
"deviceId":"56491ac67cba46e69441b8bd9b9917da", // To a specific device
"clientId":"d4c1d83ea4c64ec587703bc4867ea1d2", // To a specific client
"payload":"Sample server message to application"
}
}