Team
Launch LogCancel Flows
Quick Start GuideFurther ConfigurationCustom StylingMulti-Language SupportEmail-Verified Cancel FlowA/B TestingCancel Flow TestingPause WallFailed Payment Recovery
Payment RecoveryCampaign CustomizationFailed Payment WallReactivations
ReactivationsCampaign Customization GuideCustomer Health
Customer HealthData Integrations
Data APIWebhooksEvent TrackingBilling Providers
Payment Provider OverviewSlackStripeChargebeePaddle ClassicPaddle BillingBraintreeZuoraSupport
FAQsCancel Flow Configuration Options
Additional details for all of the following options are included below.
window.churnkey.init('show', {
customerId: 'billing_provider_cus_id',
subscriptionId: 'billing_provider_sub_id',
billingProvider: 'stripe', // or chargebee, braintree, paddle, paddling-billing
mode: 'live', // or 'test',
record: true, // session playback recording
preview: false, // disables any billing actions if true
report: true, // enable/disable including this sessions results in analytics dashboard
bypassDiscountAppliedScreen: false,
bypassPauseAppliedScreen: false,
customerAttributes: {
favoriteAnimal: 'penguin', // details below
},
i18n: {
lang: 'en',
messages: {
en: {
declineOffer: 'No thanks',
...
},
}
},
// dynamic offers
dynamicOffers: {
discount: {
couponId: 'override-coupon-id',
},
planChange: {
planIds: ['price_abc', 'price_xyz'],
}
},
// handler callbacks
handlePause: <Promise(customer, { pauseDuration: number})>,
handleCancel: <Promise(customer, surveyResponse, freeformFeedback)>,
handleDiscount: <Promise(customer, coupon)>,
handleTrialExtension: <Promise(customer, { trialExtensionDays })>,
handlePlanChange: <Promise(customer, { plan })>,
handleSupportRequest: <function(customer)>,
handleRedirect: <function(customer, { redirectLabel, redirectUrl })>,
// listener callbacks
onPause: <function(customer, { pauseDuration }>,
onCancel: <function(customer, surveyResponse)>,
onDiscount: <function(customer, coupon)>,
onTrialExtension: <function(customer, { trialExtensionDays })>,
onPlanChange: <function(customer, { planId })>,
onGoToAccount: <function(sessionResults)>,
onClose: <function(sessionResults)>,
onStepChange: <function(newStep, oldStep)>
})
Enable/Disable Session Recording
By default, session recording is enabled. You can turn off session recording by setting the record
option to false in the initialization code (step 3).
window.churnkey.init('show', {
...,
record: true
})
Live Mode vs Test Mode
Each mode corresponds to the billing provider (Stripe, Braintree, Chargebee, etc.) environment you're working in. When set to live
mode, Churnkey will look for a customer matching customerId
in your production environment, and changes made to that customer subscription will be applied in the production environment. If you're not ready for live data, you can set mode to test
and Churnkey will work with the test environment of your billing provider.
window.churnkey.init('show', {
...,
mode: 'live' // or 'test'
})
Customer ID vs Subscription ID
subscriptionId
and we’ll take it from there.Churnkey connects to your billing provider so that we can take actions on your behalf to update customer subscriptions. In particular, based on the outcome of your cancel flow, Churnkey can:
- Pause subscription(s)
- Discount subscription(s)
- Cancel subscription(s)
When initializing Churnkey, we recommend that, in addition to the customerId
parameter, you pass in subscriptionId
. This approach is useful when customers have multiple subscriptions and you want precise control over exactly which subscription is being paused, discounted, or canceled. If you don't support multiple subscriptions and have no intention of doing so, the approach described in the next section might be appropriate.
If you want Churnkey to make changes more broadly across the customer's entire billing record, you should omit the subscriptionId
parameter while still including the required customerId
parameter. This is useful when a customer has multiple plans and you want offers to apply to all current billing.
When just a customer ID is used, Churnkey will apply billing changes at the customer level, when possible. If the changes cannot be made at the customer level (e.g. Stripe does not allow customer-level pauses), the action will be taken across every active subscription the customer has.
Billing Actions using just Customer ID
Below are the default actions that Churnkey will take on your behalf if you pass only a customer ID as a configuration option.
Billing Actions using Customer ID + Subscription ID
Below are the default actions that Churnkey will take on your behalf if you pass both a customer ID and subscription ID as configuration options.
Custom Attributes
When initializing the cancel flow, you can pass in customerAttributes
which can be used for customer segmentation.
- Define a new custom attribute in the “Advanced Settings” tab of the Cancel Flows Builder page
- Example:
videosCreated
, which will represent the total number of videos a customer has created on our example platform - Use this custom attribute to create a new audience of a segmented cancel flow
- Example: create a segment which targets customers where
videosCreated
is more than 20. Tailor the copy to acknowledge that these customers have been power users and offer more generous temporary discounts - Pass in
videosCreated
undercustomerAttributes
when callingwindow.churnkey.init()
window.churnkey.init('show', {
customerId: 'CUSTOMER_ID', // required
authHash: 'HMAC_HASH', // required
appId: 'YOUR_APP_ID', // required
customerAttributes: {
videosCreated: 28 // data about the customer you provide
}
})
Customer Segmentation by Metadata
You can leverage your existing customer and subscription metadata for customer segmentation purposes.
Suppose you have a videosCreated
metadata field already set for all your customers.
- Set up a new custom attribute in the “Flow Settings” tab of the Cancel Flows Builder page. Here you are letting us know what fields you are using in your metadata: their names and the type of data they contain.
- Example:
videosCreated
, which will represent the total number of videos a customer has created on our example platform. - Use this custom attribute to create a new audience of a segmented cancel flow.
- Example: create a segment which targets customers where
videosCreated
is more than 20. Tailor the copy to acknowledge that these customers have been power users and offer more generous temporary discounts - All done! Whenever an end-customer opens the Embed, we will look up the field
videosCreated
in their customer metadata and subscription metadata. If we find it, we will use that value to evaluate your segment conditions.
Bypass Offer Accepted Screen
If a previously discount or pause offer is still in effect, we will message this to the customer. This pattern is designed to remind the customer of the offer and maximize retention. However, you can disable this check using our bypassDiscountAppliedScreen
and bypassPauseAppliedScreen
configuration parameters.
window.churnkey.init('show', {
...,
bypassDiscountAppliedScreen: false,
bypassPauseAppliedScreen: false,
})
Dynamic Offers
You can optionally pass plan IDs and/or a coupon ID which will override the default offer(s) for you flow.
window.churnkey.init('show', {
...,
dynamicOffers: {
discount: {
couponId: 'override-coupon-id',
},
planChange: {
planIds: ['price_abc', 'price_xyz'],
}
},
})
Note that these overrides, if present, will take the place of all existing discount and plan change offers in your cancel flow, respectively. We advise you to still include default discount and plan change settings in your cancel flow.
Custom Callbacks
Churnkey provides two types of callbacks for hooking into the cancel flow: handler callbacks and listener callbacks.
Handler Callbacks
Handler callbacks are intended for when you want to handle changes to a customer's subscription instead of having Churnkey do it on your behalf. If a handler callback for a customer event is defined, Churnkey will not take action on your behalf when this event occurs.
Most handler type callbacks (with the exception of handleSupportRequest
) are JavaScript Promise objects. Calling resolve
will advance Churnkey's flow to a success state. Optionally, you can pass a message which will be shown to the customer. Calling reject
will advance Churnkey's to an error state. Again, you can optionally pass a message to show the customer.
{
// AVAILABLE NOW
handlePause: <Promise>,
handleCancel: <Promise>,
handleDiscount: <Promise>,
handleTrialExtension: <Promise>,
handleRedirect: <Promise<(customer, redirectConfig)>,
handleSupportRequest: <function>
handlePlanChange: <Promise<(customer, { plan })>,
}
Listener Callbacks
Listener callbacks are used to listen for events so that you can take appropriate action in your application. Unlike handler callbacks, if listener callbacks are defined, Churnkey will still take action on your behalf to update customer accounts in Stripe.
An example use case for the onCancel
listener callback would be initiate client-side, client-specific business logic which needs to take place when a customer cancels there account, such as limiting the features available to them. Often times, this same logic client-side logic can be implemented using Stripe webhooks, and it will typically just come down to what works best for your application.
{
// AVAILABLE NOW
onPause: <function(customer, { pauseDuration }>,
onCancel: <function(customer, surveyResponse)>,
onDiscount: <function(customer, coupon)>,
onTrialExtension: <function(customer, { trialExtensionDays })>,
onPlanChange: <function(customer, { planId })>,
}
Example callbacks for custom pause and cancellation handling
For example, if you want to implement your own pause logic (instead of using Stripe's default pausing mechanism) you can defined the handlePause
option. Similarly, you can use the handleCancel
option to implement your own cancellation. These callbacks will be fired in when your customer selects that they would like to pause or cancel their subscription, respectively. And, if the respective callbacks are defined, Churnkey will not attempt to take action on your behalf by interfacing with Stripe to pause or cancel the subscription.
window.churnkey.init('show', {
appId: 'YOUR_APP_ID',
customerId: 'STRIPE_CUSTOMER_ID',
authHash: 'HMAC_HASH,
handlePause: (customer, { pauseDuration }) => {
return new Promise(async (resolve, reject) => {
try {
//////////
// YOUR CUSTOM PAUSING FUNCTION GOES HERE
//////////
// Optionally, display a custom message by passing a `message` when resolving
// if you don't pass a message, a generic pause confirmation message will be shown
resolve({ message: `Account has been paused for ${pauseDuration} month${duration === 1 ? '' : 's'}` });
} catch (e) {
console.log(e);
reject({ message: 'Failed to pause account. Please reach out to us through our live chat.' });
}
});
},
handleCancel: (customer, surveyAnswer) => {
// customer is the Stripe customer object (string)
// surveyAnswer is the reason they're leaving (string)
return new Promise(async (resolve, reject) => {
try {
///////////
// CANCEL THE CUSTOMERS SUBSCRIPTION HERE
//////////
// Optionally, display a custom message by passing a `message` when resolving
// if you don't pass a message, the generic message below will be displayed
resolve({ message: 'Your account has been canceled. You will not be billed again' });
} catch (e) {
console.log(e);
reject({ message: 'Failed to cancel account. Please reach out to us through our live chat.' });
}
});
},
})
Example support request callback (connect with Intercom, Crisp, etc.)
The below example snippet implements handleSupportRequest
, triggering an Intercom chat. handleSupportRequest
is slightly different than the other handle-type callbacks in that it is a normal function, not a promise. This is intended as implementations of handleSupportRequest
should not require backend changes and should (in nearly all cases) be synchronous.
window.churnkey.init('show', {
appId: 'YOUR_APP_ID',
customerId: 'STRIPE_CUSTOMER_ID',
authHash: 'HMAC_HASH,
handleSupportRequest: customer => {
console.log(customer);
window.Intercom('showNewMessage', 'Attention: Offboarding Customer Needs Help ASAP.\n');
window.churnkey.hide();
},
})
Example Step Change Listener
If you would like to use the onStepChange(newStep, oldStep)
listener, each step has the following structure
{
"stepType": "OFFER",
"header": "Need Some Time Off? No Problem!",
"description": "<p>We totally get it. Everyone needs a break sometimes. Did you know you can pause your subscription for up to 3 months, free of charge? When you're ready, your favorite shows and movies will be right here waiting for you.</p>",
"offer": {
"guid": "90b5af40-be1d-4c2c-aa93-c3d3007234da",
"offerType": "PLAN_CHANGE", // or PAUSE or DISCOUNT or TRIAL_EXTENSION
},
}
Utility Functions
Churnkey stores temporary flow state to keep track of survey responses and if a customer has already completed a flow to show messages like “Your discount has been applied”, if the flow is triggered again. Depending on your application, you may want to clear this state manually, or each time the flow is retriggered.
// used to reset internal state, useful for calling when a customer logs out
window.churnkey.clearState()
// used in place of window.churnkey.init('show',...)
// first clears internal state and then calls window.churnkey.init('show',...
window.churnkey.init('restart', ...
On this page
- Cancel Flow Configuration Options
- Enable/Disable Session Recording
- Live Mode vs Test Mode
- Customer ID vs Subscription ID
- Billing Actions using just Customer ID
- Billing Actions using Customer ID + Subscription ID
- Custom Attributes
- Customer Segmentation by Metadata
- Bypass Offer Accepted Screen
- Dynamic Offers
- Custom Callbacks
- Handler Callbacks
- Listener Callbacks
- Example callbacks for custom pause and cancellation handling
- Example support request callback (connect with Intercom, Crisp, etc.)
- Example Step Change Listener
- Utility Functions