Verifying Webhook Signatures
Anyone who knows (or guesses) your callback URL can send a POST request that looks like a webhook. Verifying the signature is what actually proves a request came from Baiyar — never trust a webhook payload without it.
The signature header
Section titled “The signature header”Every request carries an X-Baiyar-Signature header:
X-Baiyar-Signature: t=1725267120,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdt— a Unix timestamp of when the request was signed.v1—HMAC-SHA256(webhook_secret, "<t>.<raw request body>"), hex-encoded.
Verifying a request
Section titled “Verifying a request”- Extract
tandv1from the header. - Recompute the HMAC over
"<t>.<raw body>"using your webhook secret (from Configure a Webhook). - Compare it to
v1using a constant-time comparison. - Reject the request if
tis more than 300 seconds from your current time, to guard against a captured payload being replayed later.
import crypto from "node:crypto";
function verifyWebhook(rawBody, signatureHeader, secret) { const [tPart, v1Part] = signatureHeader.split(","); const timestamp = Number(tPart.split("=")[1]); const signature = v1Part.split("=")[1];
if (Math.abs(Date.now() / 1000 - timestamp) > 300) { throw new Error("Signature timestamp too old"); }
const expected = crypto .createHmac("sha256", secret) .update(`${timestamp}.${rawBody}`) .digest("hex");
const isValid = expected.length === signature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
if (!isValid) { throw new Error("Invalid webhook signature"); }}import hashlibimport hmacimport time
def verify_webhook(raw_body: bytes, signature_header: str, secret: str) -> None: parts = dict(part.split("=", 1) for part in signature_header.split(",")) timestamp = int(parts["t"]) signature = parts["v1"]
if abs(time.time() - timestamp) > 300: raise ValueError("Signature timestamp too old")
expected = hmac.new( secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256 ).hexdigest()
if not hmac.compare_digest(expected, signature): raise ValueError("Invalid webhook signature")Next steps
Section titled “Next steps”Handling Webhook Deliveries — what to do once a request is verified: responding correctly, retries, and idempotency.