Webhooks

To extend the default behavior of the Hubcap recorder, we allow users to create webhooks that trigger every time a new recording is made. These webhooks will send a POST request to a URL that you provide with information about the recording that was made attached in the body of the request.

Creating a webhook

Webhooks can be configured separately for both the embeddable button and the shareable link. From their respective settings pages on the dashboard, navigate to the 'Settings' tab where you will see an option to create a new webhook. This will generate a random secret which you can change if you desire, as well as prompting to set a URL where the webhook payload will be sent. Once saved, the webhook will dispatch a payload to the provided URL every time a new recording is made with that recorder.

Securing your Webhook

In order to prevent tampering on your server, it's recommended to validate the signature of incoming requests. Every payload sent by your Hubcap webhooks will include a hash signature generated from the webhook payload using the secret configured on the dashboard. Hubcap uses an HMAC hexdigest to compute the hash using the SHA-256 algorithm, which is then passed to the endpoint as a X-Hubcap-Signature header. To compare the hash, run the request body through the same algorithm using the shared secret as the key and see if the header matches: sha256=**HASHED_PAYLOAD**

An example in javascript, with an express route for validating webhook headers using the node crypto module for hashing:

// Use express as the webhook server
const express = require('express');
const app = express();

// Use body-parser to retrieve the raw body as a buffer
const bodyParser = require('body-parser');

// Use the built in Node crypto module
const crypto = require('crypto');

/**
 * POST /payload
 * Webhook payload endpoint
 */
app.post('/payload', bodyParser.raw({type: 'application/json'}), (req, res) => {
  // Get signature from request header
  const requestSignature = req.get('X-Hubcap-Signature')

  // Note: do not hard-code your secrets! Store them in environment variables:
  const hashedPayload = crypto.createHmac('sha256', process.env.HUBCAP_SECRET)
    .update(req.body) // Hmac.update can use raw buffers
    .digest('hex'); // format as a hexdigest

  // transform signatures to Buffers for use in timingSafeEqual
  const requestBuffer = Buffer.from(requestSignature);

  // prefixed with 'sha256=' to match header
  const payloadBuffer = Buffer.from(`sha256=${hashedPayload}`);

  // timing-safe equality method included in node v6
  if (crypto.timingSafeEqual(requestBuffer, payloadBuffer)) {
    // Handle the webhook payload here:
    console.log('Webhook recieved:', req.body);
    // 2xx status signals successful webhook
    res.sendStatus(200);
  } else {
    // Malformed signature - send back 500 error.
    res.status(500).send('Webhook Error');
  }
});

app.listen(8000, () => console.log('Running on port 8000'));

A couple notes:

  • The header will always have sha256= as the prefix – don't forget to include that in your check!
  • Directly equality operators (such as ==) are not recommended. A method like node's timingSafeEqual performs a "constant time" string comparison, which renders it safe from certain timing attacks against regular equality operators.

Payload Content

Payloads sent in the body of Hubcap webhooks include the following attributes:

Property Description
recordingId unique ID for the created recording
viewUrl static URL for the hosted video on Hubcap
downloadUrl a temporary URL for directly downloading the mp4 video file (NOTE: expires 1 hour after recording is made)
thumbnailUrl a temporary URL for directly downloading the png video thumbnail (NOTE: expires 1 hour after recording is made)

Example payload:

POST /payload HTTP/1.1
Host: your.domain.com
Accept: application/json, text/plain, */*
Content-type: application/json
User-agent: axios/0.18.1
X-Hubcap-Signature: sha256=c8420170a20b01ebb25bae242dc8f3273418aa24d2a1fb824303141bf63de585
Content-length: 1279
Connection: keep-alive
{
  "recordingId": "UR_5k0eyDSrYxy8-J_LG3qGU65BcdycHR",
  "viewUrl": "https://app.hubcap.video/v/UR_5k0eyDSrYxy8-J_LG3qGU65BcdycHR",
  "downloadUrl": "https://storage.googleapis.com/castify-hubcap-recordings/oSmPHHUJQETfEuYmjwjALARH0GCZJyDCx%2Fvideo.mp4?Expires=1568147161&Signature=...",
  "thumbnailUrl": "https://storage.googleapis.com/castify-hubcap-recordings/oSmPHHUJQETfEuYmjwjALARH0GCZJyDCx%2Fthumbnails%2Ffull?Expires=1568147161&Signature=..."
 }