Everything worth knowing, nothing that isn't.
How the signature is constructed
A common construction builds the canonical string as `${timestamp}.${requestBody}`, computes `HMAC-SHA256(canonical, secret)`, hex-encodes it, and sends the digest and the timestamp in a signature header. Including the timestamp inside the signed payload prevents replay attacks — your endpoint should reject webhooks more than 5 minutes old. Header names and the exact canonical format differ between platforms, so confirm them with your provider before writing verification code.
Why HMAC and not RSA signatures
HMAC is symmetric — both sides share a secret — which means signing and verification are roughly 10,000x faster than RSA-2048. For webhook fan-out where a single SMSC may emit 50,000 DLRs/second, RSA signing dominates CPU. HMAC-SHA256 on modern hardware is single-digit microseconds. The trade-off: anyone with the secret can sign, so the secret should be shared only between the platform and your environment, stored like any other credential, and rotatable on demand.
Verification gotchas
Three common bugs. Comparing strings with `==` instead of a constant-time comparison (timing attack — use `crypto.timingSafeEqual`). Validating the signature after JSON-parsing the body (you must verify the raw bytes, not a re-serialized representation — JSON reordering breaks the hash). Forgetting the timestamp anti-replay window.
Beyond HMAC
For regulated environments (banks, healthcare), some platforms also support mTLS webhook delivery — the sender presents a client certificate that your load-balancer validates before the request reaches the application. This is stronger than HMAC because it binds delivery to a hardware key, but it only justifies its cost for genuinely sensitive workloads. For most integrations, HMAC-SHA256 over HTTPS is the right answer.