Verify Webhooks
Verify webhook signatures with HMAC-SHA256
Using the SDK
The SDK’s validateWebhook() verifies the HMAC signature, checks timestamp freshness, and returns a typed event object:
The SDK uses the Web Crypto API internally, so validateWebhook() works in Node.js, Deno, Bun, and edge runtimes.
Signature format
Every webhook includes an X-Gwop-Signature header:
t— Unix timestamp when the webhook was sentv1— HMAC-SHA256 of{timestamp}.{raw_body}using your webhook secret
Manual verification
If you’re not using the SDK, verify signatures manually:
Key points
- Timing-safe comparison — Always use
timingSafeEqual(or the SDK), never===for HMAC comparison - Timestamp tolerance — Reject webhooks older than 5 minutes (300 seconds) to prevent replay attacks
- Deduplicate by
X-Gwop-Event-Id— The same event may be delivered multiple times; store processed event IDs - Return 200 quickly — Process asynchronously if needed; Gwop retries on non-2xx responses
You must use the raw body (req.body.toString() or express.raw()) for HMAC verification. If you parse the body as JSON first and re-stringify it, the signature won’t match due to whitespace or key ordering differences.