Checking webhook signatures

Anyone can send a request to your webhook URL. The signature header proves a call came from Wamafy and was not changed on the way.

How it works

Each call carries a header like this:

X-Wamafy-Signature: sha256=4c2a9f0e1b7d3a6c8e5f2b9d0a4c7e1f3b6d9a2c5e8f1b4d7a0c3e6f9b2d5a8c

The part after sha256= is an HMAC-SHA256 of the raw request body, in hex, keyed with your signing secret. Work out the same value and compare. If they differ, reject the call.

Use the body exactly as it arrived. Parsing the JSON and turning it back into text changes spacing and key order, and the signature will not match.

Examples

Each is a complete program: set WAMAFY_WEBHOOK_SECRET, run it, and point your webhook at /wamafy/webhook on that server. It reads the secret from the environment and compares in constant time.

// Node.js 18+. npm install express, save as receiver.mjs and run: node receiver.mjs
import crypto from 'node:crypto';
import express from 'express';

const SECRET = process.env.WAMAFY_WEBHOOK_SECRET;
const app = express();

// Keep the raw body: the signature is over the exact bytes we sent.
app.post('/wamafy/webhook', express.raw({ type: '*/*' }), (req, res) => {
  const raw = Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0);
  const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(raw).digest('hex');
  const given = req.get('X-Wamafy-Signature') ?? '';

  const ok = given.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
  if (!ok) return res.status(401).end();

  const event = JSON.parse(raw.toString('utf8'));
  res.status(200).end(); // answer first, then do the work
  handle(event);
});

function handle(event) {
  // Your code: create a ticket, update an order, alert someone.
  console.log('received', event.event);
}

app.listen(3000, () => console.log('listening on 3000'));

Which secret

The status webhook has one secret for the workspace. Each event webhook subscription has its own. Check every call with the secret of the registration it came through; a receiver holding the other one will reject everything.

    Checking webhook signatures - Wamafy API