Webhooks (Web Callback, HTTP Push API or Reverse API) are one way that a web application can send information to another application in real-time when a specific event happens.

Webhook Vs API

APIs send you the data when you request it. For Webhooks, you do not need to make a request. You receive the data when it is available, based on the subscriotion of events.

Configuring webhooks

  1. Configure webhooks in the configuration center.
    This way we know which event(s) you are interested in and to which endpoints we should send them.
  2. Set up your webhooks endpoint.
    This means that you need to build an application that handles incoming webhooks events and run that on an HTTPS endpoint on your server. These webhooks also need to be verified.

Configuring webhooks in the configuration center

To set up webhooks in the configuration center you need to do the following:

  • Add a webhooks endpoint.
  • Subscribe from the list of available events.
  • Webhook signature is auto generated.

Setting up your webhooks endpoint

The webhooks messages will be sent to your endpoints via HTTPS POST requests. The body of the request contains JSON.

To actually handle these incoming messages you should set up your server to listen to POST requests on the endpoints you've set up in the configuration center.

In order to prevent attackers impersonating services by simply sending a fake webhook to an endpoint, Bureau signs every webhook and its metadata with a unique key for each endpoint. This signature can then be used to verify the webhook indeed comes from Bureau, and only process it if it is.

Verifying signatures manually​

Each webhook call includes three headers with additional information that are used for verification:

  • svix-id- the unique message identifier for the webhook message. This identifier is unique across all messages, but will be the same when the same webhook is being resent (e.g. due to a previous failure).
  • svix-timestamp - timestamp in seconds since epoch
  • svix-signature: the Base64 encoded list of signatures (space delimited).

Constructing the signed content​

The content to sign is composed by concatenating the id, timestamp and payload, separated by the full-stop character (.). In code, it will look something like:

signedContent = "${svix_id}.${svix_timestamp}.${body}"

Where body is the raw body of the request. The signature is sensitive to any changes, so even a small change in the body will cause the signature to be completely different. This means that you should not change the body in any way before verifying.

Determining the expected signature​

Bureau uses an HMAC with SHA-256 to sign its webhooks.

So to calculate the expected signature, you should HMAC the signedContent from above using the base64 portion of your signing secret (this is the part after the whsec_ prefix) as the key. For example, given the secret whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw you will want to use MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw.

This generated signature should match one of the ones sent in the svix-signature header.

// Node JS Code
const crypto = require('crypto');

signedContent = `${svix_id}.${svix_timestamp}.${body}`
const secret = "whsec_5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH";

// Need to base64 decode the secret
const secretBytes = new Buffer(secret.split('_')[1], "base64");
const signature = crypto
  .createHmac('sha256', secretBytes)
  .update(signedContent)
  .digest('base64');
console.log(signature);

Retry Schedule

We attempt to deliver each webhook message based on a retry schedule with exponential backoff.

The schedule​

Each message is attempted based on the following schedule, where each period is started following the failure of the preceding attempt:

Immediately

5 seconds

5 minutes

30 minutes

2 hours

5 hours

10 hours

10 hours (in addition to the previous)

If an endpoint is removed or disabled delivery attempts to the endpoint will be disabled as well.

For example, an attempt that fails three times before eventually succeeding will be delivered roughly 35 minutes and 5 seconds following the first attempt.

Note

  1. An event represents a single unique workflowID(or capability). Whenever a transaction is run for that workflowID, a webhook is sent to the endpoint which has subscribed to that event.
  2. Webhook invocation is async from our end. Workflow does not get blocked.