Webhook signature verification for Node.js
doorbell-js HMAC the raw bytes Stripe, GitHub, Slack, Shopify, Clerk, and Svix actually signed. Not JSON.parse. Not JSON.stringify.
npm i doorbell-js
The Next.js App Router export is the route. Do not call req.json().
import { doorbell } from 'doorbell-js'
export const POST = doorbell({
stripe: {
secret: process.env.STRIPE_WEBHOOK_SECRET,
on: {
'checkout.session.completed': async (event) => {
await fulfill(event.payload)
},
},
},
})
Fix the error you pasted into Google
- No signatures found matching the expected signature for payload
- express.json() ate the Stripe or GitHub body
- Slack invalid_signature, Shopify HMAC, Clerk / Svix, Twilio URL
What it checks
HMAC first, then the clock, the way stripe-node does. Missing headers still burn HMAC so that path is not faster. GitHub and Shopify do not get event.type from unsigned headers. Compare digest bytes, not hex strings. Express does not trust X-Forwarded-Host for the Twilio URL. http is only localhost. Transfer-Encoding: chunked and a mismatched Content-Length are refused. Replies are Cache-Control: no-store.
Providers: Stripe, GitHub, Slack, Shopify, Svix, Clerk, Resend, Linear, Paddle, Meta, Twilio.
MIT. Source and README.