Skip to main content
Every webhook delivery includes an X-Notealy-Signature header so your receiver can confirm Notealy sent it and that the payload was not tampered with in transit.

Header format

  • t — unix timestamp (seconds) at signing time
  • v1 — hex-encoded HMAC-SHA256 of "{t}.{rawBody}", keyed by your subscription’s signing secret
The secret is shown once when you create or rotate the subscription. Store it as a server-side secret.

Verifying with @notealy/sdk

The TypeScript SDK ships the verifier as a tree-shakeable subpath, so you don’t have to roll the HMAC math yourself:
verifyWebhook throws WebhookVerificationError (with a discriminating code) on any mismatch — never returns a falsy value, so use a try/catch. The same function enforces a 5-minute timestamp tolerance by default; override with options.toleranceSec. See SDKs for setup.

Verifying manually (Node.js)

A few practical notes:
  • Use the raw body, not a re-serialized one. Express middlewares like express.json() consume the buffer; capture it with a verify callback or use body-parser raw mode for the webhook route.
  • Compare in constant time with timingSafeEqual (or your language’s equivalent) to avoid timing attacks.
  • Reject stale signatures — we recommend a 5-minute tolerance window. Replay attempts older than that should be rejected even if the HMAC matches.
  • Rotate periodically. Use Rotate signing secret in the dashboard; the new secret is shown once and old signatures stop validating immediately.