Delivery headers
Webhook deliveries include:
X-Lamba-EventX-Lamba-TenantX-Lamba-TimestampX-Lamba-Signature
Two signature formats may appear:
- current format: uppercase hex of
HMAC_SHA256(secret, "{timestamp}:{rawBody}") - v1 format:
v1=<lowercase hex>where input is"{timestamp}.{rawBody}"
Verification algorithm
- Read raw request body exactly as received.
- Read timestamp and signature headers.
- Recompute expected signature with your webhook secret.
- Compare signatures using constant-time comparison.
- Reject timestamps outside your replay window.
Node.js example
import crypto from "node:crypto";
export function verifySignature(input: {
secret: string;
timestamp: string;
rawBody: string;
providedSignature: string;
}) {
const base = `${input.timestamp}:${input.rawBody}`;
const expected = crypto
.createHmac("sha256", input.secret)
.update(base, "utf8")
.digest("hex")
.toUpperCase();
return crypto.timingSafeEqual(
Buffer.from(expected, "utf8"),
Buffer.from(input.providedSignature, "utf8")
);
}
Replay protection
- Set a max clock skew (for example 300 seconds).
- Reject old timestamps.
- Keep idempotency keys for processed delivery IDs.