Verification
Each delivery is signed with the endpoint’s secret using HMAC-SHA256. The
signature is the lowercase hex digest of JSON.stringify(payload) — the exact
JSON body sent.
Verify
Section titled “Verify”import crypto from "node:crypto";
app.post("/bizzle/webhook", express.raw({ type: "application/json" }), (req, res) => { const signature = req.get("X-Webhook-Signature");
const expected = crypto .createHmac("sha256", process.env.BIZZLE_WEBHOOK_SECRET) .update(req.body) // the RAW body bytes, exactly as received .digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) { return res.status(400).end(); }
const event = JSON.parse(req.body); // … res.status(200).end();});Important: sign/verify the raw body, not a re-serialized object. Any difference in key order or whitespace changes the digest.
Replay protection
Section titled “Replay protection”Bizzle does not send a timestamp or enforce a freshness window. To protect against duplicate or replayed deliveries:
- Always verify the signature.
- Deduplicate by
X-Webhook-Id— the event id is stable across retries. - Return
2xxquickly and process asynchronously.
Secret format
Section titled “Secret format”Secrets are whsec_ followed by 32 hex characters. Rotate at any time with
POST /v1/webhooks/:id/rotate-secret and update your server immediately.