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
| Field | Typical source | How teams use it |
|---|---|---|
| reason | Stripe enum (for example cancellation_requested) or portal selection | Classify cancel type in Stripe Dashboard and support tools |
| feedback | Customer Portal cancellation page options you configure | Aggregate product themes (price, missing feature, etc.) |
| comment | Free-text when enabled | Qualitative 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 warehouseMapping 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 code | Stripe feedback value | Notes |
|---|---|---|
| price | Portal option label or value | Keep stable internal codes even if Stripe labels change |
| missing_feature | … | May require custom flow if portal list is too short |
| other | comment field | Route 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.