Skip to main content
Every webhook delivery carries an HMAC signature you must verify before trusting the payload. The SDK does the work in one call — you pass the raw body, the signature header, and your webhook secret; it returns a typed event or throws.

How verification works

The full pattern

The header format

Verification is constant-time. The SDK uses crypto.timingSafeEqual so an attacker cannot derive the secret by timing your responses.

Why the raw body

The signature is computed over the exact bytes Vibefollow sent. If you parse the body first and re-serialise — key order, whitespace, escaped slashes — the recomputed HMAC differs from the header. The SDK accepts both Buffer and string for the body argument; it will not accept a parsed object.
Framework-specific notes:
Mount express.raw({ type: '*/*' }) on the webhook route only — not globally, or every other route loses JSON parsing.
Call await request.text() and pass the string.
Read request.text() inside the route handler. Pin the route to runtime = 'nodejs'.
Use the addContentTypeParser API to capture the raw buffer.

Replay protection

The header includes a Unix-seconds timestamp t. The SDK rejects any delivery whose timestamp is more than ±5 minutes from local time, which neutralises replay attacks against stale captured payloads. Override the tolerance if your environment has known clock drift:
Don’t widen it past necessity. Five minutes is the Stripe-standard window for the same reason — it balances clock drift against replay risk.

Errors you should handle

The X-Vibefollow-Signature header wasn’t on the request. Return 401.
The header didn’t parse as t=…,v1=…. Return 401.
The t is more than ±5 minutes from now. Return 401.
The HMAC didn’t match. Either the secret is wrong, the body was modified in transit, or it’s a forgery. Return 401.
The (verified) body was not valid JSON. This shouldn’t happen — return 500 and investigate.
In all WebhookSignatureError cases, return 401. Do not retry on your side — Vibefollow’s retry will pick it up if the failure was transient.

Edge-runtime caveats

Webhook verification uses node:crypto for HMAC. On Cloudflare Workers, enable the nodejs_compat flag in wrangler.toml:
Pure-edge runtimes without Node compatibility cannot use the SDK’s verifier in v1. A Web Crypto fallback is planned for a future major release.

Testing locally

Send a synthetic delivery to your handler with curl and a hand-computed signature: