Each webhook Loop sends contains the X-Loop-Signature header with the value that should be checked to ensure the webhook came from Loop's system.

We generate the value using the SHA256 algorithm.

Here is an example in PHP and JavaScript:

$encoded = base64_encode(hash_hmac('sha256', $jsonPayload, '<SECRET>', true));
const assert = require('assert');
const crypto = require('crypto');
const express = require('express');
const app = express();

// get raw payload buffer first before other middleware can act on it
    type: '*/*',
    verify: function(req, _res, buf) {
        if (Buffer.isBuffer(buf)) {
            req.buffer = buf;
app.use(express.json()) // for parsing application/json
app.post('/endpoint', (req, res) => {
  const hmac = crypto.createHmac('sha256', '<your secret>') // substitute secret
        .update(req.buffer, 'utf8')
  const signature = req.get('X-Loop-Signature');
  assert.equal(hmac, signature); // validate match
  res.json({ signature });
app.listen(3000, () => console.log('ready'));