CHURN ANALYSISCANCEL FLOWS

Churn survey questions: a cancellation reasons list you can act on

How to write a cancellation survey that produces usable data: a reasons taxonomy, follow-up questions, Stripe's feedback codes, and how to analyse the answers.

Published 8 min read

Every cancel flow asks some version of “why are you leaving?”. Most teams then do very little with the answers. The list was written in a hurry, half the responses are “Other”, the categories overlap, and when someone finally opens the spreadsheet a year later, the options have changed twice and nothing is comparable.

A cancellation survey is a small piece of data design. Get the taxonomy right once, and it pays off in routing (which offer to show), in product decisions (what to build or fix), and in analysis (which customers leave for which reasons). This guide covers how to build it.

What the survey is for

A cancellation survey has three jobs, in this order:

  1. Route the customer. The answer decides which offer, if any, the customer sees next. See cancel flow best practices.
  2. Record a reason you can count. One primary reason per cancellation, from a fixed list, stored with a stable code.
  3. Collect detail for later. An optional free-text field for the customer’s own words.

It is not a research interview. The customer is leaving. Ask one question, maybe one follow-up, and let them go. The cancel button stays visible throughout, as the click-to-cancel rules increasingly require.

A reasons taxonomy

Good reason lists are short, mutually exclusive, written in the customer’s words, and mapped to something you can do. Here is a starting taxonomy for a SaaS product, with two levels: a category for reporting, and the reason the customer sees.

Category Customer-facing reason Code What you can do
Price It’s too expensive price Downgrade, discount, monthly billing
Value I’m not using it enough low_usage Pause, onboarding help
Product It’s missing a feature I need missing_feature Roadmap info, call
Product It’s too hard to set up or use too_complex Setup call, guides
Product It’s not working reliably for me quality Support contact, bug follow-up
Competition I’m switching to another tool competitor Ask which one; call
Service Support didn’t meet my expectations service Escalate to a manager
Circumstance My project or company ended no_need Pause, or no offer
Circumstance It was only for a temporary need temporary Pause
Other Something else other Free text, no offer

That is ten options, which is at the upper limit. For most products, pick the six or seven that actually come up and fold the rest into “Something else”. You can add one later if “Other” keeps containing the same answer.

Rules for the list

  • One choice. Checkboxes give you nicer-looking data and worse decisions: you cannot route on three reasons. Ask for the main one.
  • Customer language. “I’m not using it enough” rather than “Low engagement”. “It’s too expensive” rather than “Pricing concerns”.
  • No overlaps. “Too expensive” and “Not worth the price” are the same answer to a customer and two answers in your data. Pick one.
  • Stable codes. You will reword reasons. Keep the code so history stays comparable.
  • Randomize the order, except “Something else” at the end, if you care about clean data. People in a hurry pick the first option more often.
  • Make it skippable. “Skip and cancel” must work. A skipped answer is data too.

Matching Stripe’s feedback codes

If you use Stripe, its subscriptions already have a place for this. The subscription’s cancellation_details has a feedback field and a free-text comment, documented in the cancel subscription API reference (accessed November 2026). The feedback values are:

Stripe feedback value Closest reason above
too_expensive price
unused low_usage, no_need, temporary
missing_features missing_feature
too_complex too_complex
low_quality quality
switched_service competitor
customer_service service
other other

Stripe’s customer portal cancellation page uses the same set of reasons. Stripe documents that cancellation_details can still be updated after a subscription is cancelled, so you can write the reason there from your own flow.

Our suggestion: keep your own, more specific codes as the primary record (Stripe’s unused merges “I don’t use it” with “I don’t need it anymore”, which call for different actions), and also write the closest Stripe value to cancellation_details.feedback. That keeps Stripe’s own reporting and any tool reading from Stripe consistent.

Follow-up questions

One follow-up, shown only for some reasons, can add a lot. Keep it optional.

Reason Follow-up question Format
Too expensive “What would feel like a fair price for you?” Short text or a few ranges
Missing a feature “Which feature do you need?” Short text
Switching tools “Which tool are you moving to?” Short text or list
Too hard to use “Where did you get stuck?” Short text
Not working reliably “What went wrong?” Short text
Something else “Tell us more, if you’d like” Short text

Do not ask for a follow-up when the reason is “My company closed”. There is nothing to learn that justifies the extra step.

