Hey IH. I've been building CancelKit, a tiny widget that sits in front of your Stripe cancel flow: quick exit-survey + an instant save offer (discount or pause) before someone actually cancels.
Backstory: I kept losing subscribers I could've saved with a 20% discount for 2 months, but I had zero visibility into why they were leaving and no easy way to intervene. Built CancelKit to fix that for myself first.
I originally built a full billing layer on top of it. I ended up ripping it out entirely — putting cancel-reason data behind a paywall made no sense for a tool whose whole point is that almost nobody measures this. It's free now, no card, no paid tier, one line of script.
Happy to answer questions about the build (Stripe webhook ordering and idempotency were the genuinely hard parts).
We're live on Product Hunt today — would love your support: https://www.producthunt.com/products/cancelkit
Splitting “opened then closed, no reason” from a real offer decline is the right fix. The first is closer to a bounce than to churn.
That no-reason event will probably be the bigger number. Worth watching before you treat decline rate as the health metric.
Good point to come back to. If I do split it out, this is exactly the failure mode to watch for: "decline rate" as a single number quietly mixes two different populations — people who saw the offer and said no, and people who never engaged with it at all. A widget that's mostly getting opened and closed unread isn't a discount problem, so leaning on one blended number would point me at the wrong fix. Haven't shipped the split yet, volume's still too low to know which bucket actually dominates for CancelKit, but you and another commenter both landing on the same instrumentation gap is a decent signal it's worth doing before I trust decline rate for anything.
Making the widget free can create a valuable labeled dataset: stated cancel reason, offer shown, save outcome, and later retention. The business model could emerge above the widget through benchmarks, alerts, and policy recommendations for teams with enough volume. The free layer captures ground truth; the paid layer helps companies decide which intervention is worth using.
That's a sharp way to put it, and honestly close to how I've been thinking about it without having a name for it yet — "the free layer captures ground truth, the paid layer helps decide which intervention is worth it" is a cleaner split than anything I'd written down.
Right now I'm deliberately not building toward that though — the free/no-billing decision was as much about removing my own temptation to gate data behind a paywall as it was a growth tactic. If a benchmarks/policy layer ever makes sense, I'd want it to be because enough teams asked for it with a specific decision they couldn't make without it, not because I built it speculatively on a dataset that doesn't exist yet. Right now the honest answer is I don't have volume to benchmark anything against.
The event log breakdown in the comments is a good move. One thing worth sitting with: by the time someone actually opens your widget, you have already lost the cheapest intervention window. A lot of churn signals show up weeks earlier, a support ticket that never got closed, a feature they quietly stopped touching, a downgrade they never followed up on. CancelKit catches people at the exit door, which is real and useful. The harder problem is catching them before they start walking toward it. Not saying you should build that too, just curious where you see the boundary between a cancel widget and a full pre churn signal system.
That's the real tension, and I don't think CancelKit closes it — it's deliberately downstream. The exit-survey moment is the one point where the "why" is unambiguous and self-reported, versus a quiet feature drop-off or an unclosed ticket, which are inferred signals that could mean five different things. My honest boundary: CancelKit is the ground-truth layer, not the prediction layer. If someone wanted to build the pre-churn signal system you're describing, the exit-survey reasons are probably the best labels to train it against — you'd want to know which of those "weeks earlier" signals actually preceded a real cancel vs. a save. Right now nothing connects the two; that's a real gap.
Removing the billing layer is such a refreshing move. I’ve gone back and forth on paywalls for my own side projects, and the fact that you decided the data is worth more than the revenue is a good gut check.
The Stripe webhook order and idempotency part caught my attention too—that’s exactly where I’d expect things to get messy.
One question: for people who land on the widget but don’t select a reason, do you treat that as a separate event from a true abandoned cancel? Or does it all roll up into one bucket?
Good catch — right now they're not separated, and they should be. Today the event log has: widget_opened, reason_selected, offer_shown, offer_accepted/declined, cancel_completed. If someone opens the widget and closes it without picking a reason, that currently falls into the same "declined the offer" bucket as someone who saw a real reason and a real offer and still left. I hadn't thought about it as its own signal until your question — it probably deserves a separate no_reason_selected event, since that's arguably the highest-intent-to-leave group (didn't even engage enough to tell you why) and mixing it into "declined offer" understates how bad a no-reason exit really is.
everyone here is looking at this from the founder's side, so here's the view from the other one. i read public complaint threads all day for a research project, and cancel flows are their own genre. three from the past two weeks, three unrelated people:
one was told by a parking app's call centre that their T&Cs don't permit cancellation, but that if they bought a second subscription they could then request to cancel the first. they did it. another cancelled a design tool while holding 27,168 tokens they had already paid for; the balance locked the moment they cancelled, and they were told they could have it back by paying again. a third couldn't cancel in the app, got pushed to a web page to "manage subscriptions", couldn't cancel there either, and ended up asking the room whether they were just going to lose $9.99 a year forever.
not one of those people is in anybody's exit survey. that's the bit i'd think hardest about. people who feel trapped don't fill in the form, they go write a post with your company name in the title, and your own flow logs them as "started cancellation, no reason given" — the same row as someone whose wifi dropped.
so if i were instrumenting this i'd promote abandoned-cancel to a first-class event rather than leaving it as the residual bucket: entered the flow, declined the offer, never completed. that's the segment where the reputational damage actually lives, and right now it's invisible on both sides of the screen. the line between a save offer and a dark pattern is mostly a question of how many screens it takes to get past it, and whoever ships the widget is never the person counting them.
This is the most useful comment on this whole thread, thank you for it. Those three examples are exactly the failure mode CancelKit is trying to make visible — the parking app one especially, "buy a second subscription to cancel the first" is a save tactic dressed up as a policy, and it'd show up in nobody's metrics as churn prevented. You're right that "started cancellation, no reason given" is doing too much work as a bucket right now — it's covering both wifi-drop abandons and people who gave up because the flow itself was the obstacle. I think the fix is what coderlau also just pointed at in a different comment: split it into no_reason_selected (never engaged) vs abandoned_after_offer (saw the offer, declined, left anyway) as distinct events. The second one is the one that should worry a founder. Appreciate you bringing the user side into a thread that's been pretty founder-brained so far.
If “churn instrumentation layer” is the new product identity, I’d measure one step beyond whether the widget collects useful events:
How long does it take from the first cancellation data to a founder making a different decision because of it?
For the first active accounts, I’d track something like:
enough cancellations to reveal a pattern
the insight the founder noticed
the action they took — pricing, onboarding, offer eligibility, product change
whether that cohort behaved differently 30–60 days later
Before building a paid analytics layer, I’d probably do that analysis manually for the first 10 accounts with enough data.
If the same few insights repeatedly cause founders to change something, you’ve found the paid product.
If people happily collect cancellation reasons but rarely act on them, then CancelKit may be a useful free instrumentation tool without yet having a monetizable decision layer.
What is the first concrete decision you expect a founder to make after seeing their first 10–20 cancellation events?
Honestly don't have a real answer yet — we're one day post-launch, so I don't have 10-20 real cancellation events from a live customer to point to. But the way you've framed it is the right test, and I'll hold myself to it: if I get a founder to 10-20 real cancellations and the honest answer is "nothing changed, I just have a spreadsheet now," that's a real signal CancelKit is a data collector, not a decision tool, and I should say that plainly rather than imply otherwise. My instinct for the first concrete decision is something boring — probably "this specific plan tier has a 3x higher decline-and-leave rate than others, go look at that plan's onboarding" — not a sweeping strategy shift. Small, specific, actionable beats a big insight nobody acts on.
That sounds like the right level of decision to look for.
I’d much rather see CancelKit repeatedly cause small, specific actions than produce impressive-looking insights nobody uses.
Something like:
pattern observed → action taken → later behavior checked
would probably be enough for the first few customers.
Your example is a good one: if one plan has a much higher decline-and-leave rate, the useful outcome isn’t the chart itself — it’s that the founder actually investigates or changes that plan’s onboarding.
If those small decisions start repeating across customers, that’s probably a much stronger signal for the paid layer than adding more analytics upfront.
This is an interesting approach. The part that stood out to me is removing the billing layer entirely , it makes the barrier to actually collecting churn data much lower.
I'm building in a somewhat adjacent space around understanding user behaviour, and I've been realizing that getting the data is only half the problem. The real value is whether the insight actually changes what you build.
That's the exact tension I'm watching for. Lowering the barrier to collecting churn data was the easy part — deleting the billing code took an afternoon. Whether it actually changes what founders build is unproven, I have zero live customers with enough volume to know yet. What's the adjacent space you're working in — are you running into the same "got the data, now what" wall from the other direction?
This is a smart wedge for adoption: free removes the eval friction, and once it's installed, CancelKit becomes infrastructure that's painful to rip out, that's the real moat, not the discount logic. As an investor the number I'd watch closely is time from install to first save-offer served, if that's not under a week you have a different problem than pricing. Curious whether the free version also captures why people didn't take the offer, that segment is what your paid analysis layer should be built around first.
Good metric, hadn't framed it that way but it's the right one — if install-to-first-offer-served takes more than a week, the problem isn't pricing, it's that almost nobody's actually cancelling, which is a different (probably better) problem to have. On your second question: yes, the widget already captures the decline itself as an event, but not a structured "why" beyond whatever reason they picked in the exit survey before seeing the offer. It doesn't currently ask "why didn't this discount change your mind." That's a gap — right now I'd only get that as a free-text signal if someone happened to leave a comment, not as structured data. Worth adding.
The decision to delete the billing code is the most interesting part of this post. Making the tool free solves the adoption problem that kills churn measurement products — founders won't instrument cancel flows they're not sure will work, and they won't pay for a tool they can't evaluate. Free removes both blockers at once.
The thing worth watching: when the save offer works (someone takes the 20% and stays), that's useful signal. But when they take it and churn three months later anyway, the data gets more interesting. Price-sensitivity churn and value-clarity churn look identical at the cancel screen but require completely different fixes downstream. If CancelKit can segment 'took offer, stayed' vs 'took offer, left anyway,' that's where the real insight lives.
The free model also sets up a future upsell that's actually coherent: charge for the analysis layer, not the widget. The widget earns the relationship. The analysis closes it.
You're right that the segmentation is the whole game — 'took offer, stayed' vs 'took offer, left anyway' looks identical in the exit survey but means completely different things for the founder. Right now CancelKit tracks the save-offer accept/decline event, but not the downstream churn-after-save outcome — that's the real gap. Appreciate the reframe on the future model too: analysis layer over widget is the right instinct, and it's honestly more defensible than what I was originally planning to charge for.
The real insight here is visibility collapse. Before CancelKit, you had zero measurements - you knew someone canceled but nothing about why or whether they could've been saved. That missing measurement system meant you couldn't even build the right product.
Making it free makes the measurement itself the product, not the intervention. The exit surveys and save offers are just the measurement interface. You're essentially saying: "You already have a churn problem. The first step isn't solving it - it's measuring what's actually happening."
Most SaaS founders pay for billing infrastructure and then can't see what it's preventing. You inverted it - the visibility of "why do customers leave" became so valuable that monetizing the data made the product worse. That's a founder measuring correctly.
That's a sharper way to put it than I had — 'the measurement itself is the product' is exactly what happened once I ripped the billing code out. I built it to solve my own visibility gap first, then realized the survey data was worth more than the discount ever was. Good push to think about this less as a churn tool and more as an instrumentation layer.
The important metric here is probably not the immediate save rate, but retained revenue 60–90 days later. A 20% offer can merely delay an inevitable cancellation, and showing it too broadly can teach users to enter the cancel flow for a discount. I’d add eligibility rules (tenure, plan, previous offers), a small holdout group, and reporting by offer: accepted, still active after the discount ends, and revenue retained net of the discount. That would separate genuine incremental retention from a temporarily improved cancellation number.
Completely agree on 60–90 day retained revenue over immediate save rate — accept rate alone is a vanity number if it's just delaying the inevitable. Right now there's no holdout group or eligibility rules (tenure/plan/previous-offer gating) — it's a blunt instrument, same offer to everyone who hits cancel. That's a good v2 direction, appreciate the concrete list.
The strongest signal here is that removing the billing layer changed what CancelKit actually is, not just how it’s priced.
That makes the decision to simplify the product more interesting than the free-vs-paid question itself.
Fair callout — I framed it as a pricing decision when I wrote the post, but you're right it's actually a product-identity decision. Once the billing layer was gone, CancelKit stopped being a 'discount tool you pay for' and became something closer to a churn-instrumentation layer that happens to be free. That reframe is more accurate than my own.
That reframe is interesting. Have you seen anything from users yet that suggests they actually perceive CancelKit that way, or is the “churn-instrumentation layer” framing still something you're testing?
Still a hypothesis, to be honest. We're one day into launch, so "churn-instrumentation layer" is a story I'm telling based on what changed in the code, not something a user has said back to me yet. The honest test will be whether the first few real installs use it to actually look at patterns across cancellations, or just treat it as a nicer cancel-flow UI and never open the data. I won't know which one it is for a few weeks.
That’s a useful distinction. It’ll be interesting to see whether the first installs actually use the data to understand churn, or mainly treat it as a better cancellation flow.