secret. Verify the signature before trusting the payload. Without verification, anyone who guesses your endpoint URL can forge events.
The signing scheme
Reader computes:Node.js / Express
Useexpress.raw() for the webhook route. You must have the unparsed bytes to hash.
Python / FastAPI
Next.js (App Router)
Common pitfalls
- Parsing the JSON before hashing. If you
JSON.parsethen re-stringify, the bytes won’t match what Reader signed. Always use the raw body. - Missing
express.raw(). Express’s default JSON middleware parses the body and loses the original bytes. The webhook route needs a separate middleware. - String comparison instead of constant-time compare. A
===comparison leaks timing information. Usecrypto.timingSafeEqual(Node) orhmac.compare_digest(Python). - No replay check. A captured webhook can be replayed by an attacker unless you reject stale timestamps.

