Authenticating Webhooks

To ensure the security and integrity of webhooks sent by the Send platform, each request includes specific headers that can be used to verify its authenticity.

X-Send-Signature

This header contains a signature generated by signing the timestamp (X-Send-Request-Timestamp header) and the message body using an asymmetric SHA256 private key. This signature can be used to ensure that the webhook originated from the Send platform and has not been altered.

Verifying the Signature
To verify the signature, follow these steps:

  1. Stringify the webhook body.
  2. Prefix the stringified body with the value from the X-Send-Request-Timestamp header.
  3. Use the provided public key to verify the signature.

An example of this implemented in Node.js is as follows:

const fs = require('fs');
const crypto = require('crypto');

const calculateStringToVerify = (webhookBody, timestampHeader) => {
  const bodyString = JSON.stringify(webhookBody);
  return `${timestampHeader}${bodyString}`;
}

const verifySignature = async (webhookBody, timestampHeader, signatureHeader) => {
  // Create the string to verify
  const stringToVerify = calculateStringToVerify(webhookBody, timestampHeader);

  // Retrieve the public key
  const publicKey = fs.readFileSync('./public-key.pem', 'utf8');

  // Set up the verifier
  const verifier = crypto.createVerify('RSA-SHA256');
  verifier.update(stringToVerify);
  verifier.end();

  // Verify the signature
  const isVerified = verifier.verify(publicKey, signatureHeader, 'base64');
  return isVerified;
}

If you intend to implement signature verification for incoming webhooks, please contact the Send team to obtain the current public key.

X-Send-Request-Timestamp

This header contains an ISO 8601 timestamp (UTC), indicating when the webhook was sent. To mitigate the risk of replay attacks, we recommend rejecting any webhook messages older than a threshold of your choosing.

This timestamp is also used in the signature calculation process described above.