Guide

Stripe Cancellation Reasons: API Fields, Portal Data, and Persistence

Product teams want to know why customers cancel. Stripe can store structured cancellation metadata on the subscription object, but the data does not magically appear in your analytics warehouse — you still choose where to ask, how to map answers to Stripe fields, and when to copy values into your database.

This guide explains cancellation_details on Stripe subscriptions: what each field means, how the Customer Portal populates them, how to set them from your own cancel API call, and why feedback sometimes shows up on a later customer.subscription.updated webhook. For survey design and reason taxonomies, start with SaaS cancellation reasons and cancellation survey questions. For wiring the full flow, see build a Stripe cancellation flow.

Last updated October 13, 2026

What cancellation_details is

On the Subscription object, cancellation_details groups metadata about why a subscription was canceled. Stripe documents fields such as reason, feedback, and comment — verify the current API reference for the full list and enum values.

Distinguish this from your in-app survey store. Many teams keep rich session context (page path, offer shown, experiment bucket) in their own database while mirroring a normalized reason code into Stripe for support visibility.

reason, feedback, and comment

FieldTypical sourceHow teams use it
reasonStripe enum (for example cancellation_requested) or portal selectionClassify cancel type in Stripe Dashboard and support tools
feedbackCustomer Portal cancellation page options you configureAggregate product themes (price, missing feature, etc.)
commentFree-text when enabledQualitative detail; handle PII carefully in logs

Exact enum values and portal configuration options change over time — always confirm against Stripe’s cancellation page documentation before you hard-code mappings.

Collecting reasons through the Customer Portal

When you enable the cancellation page in the portal, Stripe can prompt for a reason before cancel completes. See cancellation page and portal cancellation setup.

Portal-collected feedback is written onto the subscription. Your backend should listen for customer.subscription.updated and copy cancellation_details into your analytics pipeline. Do not assume the first updated event after cancel already contains feedback — customers may submit the form slightly after the subscription is scheduled or ended.

Setting cancellation_details from a custom flow

In a custom in-app cancel flow, you usually capture the reason in your UI first, then call Stripe when the customer confirms. Depending on your integration, you may pass cancellation details on the cancel or update request documented in cancel subscriptions.

Pattern:

  • Persist reason in your database at submit time (source of truth for product analytics)
  • Include mapped cancellation_details when calling Stripe cancel/update
  • On webhook, reconcile Stripe’s copy with your row (detect drift if API failed)
# Illustrative sequence (verify parameters in current Stripe API docs)
1. User selects reason in your app → save to your DB
2. POST cancel or update subscription with cancellation_details
3. Webhook updated/deleted → upsert cancellation_details to your warehouse

Mapping portal options to your reason taxonomy

Cluster #1 guide SaaS cancellation reasons proposes categories that product teams track internally. Stripe portal options may not match one-to-one. Maintain an explicit mapping table:

Your internal codeStripe feedback valueNotes
pricePortal option label or valueKeep stable internal codes even if Stripe labels change
missing_feature…May require custom flow if portal list is too short
othercomment fieldRoute to qualitative review

Persistence and analytics patterns

  • Store session-level attributes (offer shown, saved vs canceled) in your database — Stripe will not retain full funnel context
  • Mirror Stripe cancellation_details to your warehouse on every subscription.updated affecting that subscription id
  • For churn analysis, join internal reason codes with MRR movement; use Stripe fields as a cross-check

Webhooks and late-arriving feedback

Treat reason capture as eventually consistent. Implement idempotent upserts keyed by subscription id and update your cancel outcome when cancellation_details.feedback transitions from empty to set. Details: Stripe cancellation webhooks.

Want reason capture inside your product?

ChurnIntel’s cancellation survey runs in your app before Stripe actions execute — so you keep full session context while still applying saves through Stripe Connect.