Skip to main content
When your webhook has a secret configured, every delivery includes an X-Way-Signature header:
It is the HMAC-SHA256 of the request body, keyed with your webhook secret, base64-encoded. Recompute it and compare to prove the request came from Way and wasn’t tampered with.
Compute the HMAC over the raw request body bytes - not a re-serialized copy. Parsing the JSON and re-stringifying it (JSON.stringify(req.body)) can produce different bytes than what Way signed (number formatting, key handling, and unicode escaping vary by language and parser), which makes verification fail intermittently or - worse - pass only by coincidence. Capture the body before your framework parses it.

Node.js (Express)

Python (Flask)

Pitfalls

  • Body re-serialization - the most common failure. Frameworks that eagerly parse JSON (Express’s express.json(), some serverless runtimes) must be configured to expose the raw body for the webhook route.
  • Proxies that mutate the body - anything that re-encodes or pretty-prints the JSON between Way and your handler breaks the signature. Verify at the first hop that sees the raw bytes.
  • Missing header - deliveries carry X-Way-Signature only when the webhook has a secret configured. If you expect it and it’s absent, treat the request as unauthenticated.
  • String comparison - use a timing-safe comparison (crypto.timingSafeEqual, hmac.compare_digest) rather than ===.