Vercel Stripe Webhook Not Working
Vercel deployments break Stripe webhooks in 2026 because Vercel's default body parser automatically parses JSON request bodies — which overwrites the raw body that Stripe's signature verification requires. This is the #1 cause of Stripe webhook failures on Vercel, and the fix is one line of code.
Why This Happens
- Configuration gaps between tools or services
- Missing integrations or manual workarounds that weren't designed to scale
- Changes in vendor behavior, pricing, or API that weren't communicated clearly
What To Check First
- Verify your current setup matches the vendor's latest documentation
- Look for recent changes — platform updates, new team members, configuration drift
- Check if the problem is consistent or intermittent (different root causes, different fixes)
When To Escalate
- The problem is costing you money or customers per week
- You've spent more than 2 hours on it without progress
- A vendor quoted you more than $500 and you're not sure if it's necessary
Frequently Asked Questions
Why is Stripe not sending webhooks to my Vercel function?
The most common causes: (1) Missing or wrong STRIPE_WEBHOOK_SECRET environment variable in Vercel — check Project Settings → Environment Variables for both Preview and Production. (2) Wrong endpoint URL registered in Stripe Dashboard — it must match your Vercel deployment URL exactly. (3) Vercel cold-start timeout — serverless functions that haven't run recently may take 10-15 seconds to spin up, sometimes exceeding Stripe's retry window.
How do I test Stripe webhooks on Vercel?
Use the Stripe CLI: run 'stripe listen --forward-to localhost:3000/api/webhooks' locally to test the handler logic before deploying. Once deployed to Vercel, use the 'Send test webhook' button in Stripe Dashboard → Webhooks to fire a test event and check Vercel's function logs in real time.
How do I verify my Stripe webhook signature in Vercel?
Use stripe.webhooks.constructEvent() with your raw request body and STRIPE_WEBHOOK_SECRET. A common Vercel mistake: body parsers (like Next.js's default) convert the raw body before your handler sees it, breaking signature verification. Disable body parsing for the webhook route: export const config = { api: { bodyParser: false } }.
What Stripe webhook events should I register for on Vercel?
Register only the events your function actually handles. For most SaaS: payment_intent.succeeded, customer.subscription.updated, customer.subscription.deleted, and invoice.payment_failed. Registering all events adds noise and increases your function's invocation count.
Dealing with this right now?
The fix: in your API route file (e.g., `pages/api/webhook.js` or `app/api/webhook/route.js`), export a config object that disables the default body parser: `export const config = { api: { bodyParser: false } };`. Then manually read the raw body from the request stream. For Next.js App Router, use `req.text()` to get the raw string. Without this, Stripe's `constructEvent()` will throw "No signatures found matching the expected signature" on every request, even with the correct webhook secret.