Webhook signatures
Verify that webhook deliveries are from Blooio with HMAC-SHA256.
Every webhook delivery includes an X-Blooio-Signature header. Verify it before trusting the payload.
Header format
X-Blooio-Signature: t=1735324800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd| Part | Meaning |
|---|---|
t |
Unix timestamp (seconds) when Blooio sent the delivery |
v1 |
HMAC-SHA256 hex digest of {timestamp}.{raw_body} |
Signing secret
Creating a webhook returns a signing_secret once. Store it securely. Rotate with POST /webhooks/{webhookId}/secret/rotate if needed — rotation is immediate.
Verify (Node.js)
import crypto from "node:crypto";
function verifyBlooioSignature(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=")),
);
const timestamp = parts.t;
const expected = parts.v1;
const signed = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const a = Buffer.from(signed, "utf8");
const b = Buffer.from(expected || "", "utf8");
if (a.length !== b.length) return false;
if (!crypto.timingSafeEqual(a, b)) return false;
// Reject stale deliveries (e.g. older than 5 minutes)
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
return age < 300;
}Use the raw request body string (not a re-serialized JSON object).