Handling Webhook Timeouts: Stripe Events and Background Queues

TL;DR: Processing complex Stripe webhooks synchronously on Vercel Serverless Functions often leads to 504 Timeout errors, causing Stripe to endlessly retry and disable your endpoint. The solution is to verify the webhook, immediately return a 200 OK, and push the actual processing logic into a background message queue like Upstash QStash.
The Webhook Timeout Problem
When a user subscribes to your SaaS, Stripe fires an invoice.paid webhook to your server. If your server is hosted on Vercel (which enforces a strict 10-second timeout on standard serverless functions), and your provisioning logic takes 12 seconds, Vercel kills the process. Stripe receives a 504 Timeout and assumes the webhook failed.
Why It Matters
When Stripe thinks a webhook failed, it implements exponential backoff and retries. If the event keeps timing out, Stripe will eventually disable your webhook endpoint entirely. Suddenly, users are paying you money, but your application never upgrades their accounts.
How It Works
Synchronous vs Asynchronous Processing
In a synchronous architecture, the request is held open while all the business logic executes. In an asynchronous, event-driven architecture, the web server's only job is to acknowledge receipt of the message and close the connection immediately.
The Queue Architecture
- Stripe POSTs to your
/api/webhooks/stripeendpoint. - Your endpoint validates the Stripe Signature using your webhook secret.
- Your endpoint takes the raw JSON payload and forwards it to a message queue (like Upstash QStash).
- Your endpoint immediately returns a
200 OKto Stripe. Total time: 150ms. - The message queue independently triggers a background worker endpoint to actually process the subscription logic.
Practical Steps for Implementation
- Setup Upstash QStash:QStash is perfect for serverless environments because it uses HTTP to trigger workers. You don't need a persistent Redis connection.
- Write a Fast Receiver: Keep your main webhook route incredibly lightweight. Validate the signature, push to QStash, and exit.
- Build Idempotent Workers: Since background queues guarantee “at-least-once” delivery, your worker might receive the same Stripe event twice. Check the database to see if the
stripe_event_idhas already been processed before upgrading the account.
Common Mistakes
A common mistake is trying to bypass timeouts by using NextResponse.json() early, but leaving the async work running in the background of the serverless function. On Vercel, the moment a response is returned, the microVM is frozen. Any background promises are paused or killed.
FAQ
What is Upstash QStash?
QStash is a serverless messaging and scheduling solution. It acts as a middleman, receiving messages and forwarding them to your API endpoints with built-in retries and delays.
What does it mean for a worker to be idempotent?
Idempotency means that if a function is executed multiple times with the same input, the final state of the system is exactly the same as if it were executed only once. For payments, it prevents double-upgrading an account.
Conclusion
Decoupling the receipt of a webhook from the processing of its payload is a critical milestone in scaling a SaaS application. By introducing a message queue, you eliminate 504 errors and build a resilient billing architecture that won't drop events under heavy load.
Stop flying blind on AI costs
Frugal tracks every dollar across OpenAI, Anthropic, and more — with budget alerts before costs spiral.
Start free →