Engineering6 min read13 April 2026

Don't Write a Stripe Webhook Script — Use This Instead

Building your own Stripe cancellation feedback system seems like a weekend project. Here's what it actually involves — and why there's a faster way.

At some point, every technical SaaS founder has the same thought. Customers are churning. You want to know why. Stripe doesn't tell you. So you open a new file, pull up the Stripe docs, and start sketching out a webhook handler.

It seems simple. Listen for customer.subscription.deleted, fire off an email asking why they left, log the response somewhere. A weekend project, maybe less. You've built harder things than this.

Three weeks later, you're still working on it — or you've shipped something half-finished that you're quietly embarrassed about. This article is about why that happens, and what to do instead.

What you think you're building

The mental model most founders start with looks something like this:

// Step 1: catch the webhook app.post('/webhooks/stripe', (req, res) => { const event = stripe.webhooks.constructEvent(...) if (event.type === 'customer.subscription.deleted') { sendSurveyEmail(event.data.object.customer) } res.json({ received: true }) }) // Step 2: send an email // Step 3: log the response // Done

That's the happy path. It takes maybe two hours to get working in a dev environment. And then you start thinking about production.

What you're actually building

Here's the full list of things a production-grade cancellation feedback system actually requires. Not nice-to-haves — things you'll genuinely need before you can trust the data.

Webhook signature verification. Stripe signs every webhook payload. If you're not verifying the signature, any request to your endpoint will trigger a survey email. You'll be surveying people who never cancelled and missing real cancellations that came in through a different path.

Idempotency handling. Stripe retries webhook delivery if your endpoint doesn't respond with a 200 within a few seconds. If your email sending or database write is slow, you'll receive the same event multiple times. Without idempotency logic, the same customer gets multiple survey emails. That's a bad experience and bad data.

Customer email lookup. The customer.subscription.deleted event gives you a customer ID, not an email address. You need to make a separate API call to Stripe to get the customer's email before you can send anything. Simple — but another thing to build and another failure mode to handle.

Tokenised survey links. Your survey email needs to contain a link that identifies which customer is responding. That means generating a unique token per cancellation, storing it somewhere, and validating it when the customer clicks. If you skip this, anyone can submit a survey response for anyone else's cancellation.

A survey page. The link in the email has to go somewhere. That means building a page — ideally one-click, with pre-defined reason options — that records the response and shows a confirmation. If you make it a form, response rates drop sharply. If you make it one-click, you need to handle the response on page load, which has its own edge cases around email prefetching.

Email prefetch protection. Gmail, Outlook, and most email clients prefetch links in emails to check for malware. If your survey response is recorded on page load — which is the obvious implementation — every survey will appear to be "responded to" before the customer ever opens the email. You need to separate the page render from the data write, which means a client-side POST on explicit interaction, not a server-side write on navigation.

A database schema. You need to store cancellations, tokens, reasons, freetext responses, timestamps. That's a migrations file, a schema design decision, RLS policies if you're on Supabase, and ongoing maintenance as the schema evolves.

Multi-account support. Unless you're building this purely for your own product, you'll eventually want to offer it to customers. That means the webhook handler needs to identify which account the cancellation belongs to — which means storing webhook secrets per account, matching incoming webhooks to accounts, and handling the case where a secret has been rotated.

A dashboard. Raw data in a database table isn't useful. You need to visualise it — at minimum a breakdown of cancellation reasons by percentage, MRR lost over time, and a response rate metric. That's a charting library, data aggregation queries, and a frontend to display it.

Email deliverability. Survey emails sent from a generic domain or a shared IP will land in spam. You need a sending domain, SPF and DKIM records configured correctly, and ideally a dedicated sending service. If you're using SMTP directly, you need to handle authentication, TLS, and connection pooling.

The honest time estimate

If you're an experienced developer working on your own stack, here's a realistic breakdown:

Webhook handler with signature verification and idempotency: 3–4 hours. Customer email lookup and tokenised survey link generation: 2 hours. Survey page with one-click response and prefetch protection: 4–6 hours. Database schema and migrations: 2 hours. Basic dashboard with charts: 6–8 hours. Email deliverability setup: 2–3 hours. Testing, edge cases, and production hardening: 4–6 hours.

That's 23–31 hours of engineering time before you have something you'd trust in production. At a conservative £100/hour opportunity cost, that's £2,300–£3,100 of founder time — for a system that still needs ongoing maintenance every time Stripe changes something, your email provider updates their API, or you want to add a feature.

What you should actually do instead

The decision to build vs buy is usually framed as a cost question. It shouldn't be. The real question is whether building this is the highest-value use of your engineering time right now.

For most SaaS founders, the answer is clearly no. The churn data you're trying to collect is only valuable if you act on it — and you can't act on it while you're still building the collection system. Every day you spend on the webhook script is a day you don't have the data that would tell you what to fix.

Dropcause handles every part of this — webhook verification, tokenised survey links, prefetch-safe response recording, email deliverability, and a dashboard with reason breakdown, MRR attribution, and response rate — out of the box. It connects to Stripe in minutes via OAuth or a webhook secret paste, and it starts collecting data immediately.

The script you were about to write this weekend would take 25 hours to build properly. Dropcause takes ten minutes to set up. The data it produces is identical. The time difference is not.

When you should build it yourself

There are legitimate reasons to build your own cancellation feedback system. If you have highly specific requirements that no tool supports. If you're operating at a scale where the per-account economics justify the engineering investment. If data residency requirements mean you can't use third-party services. If this is genuinely core to your product rather than infrastructure that supports it.

For the vast majority of bootstrapped SaaS founders at under £50k MRR, none of these apply. The script is a distraction from the work that actually moves the business forward.

The bottom line

The webhook script feels like the pragmatic choice. You're technical, you can build it, and building feels like progress. But a half-finished feedback system that took three weeks to build and still has edge cases you haven't handled isn't progress — it's expensive procrastination.

The data you need to reduce churn is available right now. The only question is how quickly you want it.

Stop guessing why customers cancel.

Dropcause automatically sends exit surveys when a Stripe subscription cancels — so you always know why.

Start free trial →

Related reading

Churn · 8 min read
Why Customers Cancel SaaS Subscriptions (And What To Do About It)
Churn · 5 min read
Stripe Doesn't Tell You Why Customers Cancel — Here's How to Fix That
Churn · 6 min read
How to Reduce SaaS Churn (Even If You Don't Know Why Users Leave)