With every webhook request, Spacebring includes headers that you can use to verify the request.

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).

Webhook signing secrets are used to validate the payload data sent to your application from Spacebring. You can find the signing secret in your webhooks management page.

There are two ways to verify webhook requests:

1. Using the Svix library

First, install the Svix library for your language.

Then, use the following code to verify the request (example in JavaScript):

import { Webhook } from "svix";

const secret = "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw";

// These were all sent from the server
const headers = {
  "svix-id": "msg_p5jXN8AQM9LWM0D4loKWxJek",
  "svix-timestamp": "1614265330",
  "svix-signature": "v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=",
};
const payload = '{"test": 2432232314}';

const wh = new Webhook(secret);
// Throws on error, returns the verified content on success
const payload = wh.verify(payload, headers);

The payload is the raw (string) body of the request, and the headers are the headers passed in the request.

2. Manually verifying the signature

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:

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

Determining the expected signature

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

So to calculate the expected signature, you should HMAC the signed_content 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.

For example, this is how you can calculate the signature in Node.js:

const crypto = require('crypto');

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

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

console.log(signature);

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

The svix-signature header is composed of a list of space delimited signatures and their corresponding version identifiers. The signature list is most commonly of length one. Though there could be any number of signatures.