verihook
| Install | |
|---|---|
npm install verihook |
|
| Latest Version: | 1.21.2 |
| License: | MIT |
| Last Updated: | Oct 19, 2026 |
| Links: | GitHub · npm |
verihook 🪝
Universal, typed webhook signature verifier for TypeScript and JavaScript.
Documentation · Providers · Frameworks · Troubleshooting · Changelog
verihook verifies webhook signatures from Stripe, GitHub, Shopify, Slack, Twilio, Clerk, Resend, Paddle, Razorpay and 30+ other providers with one typed API, so you don't copy a different HMAC snippet for every service or install ten SDKs.
- Zero dependencies. Web Crypto, so it runs on Node.js 18.17+, Deno, Bun, Cloudflare Workers and Vercel Edge.
- One-line adapters for Next.js, Express, Fastify, Hono, NestJS, Nuxt/h3, SvelteKit, Remix/React Router, Astro and AWS Lambda. They keep the raw body for you.
- Says what went wrong. Failed checks return an error code and a hint: a parsed body, an API key instead of the signing secret, a proxy rewriting the URL.
- Typed events.
result.eventis the parsed payload, typed per provider;result.eventTypeis its name. - Testing helpers.
verihook/testingsigns requests for every provider;npx verihook simulatesends them to your local server. - Hardened. Timing-safe comparison, replay windows, deduplication keyed only on signed data, body size limits. Conformance-tested against the official Stripe, GitHub, Svix and Twilio SDKs, and fuzz-tested.
- Small. One provider from
verihook/stripeis ~4 kB min+brotli; all of them ~10 kB.
Install
npm install verihook # or pnpm add / yarn add / bun add
deno add jsr:@verihook/verihook
Quick start
import { verifyWebhook } from 'verihook';
export async function POST(request: Request) {
const result = await verifyWebhook('stripe', request, process.env.STRIPE_WEBHOOK_SECRET!);
if (!result.valid) {
console.warn(result.code, result.reason, result.hint);
return new Response('Invalid signature', { status: 401 });
}
console.log(result.eventType, result.event); // "invoice.paid", typed as StripeEvent
return Response.json({ received: true });
}
request is a Fetch Request or { headers, body, url? } with the raw body. Signatures cover the exact bytes sent, so verify before any JSON parser touches the body (why).
Frameworks
// Next.js App Router: app/api/webhooks/github/route.ts
import { createWebhookHandler } from 'verihook/next';
export const POST = createWebhookHandler('github', process.env.GITHUB_WEBHOOK_SECRET!, async (payload, result) => {
// runs only for a valid signature
});
// Express: register before express.json()
import { verihookExpress } from 'verihook/express';
app.post('/webhooks/stripe', verihookExpress('stripe', process.env.STRIPE_WEBHOOK_SECRET!), (req, res) => {
res.json({ received: true });
});
| Framework | Import | Guide |
|---|---|---|
| Next.js | verihook/next |
docs |
| Express | verihook/express |
docs |
| Fastify | verihook/fastify |
docs |
| Hono / Workers / Bun / Deno | verihook/hono |
docs |
| NestJS | verihook/nestjs |
docs |
| Nuxt / Nitro / h3 | verihook/h3 |
docs |
| SvelteKit | verihook/sveltekit |
docs |
| Remix / React Router | verihook/remix |
docs |
| Astro | verihook/astro |
docs |
| AWS Lambda | verihook/lambda |
docs |
Every adapter answers 401 to a bad signature, 413 to an oversized body and 200 to a duplicate event.
Providers
Each page says where to find the secret, which headers are sent and how to verify in every framework.
Stripe stripe |
GitHub github |
Shopify shopify |
Slack slack |
Twilio twilio |
Svix svix |
Resend resend |
Clerk clerk |
Meta / WhatsApp meta |
Discord discord |
X (Twitter) twitter |
PayPal paypal |
Lemon Squeezy lemonsqueezy |
Paddle paddle |
PagerDuty pagerduty |
Webflow webflow |
WorkOS workos |
Linear linear |
Razorpay razorpay |
Square square |
Zoom zoom |
Cashfree cashfree |
PhonePe phonepe |
Mollie mollie |
Adyen adyen |
Checkout.com checkout |
Authorize.net authorizenet |
Recurly recurly |
GitLab gitlab |
Bitbucket bitbucket |
Vercel vercel |
Sentry sentry |
Twitch EventSub twitch |
Telegram telegram |
Postmark postmark |
SendGrid sendgrid |
Mailgun mailgun |
HubSpot hubspot |
Intercom intercom |
Calendly calendly |
Typeform typeform |
Standard Webhooks standard-webhooks |
Anything else: the generic HMAC verifier or your own provider.
Testing your handlers
import { signWebhook } from 'verihook/testing';
const hook = await signWebhook('stripe', {
secret: 'whsec_test',
payload: { id: 'evt_1', type: 'payment_intent.succeeded' },
});
await request(app).post('/webhooks/stripe').set(hook.headers).send(hook.body).expect(200);
npx verihook simulate stripe --url http://localhost:3000/webhooks/stripe
See Testing and the CLI, including the listen relay.
More
- Deduplication and replays: reject retried and replayed deliveries, in memory or in Redis/KV.
- Typed events and options: strict mode, telemetry, tolerances.
- Edge runtimes and bundle size: per-provider imports.
- Architecture and Security policy.
Contributing
Contributions are welcome, especially new providers. CONTRIBUTING.md covers setup, the provider template and the PR checklist. npm run test:all runs everything CI runs.
License
MIT © Piyush Anand
Related Packages
Nabız Hub için Node.js raporlayıcı — sunucu hataları, SSR hataları, yavaş istekl...
WatchForge JavaScript SDK: automatic error, log and performance capture for brow...
Version History
| Version | Released | License |
|---|---|---|
| 1.21.2 | MIT | |
| 1.21.1 | MIT | |
| 1.21.0 | MIT | |
| 1.20.0 | MIT | |
| 1.19.0 | MIT | |
| 1.18.0 | MIT | |
| 1.17.0 | MIT | |
| 1.16.0 | MIT | |
| 1.15.0 | MIT | |
| 1.14.2 | MIT | |
| 1.14.1 | MIT | |
| 1.14.0 | MIT | |
| 1.13.0 | MIT | |
| 1.12.0 | MIT | |
| 1.11.0 | MIT | |
| 1.10.0 | MIT | |
| 1.9.0 | MIT | |
| 1.8.0 | MIT | |
| 1.7.3 | MIT | |
| 1.7.2 | MIT | |
| 1.7.1 | MIT | |
| 1.7.0 | MIT | |
| 1.6.0 | MIT | |
| 1.5.1 | MIT | |
| 1.5.0 | MIT | |
| 1.4.0 | MIT | |
| 1.3.1 | MIT | |
| 1.3.0 | MIT | |
| 1.2.1 | MIT | |
| 1.2.0 | MIT |
Showing the latest 30 of 33. See every release on npm