Webhooks
Verify the signature
Every delivery carries Joomjoo-Signature with a timestamp and an HMAC-SHA256 of the timestamp, a dot, and the raw body, keyed with your endpoint's signing secret.
The header
Text
Joomjoo-Signature: t=1762182000,v1=5f1d2c...t is the unix time of the delivery in seconds. v1 is HMAC_SHA256(secret, t + "." + rawBody) as lowercase hex. The same value is also sent as Mooj-Signature.
Verify
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
return expected.length === parts.v1.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
msg = parts["t"].encode() + b"." + raw_body
expected = hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])- Compute over the raw bytes you received. Parsing and re-serialising the JSON changes the string and breaks the signature.
- Compare in constant time.
- Reject a
tmore than a few minutes old to shut out replays. - Answer 2xx only after the signature verified; anything else is retried.
Other headers
| Header | Value |
|---|---|
Joomjoo-Event-Type | The event type, the same as type in the body. |
Joomjoo-Event-Id | The event id, the same as id in the body. Use it to deduplicate retries. |
Mooj-Signature, Mooj-Event-Type, Mooj-Event-Id | The same three values under the older names. |