Browse documentation
Security
Verify signatures
Every request includes an X-Hooks-Signature header. Verify it against the raw body using your endpoint’s signing secret before acting on the event.
The header contains a timestamp and one or more signatures: t=1739980800,v1=8f2c…. Compute HMAC-SHA256(secret, timestamp + "." + rawBody) and compare it in constant time.
const crypto = require("node:crypto");
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(",").map((part) => part.split("="))
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(parts.v1),
Buffer.from(expected)
);
}import hashlib
import hmac
def verify(raw_body, header, secret):
parts = dict(part.split("=", 1) for part in header.split(","))
signed = f"{parts['t']}.".encode() + raw_body
expected = hmac.new(
secret.encode(),
signed,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(parts["v1"], expected)Never compare signatures with ==. Use a constant-time function and reject requests whose timestamp is more than five minutes old to limit replay attacks.
Tell us if something was missing or unclear.