Signature Verification
Every webhook delivery is signed with HMAC-SHA256 using your webhook secret. Always verify the signature before processing the payload.
How It Works
When Merx sends a webhook, it computes an HMAC-SHA256 hash of the raw request body using your webhook secret as the key. The resulting hex digest is sent in the X-Merx-Signature header with the format sha256=<hex_digest>.
Verification Steps
- Extract the signature from the X-Merx-Signature header
- Remove the
sha256=prefix to get the hex digest - Compute HMAC-SHA256 of the raw request body using your webhook secret
- Compare the computed digest with the received digest using a timing-safe comparison
- Reject the request if the signatures do not match
USE RAW BODY
You must use the raw, unparsed request body for signature verification. Parsing and re-serializing the JSON may change whitespace or key order, producing a different hash.
Code Examples
Signature verification
const crypto = require('crypto')
function verifyWebhook(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
const received = signatureHeader.replace('sha256=', '')
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(received, 'hex'),
)
}
// Express middleware example
app.post('/webhooks/merx', express.raw({ type: '*/*' }), (req, res) => {
const signature = req.headers['x-merx-signature']
const secret = process.env.MERX_WEBHOOK_SECRET
if (!verifyWebhook(req.body, signature, secret)) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.body)
console.log(event.event, event.data)
// Return 200 immediately, process asynchronously
res.status(200).send('OK')
})Testing Locally
To test webhook verification locally, compute a signature manually and include it in a test request.
Test with curl
SECRET="your_webhook_secret"
BODY='{"event":"order.filled","data":{"id":"ord_test"},"timestamp":1711200000}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)
curl -X POST http://localhost:3000/webhooks/merx \
-H "Content-Type: application/json" \
-H "X-Merx-Signature: sha256=$SIG" \
-d "$BODY"TIP
Use a tool like ngrok to expose your local server and register the tunnel URL as your webhook endpoint during development.