Stayput / Guides

Guide · facts checked 4 Nov 2026

Stripe cancellation_details: the reason values and how to read them

When a customer cancels in Stripe's customer portal and picks a reason, Stripe stores it on the subscription in cancellation_details.feedback, one of 8 fixed values. Anything they typed after choosing Other is in cancellation_details.comment. Both arrive on the customer.subscription.updated event when the cancel is scheduled for period end (and on customer.subscription.deleted when it takes effect immediately), so your code can act the moment someone cancels.

The fields that hold a cancel reason

FieldWhat Stripe says it holds
cancellation_details.feedbackThe reason the customer submitted for cancelling, if they cancelled explicitly. One of 8 values (below).
cancellation_details.commentAdditional comments about why the user cancelled, if they cancelled explicitly.
cancellation_details.feedback_optionCustomised feedback options that give deeper insight into why the subscription was cancelled, if it was cancelled explicitly by the user.
cancellation_details.reasonWhy the subscription was cancelled from Stripe’s side: cancellation_requested, payment_failed, payment_disputed or canceled_by_retention_policy.

Do not mix up feedback and reason. feedback is what the customer chose. reason is what happened to the subscription, and it is how you tell a customer who left from a card that failed.

The 8 feedback values

Stripe’s API reference lists these values for cancellation_details.feedback. The right-hand column is the portal wording from Stripe’s cancellation page that each value corresponds to; the pairing is read from the two lists, not stated by Stripe.

API valuePortal reason it corresponds to
too_expensiveIt’s too expensive
missing_featuresI need more features
switched_serviceI found an alternative
unusedI no longer need it
customer_serviceCustomer service was less than expected
too_complexEase of use was less than expected
low_qualityQuality was less than expected
otherOther reason (the customer can type free text)

You choose which of the reasons your customers see in the portal settings, so a given subscription may only ever carry some of these values. The reading cancel reasons guide maps each one to an offer worth trying.

Reading the reason from a webhook

Stripe’s cancellation page names the subscription webhook as one of the places to find the reasons customers select, next to the subscription page in the Dashboard and Stripe Sigma. In code, the subscription sits in event.data.object on a customer.subscription.updated event:

// Inside your verified webhook handler (see the Stripe cancel flow guide for signature checking)
if (event.type === "customer.subscription.updated") {
  const sub = event.data.object;
  const d = sub.cancellation_details;            // null if nobody cancelled explicitly
  if (d && d.feedback) {
    await saveCancelReason({
      subscriptionId: sub.id,
      feedback: d.feedback,                      // e.g. "too_expensive"
      comment: d.comment ?? null,                // free text, mostly with "other"
    });
  }
}

Every field is nullable, so test for cancellation_details and for feedback before you use them. customer.subscription.updated fires for every change to a subscription, so write by subscription ID (an upsert) and you will not store a cancel twice. When a cancel takes effect immediately, the subscription is deleted and the same cancellation_details object is on the customer.subscription.deleted event. The handler in the Stripe cancel flow guide already reads feedback and comment when a cancel is scheduled and is covered by tests: add the lines above where it handles the update.

What the fields do not tell you

  • Your own cancel page leaves these fields empty unless you fill them. The subscription cancel and update calls accept cancellation_details.feedback and comment, so write the reason there and every cancel, portal or not, lands in one place. A failed card has reason: payment_failed and no feedback.
  • Eight buckets are coarse. Too expensive may mean the price, or that they stopped getting value. The comment field is where the difference lives, and in the portal free text is offered only after Other reason.

That last gap is where a cancel flow earns its keep: it asks the question in your own words, reads the sentence, and answers with one offer that fits.

See a reason become an offer

Run a sample cancel session: play a subscriber, pick a reason, type a sentence, and see the offer, the wording and the Stripe change that come back.

90 seconds, no sign-up. You play the subscriber; the AI answers, and “Cancel anyway” is always one click away.

Related

Sources, read 4 Nov 2026