Questions to avoid

  • “How likely are you to recommend us?” at cancellation. The answer is predictable and the moment is wrong.
  • Leading questions. “Did you know we have a cheaper plan?” is an offer dressed as a question. Show the offer as an offer.
  • Multiple pages of questions. Every extra screen is friction between the customer and the exit.
  • Mandatory free text. People type “x” to get through, and you learn nothing.

Handling free text

The free-text answers are the most useful part of the survey and the hardest to use. A few practices:

  • Read them. Every week, someone should read every comment. It takes minutes at most stages.
  • Tag them. When a comment says more than the reason chosen, add a secondary tag (a feature name, a competitor, a bug).
  • Treat them as customer data. Comments can contain names, emails and other personal details. Store them with the same care as the rest of your customer data, and do not paste them into logs.
  • Re-classify “Other” monthly. If several “Other” comments say the same thing, that is a missing reason.

AI can help summarise and tag comments at volume, as long as you check a sample by hand and treat the comment text as data, not instructions.

Analysing the answers

Reasons are only useful when you can compare them across segments and over time.

The basic report

Each month, count cancellations by category and reason, and compute each as a share of cancellations that answered. Report the skip rate separately.

Reason Cancellations Share of answers
Too expensive n n / answered
Not using it enough n n / answered
…
Skipped n (shown separately)

Cut it by segment

The same reason means different things in different segments. Useful cuts:

  • Plan and price point. “Too expensive” on your cheapest plan is a different problem from “too expensive” on the top plan.
  • Tenure. Reasons in the first 60 days usually point to onboarding and expectations; reasons after a year point to value and competition.
  • Acquisition source. If customers from one channel leave for “not what I expected”, the channel is setting the wrong expectations.
  • Activation. Customers who never activated and say “too expensive” are often telling you they never saw the value.

Our guide to finding what predicts churn with your own data covers how to compare segments properly, including sample sizes.

Connect reasons to outcomes

The reason is also the input to your offers and win-back emails:

  • Offer acceptance by reason: which offer works for which reason, measured against a holdout. See pause vs discount vs downgrade.
  • Saved customers still active at 30, 60 and 90 days, by reason.
  • Win-back rate by reason: customers who left for “missing feature” are the ones to email when you ship it. See win-back email examples.

Caveats

Stated reasons are what customers chose to tell you in a hurry. “Too expensive” is sometimes the polite version of “not worth it to me”. Use reasons alongside behaviour (usage, support tickets, payment history), not instead of it, and expect some noise.

Changing the list without breaking history

You will want to change the list. Do it carefully:

  1. Never reuse a code for a different meaning.
  2. Retire codes rather than deleting them; keep them in your data dictionary.
  3. Log the list version with each answer, so you know what options the customer saw.
  4. Map old codes to new categories in your reporting, so year-over-year charts still work.
  5. Change one thing at a time and note the date, so a jump in a reason’s share is not mistaken for a real trend.

Checklist

  • Six to eight reasons, in customer language, mutually exclusive.
  • One primary reason, single choice, skippable.
  • Stable internal codes, mapped to Stripe’s feedback values.
  • At most one optional follow-up, only where it helps.
  • Each reason mapped to one offer, or none.
  • Free text read weekly and treated as customer data.
  • Monthly report by reason, with the skip rate.
  • Reasons cut by plan, tenure, source and activation.
  • List version logged with each answer.

How NoChurn helps

NoChurn’s cancel flows ask for a reason, show one matching offer, and keep a one-click cancel button visible at every step. The reasons, offers and texts are yours to edit, per language, or you can run the flow headless through the API in your own UI. Each answer is stored with the customer, used to choose the right win-back email at 30, 60 and 90 days, and available for analysis alongside the custom attributes you send, so you can see which customer attributes correlate with churn.

It connects to Stripe in one click, and pricing depends only on MRR. See cancel flows, custom data and pricing.

PROMPT

Read https://www.nochurn.sh/llms.txt,
then follow https://www.nochurn.sh/docs/quickstart/
to add NoChurn to this app. Use a test key
(nc_test_…) until I say we go live.

Stop leaking revenue.

  • Free until $300 MRR
  • No card needed
  • Live in 10 minutes
  • Never a cut of what we recover