Improved

Webhook: cancellation reason

Platform
Subscription Canceled webhook now includes the cancellation reason

When a subscription is canceled, the SUBSCRIPTION_CANCELED webhook now tells you why. A new top-level cancellation object arrives alongside the existing subscription_contract data, carrying the reason label and any free text the subscriber typed.

What the new field looks like

The payload gains one additive key:

{
  "subscription_contract": { ... },
  "cancellation": {
    "reasons": [{ "title": "Too expensive" }],
    "custom_reason": "shipping was always late"
  }
}

reasons is always present and never null. Each entry is the label from your cancellation flow or reason list — for example "Too expensive" or "Pausing for now". An empty array means no reason was recorded: a merchant- or API-initiated cancellation, a shop without a cancellation flow, or a subscriber who submitted none.

custom_reason carries the free text the subscriber typed, or null if they typed nothing. The field is never an empty string — a blank submission and no submission are both null, so your code never has to branch on "".

This shape is consistent regardless of how you collect cancellation reasons: a Subi cancellation flow and a per-shop reason list produce the same structure on the webhook side.

What this enables

You can now branch your Klaviyo flows — or any webhook consumer — on the cancellation reason without a support lookup. A subscriber who chose "Too expensive" is a candidate for a discount win-back sequence; one who chose "Pausing for now" is more likely to return on a pause-and-return flow; one who said nothing may be a lapsed-payment case worth a different touch.

The reason data was already captured in Subi — it simply was not serialized into the webhook payload. No change is required to your webhook endpoint configuration or signing setup.

Beyond Subscriptions

Launch and scale recurring revenue on Shopify in days, not months.

Book Demo