Skip to content

Verifying Signatures

Every webhook delivery is signed with HMAC-SHA256 using your endpoint’s secret. Always verify the signature before processing the payload.

Signature Header

The signature is sent in the X-XQR-Signature header:

X-XQR-Signature: sha256=5d2a7b8c4e...

Verification Algorithm

  1. Read the raw request body as bytes (do not parse JSON first)
  2. Compute HMAC-SHA256(secret, raw_body) using your webhook secret
  3. Compare the hex digest with the value after sha256= in the header
  4. Use constant-time comparison to prevent timing attacks

Code Examples

import crypto from "node:crypto";
function verifySignature(secret, payload, signature) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
const sig = signature.replace("sha256=", "");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(sig, "hex")
);
}
// Express middleware
app.post("/webhooks/xqr", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-xqr-signature"];
if (!verifySignature(WEBHOOK_SECRET, req.body, signature)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body);
console.log(`Received ${event.event}`, event.data);
res.status(200).send("OK");
});

Retrieving Your Secret

Your webhook signing secret is generated when you create the endpoint. Retrieve it via:

  • Dashboard: Developers → Webhooks → click the endpoint → Show Secret
  • API: GET /api/developer/webhooks/{id}/secret (JWT-authenticated)

Was this page helpful?