BetaThe V4 API is in beta — endpoints and functionality may change.

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).