# How to ask customers why they cancel, in Stripe

> The Stripe customer portal can ask for a reason on the way out. How to turn it on, which reasons it offers and where to find the answers.

By Luis Miguel, founder of TrustChurn. Published 2026-10-31.
Web version: https://trustchurn.com/blog/stripe-cancellation-reasons

To ask customers why they cancel in Stripe, turn on "Cancellation reason" in Settings, then Billing, then Customer portal. Customers who cancel through the portal then pick a reason from a short list before the cancellation goes through, and Stripe saves their answer on the subscription, in `cancellation_details.feedback`, with anything they typed in `cancellation_details.comment`. If they cancel somewhere else, in your app for example, you can ask them yourself and save the answer in the same fields through the API.

It takes a minute to set up and costs nothing. The steps match Stripe's docs for the [customer portal](https://docs.stripe.com/customer-management/configure-portal) and the [cancellation page](https://docs.stripe.com/customer-management/cancellation-page).

## How do you turn it on?

In the Dashboard, go to Settings, then Billing, then Customer portal. In the Cancellations section:

- **Cancel subscription** lets customers cancel from the portal at all.
- **Cancellation reason** shows them a short list of reasons before the cancellation goes through.
- **Retention coupons** offers a coupon you pick when they try to cancel. Stripe calls this deflecting cancellations. It's off by default.

You also choose whether a cancellation happens right away or at the end of the period they already paid for. I'd pick the end of the period. The customer keeps what they paid for, and you get days or weeks to talk to them before they go, which is why [scheduled cancellations](https://trustchurn.com/blog/scheduled-cancellations-stripe) are worth watching. If you cancel immediately, you also choose whether to prorate what's left of the period.

If you configure the portal through the API instead, the same switches live under `features.subscription_cancel`: `mode` (`at_period_end` or `immediately`) and `cancellation_reason.enabled` with its `options`.

## Which reasons can customers choose?

The customer picks one of these. The second column is what Stripe stores:

| What the customer sees | Stored as |
| --- | --- |
| It's too expensive | `too_expensive` |
| I need more features | `missing_features` |
| I found an alternative | `switched_service` |
| I no longer need it | `unused` |
| Customer service was less than expected | `customer_service` |
| Ease of use was less than expected | `too_complex` |
| Quality was less than expected | `low_quality` |
| Other reason | `other` |

You can choose which ones to show. With "Other reason" they can type a comment. Stripe also lets you define your own feedback options, which are stored in `cancellation_details.feedback_option` instead.

Show only the options that fit your product. If "customer service" doesn't apply to you, leave it out, and the answers you get are easier to act on.

## Where do the answers end up?

On the subscription, in `cancellation_details`:

- `feedback` is the reason they picked, from the table above.
- `comment` is anything they typed.
- `reason` is how the cancellation happened, not why: `cancellation_requested` when someone canceled it, `payment_failed` when it ended after failed payments, `payment_disputed` after a dispute.

You can see them on the subscription's page in the Dashboard and in the `customer.subscription.updated` event, or query them with Sigma.

To pull them all at once, a few lines of the API and a read-only key will do it:

```ts
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_KEY!);

for await (const s of stripe.subscriptions.list({ status: "canceled", limit: 100 })) {
  const d = s.cancellation_details;
  if (d?.feedback || d?.comment) console.log(s.id, d.feedback ?? "", d.comment ?? "");
}
```

## What if customers cancel in your own app?

You can still ask them and keep the answers in the same place. Both the cancel call and the update call accept `cancellation_details` with a `feedback` (one of the values in the table) and a `comment`. Even after a subscription is canceled, Stripe still lets you update its `cancellation_details`, so a reason that arrives by email a day later can go on the right subscription.

## How should you read them?

With a few dozen customers, read every one. Tallying the reasons is fine, but the typed comments are where you find something to reply to.

What each reason tends to mean:

- **`too_expensive`** can mean "not worth it for what I use" as much as "I can't afford it". Send one email asking which part they used. Sometimes a smaller plan or a pause keeps them.
- **`unused`** can mean they never got set up. Look at onboarding before you touch pricing.
- **`missing_features`** and **`switched_service`** tell you what you're compared against. Ask what they moved to.
- **`too_complex`** points at the first days of use. Check where those customers stopped.
- **`payment_failed`** in `reason` isn't a decision at all. That's [involuntary churn](https://trustchurn.com/blog/failed-payments-stripe), and it needs a different fix.

When you're small, I'd look at the reasons before the rate. If ten cancellations give the same reason, you know what to work on, whatever your churn number says ([churn with 20 customers](https://trustchurn.com/blog/churn-with-few-customers)).

## Should you offer a coupon when they cancel?

The portal can do it: with a retention coupon set, customers who start to cancel see the offer first. It can keep customers for whom price really was the issue. It can also teach customers that clicking "cancel" gets a discount, and it does nothing for the other reasons. If you try it, look at whether the customers who took the coupon are still there a few months later, not only whether they took it.

## Do reasons show up on TrustChurn?

Never on a public profile. They go to your private list, which shows everyone who left in the last 30 days with the reason and the comment they gave in the portal, and a new "too expensive" sends you an email the same hour, while there's still time to write back.

## FAQ

### Where do I see why a customer canceled in Stripe?

On the subscription's page in the Dashboard, and in the subscription's `cancellation_details`: `feedback` has the reason they picked and `comment` anything they typed. You can also query them with Sigma or list canceled subscriptions with the API.

### Can I add my own cancellation reasons?

Yes. Besides choosing which of Stripe's reasons to show, you can define custom feedback options. The answer to those is stored in `cancellation_details.feedback_option`.

### Can I offer a discount when someone tries to cancel?

Yes. In the Cancellations section of the customer portal settings, pick a retention coupon. Stripe offers it to customers before the cancellation goes through. It's off by default.

### What does cancellation_details.reason mean?

It says how the subscription was canceled, not why the customer left: `cancellation_requested` when someone canceled it, `payment_failed` when it ended after failed payments, and `payment_disputed` after a dispute. The customer's own answer is in `feedback`.

### Can I record a reason if I canceled the subscription myself?

Yes. The cancel and update calls accept `cancellation_details` with a `feedback` and a `comment`, and you can still update them after the subscription is canceled.

---

Keep reading:

- [How to find scheduled cancellations in Stripe](https://trustchurn.com/blog/scheduled-cancellations-stripe.md)
- [Failed payments and expiring cards in Stripe](https://trustchurn.com/blog/failed-payments-stripe.md)

TrustChurn is a public ranking of SaaS by monthly churn, read from Stripe with a read-only restricted key, not typed by the founder. The formulas are public: https://trustchurn.com/methodology
