> For the complete documentation index, see [llms.txt](https://developer.paddle.com/llms.txt).

# Configure Cancellation Flows

Build a cancellation process that saves customers by presenting them with dynamic salvage attempts, as well as capturing cancellation insights for your team.

---

You can use [Cancellation Flows](https://developer.paddle.com/concepts/retain/cancellation-flows-surveys.md), part of [Paddle Retain](https://developer.paddle.com/concepts/retain/overview.md), to build custom curated off-boarding experiences that are designed to prevent customers from churning.

Cancellation Flows presents customers with a simple survey that suggests dynamic salvage attempts, like pausing a subscription or switching plans, as well as gathering useful insights around why they want to cancel. As a last resort, you can offer customers [a discount](https://developer.paddle.com/build/products/offer-discounts-promotions-coupons.md) to incentivize them to stick around.

## How it works

[Paddle Retain](https://developer.paddle.com/concepts/retain/overview.md) combines world-class subscription expertise with algorithms that use billions of data points to automatically reduce churn. Paddle Billing is fully integrated with Retain, meaning it automatically handles dunning and retention for you.

[Cancellation Flows](https://www.profitwell.com/cancellation-flows) are a part of Paddle Retain, helping you save customers from canceling and gathering cancellation insights. They ask customers why they're canceling, as well as what they found valuable about your app, then presents curated salvage attempts.

If you use Paddle Billing, Cancellation Flows automatically takes action on the related subscription for you. Cancellation Flows are built-in to the [customer portal](https://developer.paddle.com/concepts/customer-portal.md), or you can build your own workflow in your frontend using Paddle.js.

## Before you begin

{% callout type="info" %}
Paddle Retain works with live data for your billing platform. This means you can't integrate or test with sandbox accounts.
{% /callout %}

- **Set up Paddle Retain**  
  If you haven't already, connect Paddle Retain to your billing platform and [set up Paddle Retain](https://developer.paddle.com/build/retain/set-up-retain-profitwell.md).
- **Make sure you've installed Paddle.js**  
  Paddle.js must be installed and verified as installed on a public page on your site. [Follow the instructions during setup](https://developer.paddle.com/build/retain/set-up-retain-profitwell#initial-setup-install.md), click **Edit** under **Paddle.js is not installed**, or click **Install** under **Paddle.js is not installed in web app**.

## Set up Cancellation Flows

{% tabs sync="retain-platform" %}
{% tab-item title="Paddle Billing" %}

### Go to Retain

If you set up Retain [for Paddle Billing](https://developer.paddle.com/build/retain/set-up-retain-profitwell#initial-setup.md), you can access and configure Cancellation Flows in the Paddle Billing dashboard.

{% instruction-steps %}

1. Go to **Paddle > Retain** in your dashboard.
2. Click {% mock-button %}Cancellation Flows.
3. If setting up for the first time, click Get started. If you’ve already configured Cancellation Flows, click Edit.
{% /instruction-steps %}

{% /dashboard-instructions %}

### Add branding

{% callout type="warning" %}
Any changes you make aren't saved until you reach the end of the setup flow. This includes when you edit an existing configuration.
{% /callout %}

Branding lets you customize the look and feel of Cancellation Flows, so they fit the style of your app. You can change the font family, and colors for text and components.

{% instruction-steps %}

1. Click the field under **Brand color** and use the color picker to select the color for selected states and progress bars.
2. Choose the font family for the text from the dropdown under **Font**.
3. Click the field under **Text color** and use the color picker to set the color for text.
4. Click the field under **Selected text color** and use the color picker to set the color for text in selected states.
5. View a preview of the first screen of the flow with your style selections on the right-hand side.
6. Click {% mock-button %}Continue to continue to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

### Add responses for cancellation reasons

On the first screen of the flow, a customer is asked why they're canceling. You can add up to 5 responses to this question that are displayed as options the customer can select.

{% instruction-steps %}

1. Enter the text for each response under the **Response** fields.
2. Click {% mock-button icon="carbon:add" %}Add response to add a new response field.
3. To remove a response, click the  icon next to it.
4. Check the preview of the first screen of the flow with your added responses on the right-hand side.
5. Click Continue to move to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

### Add responses for cancellation insights

On the second screen of the flow, a customer is asked what they think your app or company is doing well. You can add up to 5 responses to this question that are displayed as options the customer can select.

These response options are also used in the next step to offer a salvage attempt to the customer.

{% instruction-steps %}

1. Enter the text for each response in the **Response** fields.
2. Click {% mock-button icon="carbon:add" %}Add response to add another response field.
3. To remove a response, click the  icon next to it.
4. Preview the second screen of the flow with your added responses on the right-hand side.
5. Click Continue to move to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

### Map responses to salvage attempts

On the third screen of the flow, a customer is offered an alternative option to canceling based on their responses to the previous question.

For each response, you can choose which salvage attempt to offer. There are five types:

| Type                        | Description                                                                                                                   |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Contact support via email   | Encourage customers to email your support team.                                                                               |
| Book a meeting with support | Encourage customers to schedule a meeting with your team using [Calendly](https://calendly.com/).                             |
| Pause plan                  | Encourage customers to pause their subscription, so they can come back in the future.                                         |
| Plan switch                 | Encourage customers to switch to a different product or price, retaining them with an offer that's more suited or affordable. |
| No attempt                  | Don't offer any salvage attempts. Customers are directed to the next step.                                                    |

#### Contact support via email

{% instruction-steps %}

1. Select the response you want to offer this salvage attempt for from the dropdown boxes titled **When a customer selects**.
2. Select **Contact support via email** from the dropdown under **Offer**.
3. Customize another response's salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### Book a meeting with support

{% instruction-steps %}

1. Select the response you want to offer this salvage attempt for from the dropdown boxes titled **When a customer selects**.
2. Select **Book a meeting with support** from the dropdown under **Offer**.
3. Enter the text for the copy that precedes "can we chat a bit more about this?" under **Header copy**.
4. Customize another response's salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### Pause plan

{% instruction-steps %}
The number of months to pause the subscription for is automatically set to the number of months remaining on the subscription.

1. Select the response you want to offer this salvage attempt for from the dropdown boxes titled **When a customer selects**.
2. Select **Pause plan** from the dropdown under **Offer**.
3. Enter the text for the copy that precedes "how about pausing your subscription for [x] months?" under **Header copy**.
4. Customize another response's salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### Plan switch

{% instruction-steps %}

1. Select the response you want to offer this salvage attempt for from the dropdown boxes titled **When a customer selects**.
2. Select **Plan switch** from the dropdown under **Offer**.
3. Enter the text for the copy that precedes "how about switching to [x]?" under **Header copy**.
4. Customize another response's salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### No attempt

{% instruction-steps %}

1. Select the response you want to offer this salvage attempt for from the dropdown boxes titled **When a customer selects**.
2. Select **No attempt** from the dropdown under **Offer**.
3. Customize another response's salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

### Configure salvage attempts

Once you've mapped responses to salvage attempts, you must configure the salvage attempts you selected.

{% callout type="info" %}
You can't customize salvage attempts on a per-response basis. Your configuration applies to all responses that offer the same salvage attempt.
{% /callout %}

#### Contact support via email

{% instruction-steps %}

1. Click on the **Contact support via email** dropdown box.
2. Enter the email address to direct customers to under **Email for customer cancellation updates**.
3. Customize another salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### Book a meeting with support

{% instruction-steps %}

1. Click on the **Book a meeting with support** dropdown box.
2. Copy your [Calendly scheduling link](https://help.calendly.com/hc/en-us/articles/223193448-How-to-share-your-scheduling-link) on the Calendly platform.
3. Enter your Calendly scheduling link under **Calendly link**.
4. Customize another salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### Pause plan

{% instruction-steps %}

1. Click on the **Pause plan** dropdown box.
2. Select the unit of time to pause the subscription for from the dropdown under **For**.
3. Enter the number of time units to pause the subscription for in the field under **For**.
4. Customize another salvage attempt, or click {% mock-button %}Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### Plan switch

{% instruction-steps %}

1. Click on the **Plan switch** dropdown box.
2. Select the plan that customers would be switched from using the dropdown under **If customer is on**.
3. Select the plan that customers are offered to switch to using the dropdown under **Offer**.
4. Enter any additional details you want to display with the offer under **Information to display**.
5. Click {% mock-button icon="carbon:add" %}Add product to set up another plan switch pairing.
6. Click the  icon next to the **Salvage plan** dropdown box to remove an existing pairing.
7. Customize another salvage attempt, or click Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

### Customize the final discount offer

[Discounts](https://developer.paddle.com/api-reference/discounts/overview.md) let you reduce the amount a customer pays on subsequent transactions for their subscription. You can optionally offer a one-time, percentage-based discount to encourage customers who reject the salvage attempts to stay.

You can add up to 32 discounts to offer. Customers are offered the discount that matches the billing period of the subscription they are attempting to cancel. If there isn't a discount with a matching billing period, no discount is offered.

{% callout type="info" %}
If you choose to not offer a discount, or no discount can be offered, the customer is sent directly to the final page after they reject the salvage attempts.
{% /callout %}

You can also set the percentage amount to discount by, and the number of billing periods the discount applies for.

#### Offer a final discount

{% instruction-steps %}

1. Toggle **Offer a discount** on to enable the final discount offer.
2. Select the billing period the discount and subscription must match for the discount to be offered from the dropdown under **Billing period**.
3. If you selected **Every number of days, months, or years**, select the time unit from the dropdown and enter the number of time units.
4. Enter the percentage amount to discount by in the field under **Percentage off**.
5. Enter the number of billing periods the discount applies for in the field under **Discount length**.
6. Click {% mock-button icon="carbon:add" %}New discount to add a new discount to match against.
7. Click  to remove an existing discount.
8. Click Continue to progress to the next step.
{% /instruction-steps %}

{% /dashboard-instructions %}

#### Turn off the final discount offer

{% instruction-steps %}

1. Toggle **Offer a discount** off to disable the final discount offer.
2. Click {% mock-button %}Continue to finish the flow setup and save your changes.
{% /instruction-steps %}

{% /dashboard-instructions %}

### Customize the final feedback page

If the customer rejects the discount, or if no discount was offered, the customer is sent to the final feedback page to give any final feedback before confirming their cancellation. You can customize the copy on this page to encourage customers to provide feedback and fit your brand.

{% instruction-steps %}

1. Enter the text for the page's header copy under **Header copy**.
2. Enter the text for the copy following the header under **Body copy**.
3. Click {% mock-button %}Continue to finish the flow setup and save your changes.
{% /instruction-steps %}

{% /dashboard-instructions %}

{% /tab-item %}
{% tab-item title="Other platforms" %}

1. Grab a copy of the [Paddle Retain Cancellation Flows configuration](https://assets.paddle.com/assets/retain/paddle-retain-cancel-flow-setup-form.xlsx) template and fill it in. If you use Stripe, grab a copy of the [Paddle Retain Cancellation Flows configuration for Stripe](https://docs.google.com/spreadsheets/d/1K09DDHEpxCk5PAnEsVfLN6t7o99ZU5rQEknyCqMddIA/edit#gid=413128940) template and fill it in instead.
2. Send it to us at [sellers@paddle.com](mailto:sellers@paddle.com?subject=RETAIN%20Cancellation%20Flows).

{% /tab-item %}
{% /tabs %}

## Preview Cancellation Flows

Once Cancellation Flows are set up, you can preview the flow customers see when they try to cancel a subscription. The flow is interactive, so you can view each page and check how it behaves.

{% instruction-steps %}

1. Go to **Paddle > Retain > Cancellation Flows**.
2. Click the  button in the **Cancellation flow** card.
3. Click  to preview the flow.

{% /dashboard-instructions %}

## Integrate Cancellation Flows into your frontend

{% tabs sync="retain-platform" %}
{% tab-item title="Paddle Billing" %}

If you handle cancellations using the [customer portal](https://developer.paddle.com/concepts/customer-portal.md), you don't need to do anything. The customer portal automatically launches Cancellation Flows when customers try to cancel a subscription.

If you've built your own cancellation logic outside the customer portal, you need to:

1. Call the [`Paddle.Retain.initCancellationFlow()`](https://developer.paddle.com/paddlejs/methods/paddle-retain-initcancellationflow.md) method when a customer tries to cancel a subscription on your website or app, passing the Paddle ID of [the subscription entity](https://developer.paddle.com/api-reference/subscriptions/overview.md) that the customer wants to cancel.
2. Attach a callback to build workflows around [the result](https://developer.paddle.com/paddlejs/methods/paddle-retain-initcancellationflow#returns.md).
3. Retain automatically handles pausing, applying a discount, switching plans, or canceling the related subscription in Paddle Billing. When those [events](https://developer.paddle.com/webhooks/overview.md) occur in Paddle, [handle provisioning in your app](https://developer.paddle.com/build/subscriptions/provision-access-webhooks.md) as normal.

### Example

```html {% title="Example of a cancellation flow" %}
<!-- Cancellation button -->
<button onclick="cancelSubscription();">Cancel my subscription</button>

<script type="text/javascript">
  function cancelSubscription() {
    Paddle.Retain.initCancellationFlow({
      subscriptionId: 'sub_01h8bqcrwp0vjd1p3bv20y7323'
    });
  }
</script>
```

{% /tab-item %}
{% tab-item title="Other platforms" %}

1. Update the cancellation logic in your web app to call the `profitwell` method, passing `init_cancellation_flow` and an object containing the ID of the subscription in your billing platform that you want to cancel.
2. Use the `.then()` method to attach a callback to the result, then build logic to [handle the result](https://developer.paddle.com/paddlejs/methods/paddle-retain-initcancellationflow#returns.md). You should cancel a subscription when the customer chooses to cancel or when the cancellation flow encounters an error.
3. Retain automatically handles pausing, applying a discount, or switching plans in your billing platform. Handle provisioning in your app as normal.

{% callout type="warning" %}
Retain doesn't cancel subscriptions in your billing platform, unless you're using Paddle Billing. You must [build logic to handle this](https://developer.paddle.com/build/lifecycle/subscription-cancellation.md). You should run this logic when a cancellation flow encounters an error, too.
{% /callout %}

### Example

```html {% title="Example of a cancellation flow" %}
<!-- Cancellation button -->
<button onclick="cancelSubscription();">Cancel my subscription</button>

<script type="text/javascript"> 
  function cancelSubscription() {
    
    // Any pre-cancellation logic here
    
    // Start cancellation flow
    profitwell('init_cancellation_flow', {
        subscription_id: 'sub_1OFyRG2eZvKYlo2CQZLEmvPK'
      })
      .then((result) => {
        if (result.status === 'retained' || result.status === 'aborted') {
        
          // Logic to run when a customer accepts a salvage attempt, salvage offer, 
          // or closes the cancellation flow modal.
          console.log("Customer retained!");
          
        } else {
        
          // Logic to run when a customer chooses to cancel or when an error occurs.
          console.log("Customer proceeded with cancellation, or an error occurred.");
          
        }
      });
  };
</script>
```

{% /tab-item %}
{% /tabs %}

## Simulate Cancellation Flows

Once you've set up Cancellation Flows, you can simulate one to check how it looks to customers.

{% tabs sync="retain-platform" %}
{% tab-item title="Paddle Billing" %}

1. Go to a page where you've [installed Paddle.js for Retain](https://developer.paddle.com/paddlejs/include-paddlejs#manual-initialize-paddlejs-retain.md).
2. Open your [browser console](https://developer.chrome.com/docs/devtools/console/).
3. Enter `Paddle.Retain.demo({feature: 'cancellationFlow'})`.

{% /tab-item %}
{% tab-item title="Other platforms" %}

1. Go to a page where you've [installed the ProfitWell.js snippets](https://developer.paddle.com/build/retain/set-up-retain-profitwell#connect-billing".md).
2. Open your [browser console](https://developer.chrome.com/docs/devtools/console/).
3. Enter `profitwell('cq_demo', 'cancellation_flow')`.

{% /tab-item %}
{% /tabs %}

## Subscribe to notification emails

You can subscribe to receive notification emails based on the outcome of a cancellation flow for monitoring and reporting purposes. You choose which emails you'd like to receive by toggling the available options.

{% collapsible title="Notification email options" %}

- **Abort Cancellation**  
  Notify when a customer starts to cancel but is successfully retained and doesn't go through with it.
- **Cancellation**  
  Notify when a customer completes a cancellation despite going through the flow.
- **Pause Plan**  
  Notify when a customer accepts a pause offer instead of cancelling.
- **Switch Plan**  
  Notify when a customer accepts a plan switch as an alternative to cancelling.
- **Salvage Offer**  
  Notify when a customer accepts a discount or other salvage offer.

{% /collapsible %}

{% instruction-steps %}

1. Go to **Paddle > Retain > Cancellation Flows**.
2. In the **Notification email settings** section, click {% mock-button %}Edit.
3. Enter the email address where you want to receive notifications under **Notification email**.
4. Toggle the notification options that you'd like to receive emails for.
5. Click Save to save your changes.
{% /instruction-steps %}

{% /dashboard-instructions %}

## Subscribe to receive reports

You can subscribe to receive a weekly CSV report of Cancellation Flows activity. The report is an activity log with one row for every time a cancellation flow is initialized. Use it to analyze cancellation patterns, measure your save rate, and identify which salvage attempts are most effective.

Certain fields in the report map to those in the return object of [`Paddle.Retain.initCancellationFlow()`](https://developer.paddle.com/paddlejs/methods/paddle-retain-initcancellationflow#returns.md).

{% collapsible title="Report fields" %}

```yaml
title: Cancellation Flows activity report
type: object
properties:
  id:
    type: integer
    description: Internal ID for this row.
  uuid:
    type: string
    description: Unique identifier for this cancellation flow session.
  company_id:
    type: integer
    description: Identifier for your Paddle account.
  customer_id:
    type: string
    description: Unique Paddle ID of the customer who triggered the cancellation flow, prefixed with `ctm_`.
  status:
    type: string
    description: Outcome of the cancellation flow session.
    enum:
      - pending
      - aborted
      - retained
      - chose_to_cancel
    x-enum-descriptions:
      pending:
        description: The cancellation flow was initialized but hasn't completed yet.
      aborted:
        description: The customer exited the flow without completing it.
      retained:
        description: The customer accepted a salvage attempt or discount offer and didn't cancel.
      chose_to_cancel:
        description: The customer completed the flow and chose to cancel.
  created_on:
    type: string
    format: date-time
    description: When the cancellation flow was initialized.
  status_last_updated:
    type: string
    format: date-time
    description: When the status was last updated.
  cancel_reason_id:
    type:
      - string
      - "null"
    description: ID of the cancellation reason the customer selected. Maps to your configured cancellation reasons. Corresponds to the label text returned as `cancelReason` by [`Paddle.Retain.initCancellationFlow()`](https://developer.paddle.com/paddlejs/methods/paddle-retain-initcancellationflow.md).
  satisfaction_insight_id:
    type:
      - string
      - "null"
    description: ID of the satisfaction insight the customer selected. Maps to your configured insight responses. Corresponds to the label text returned as `satisfactionInsight` by [`Paddle.Retain.initCancellationFlow()`](https://developer.paddle.com/paddlejs/methods/paddle-retain-initcancellationflow.md).
  salvage_attempt_intended:
    type:
      - string
      - "null"
    description: The type of salvage attempt that was offered to the customer, based on your configuration.
    enum:
      - contact_support_email_notification
      - contact_support_meeting_scheduler
      - pause_subscription
      - plan_switch
    x-enum-descriptions:
      contact_support_email_notification:
        description: Customer was prompted to send a message to your support team.
      contact_support_meeting_scheduler:
        description: Customer was prompted to schedule a meeting using Calendly.
      pause_subscription:
        description: Customer was prompted to pause their subscription.
      plan_switch:
        description: Customer was prompted to switch to another plan.
  salvage_attempt_used:
    type:
      - string
      - "null"
    description: The salvage attempt the customer accepted and engaged with.
    enum:
      - contact_support_email_notification
      - contact_support_meeting_scheduler
      - pause_subscription
      - plan_switch
    x-enum-descriptions:
      contact_support_email_notification:
        description: Customer sent a message to your support team.
      contact_support_meeting_scheduler:
        description: Customer scheduled a meeting using Calendly.
      pause_subscription:
        description: Customer paused their subscription.
      plan_switch:
        description: Customer switched to another plan.
  salvage_attempt_decision:
    type:
      - string
      - "null"
    description: Whether the customer accepted or rejected the salvage attempt, or no attempt was made.
    enum:
      - accepted
      - rejected
      - no_attempt
    x-enum-descriptions:
      accepted:
        description: The customer accepted the salvage attempt.
      rejected:
        description: The customer rejected the salvage attempt.
      no_attempt:
        description: No salvage attempt was offered.
  salvage_attempt_resolution:
    type:
      - string
      - "null"
    description: Whether the salvage attempt successfully prevented the customer from canceling.
    enum:
      - succeeded
      - failed
    x-enum-descriptions:
      succeeded:
        description: The salvage attempt prevented the customer from canceling.
      failed:
        description: The salvage attempt did not prevent the customer from canceling.
  salvage_offer_decision:
    type:
      - string
      - "null"
    description: Whether the customer accepted or rejected the final discount offer.
    enum:
      - accepted
      - rejected
    x-enum-descriptions:
      accepted:
        description: The customer accepted the discount offer.
      rejected:
        description: The customer rejected the discount offer.
  salvage_offer_id:
    type:
      - string
      - "null"
    description: ID of the discount offer that was presented to the customer.
  subscription_id:
    type: string
    description: Paddle ID of the subscription the cancellation flow was triggered for, prefixed with `sub_`.
  additional_feedback:
    type:
      - string
      - "null"
    description: Free-text feedback provided by the customer on the final feedback page before confirming cancellation.
  plan_map_id:
    type:
      - string
      - "null"
    description: ID of the plan map configuration used for a plan switch salvage attempt.
  salvage_attempt_context:
    type:
      - string
      - "null"
    description: Additional context about the salvage attempt outcome, as a JSON string.
  mrr_cents_at_risk:
    type: integer
    description: Monthly recurring revenue at risk from this cancellation, in cents, in the subscription currency.
  currency:
    type: string
    description: Three-letter ISO 4217 currency code for the subscription.
  plan_id:
    type: string
    description: Paddle ID of the price the customer was subscribed to, prefixed with `pri_`.
  one_click_cancellation:
    type: integer
    description: Whether the customer used one-click cancellation. `1` if true, `0` if false.
```

{% /collapsible %}

{% instruction-steps %}

1. Go to **Paddle > Retain > Cancellation Flows**.
2. Click {% mock-button %}Edit in the **Weekly report email** section.
3. Enter the email address you want to receive the report at.
4. Add more email addresses by clicking Add email.
5. Click Save.
{% /instruction-steps %}

{% /dashboard-instructions %}