Docs navigation

Concepts

The SDK is built around five concepts: steps, offers, handlers, listeners, and integration levels. The rest of the documentation builds on these.

Steps

A cancel flow is an ordered list of steps. Each step has a type that decides what gets rendered. The SDK ships five built-in types and accepts any other string as a custom type.

TypeWhat it renders
surveyA list of cancellation reasons. Reasons can carry an attached offer.
offerA value proposition: discount, pause, plan change, trial extension, contact, redirect, or custom.
feedbackA freeform text input.
confirmThe final cancellation prompt.
successThe terminal state, either saved or cancelled.

Any type the SDK doesn't recognize is a custom step. The SDK navigates through it like a built-in and looks up your renderer in customComponents keyed by that string.

const steps = [
  { type: 'survey', reasons: [/* ... */] },
  { type: 'nps', data: { scale: 10 } },     // custom step
  { type: 'feedback' },
  { type: 'confirm' },
]

The SDK walks steps in order, with two exceptions. Picking a survey reason with an attached offer routes the flow to that offer next, skipping any intervening steps. Accepting an offer advances directly to success.

Offers

An offer is a value proposition shown during the flow to keep the customer. Attach one to a survey reason so it appears when the customer picks that reason, or declare an OfferStep directly in steps to show one up front.

TypeWhat it captures
discountA coupon: percent or amount off, fixed-duration or recurring.
pauseA subscription hold for a fixed window.
plan_changeA step-down to a smaller plan.
trial_extensionAdditional trial days.
contactA link to support: email, phone, or chat.
redirectA link to a custom URL.
CustomAny string type paired with a customComponents entry.

Discount offers have two kinds of fields. Identity fields like couponId are what your billing system uses to apply the discount. Display fields like percentOff and durationInMonths are what the SDK shows the customer. The SDK doesn't verify the two against each other, so you can either reference an existing coupon (set couponId) or build it on the fly inside handleDiscount from the display fields (omit couponId).

{
  type: 'survey',
  reasons: [{
    id: 'expensive',
    label: 'Too expensive',
    offer: {
      type: 'discount',
      couponId: 'STRIPE_SAVE20',     // identity: passed to handleDiscount
      percentOff: 20,                // display: shown to the customer
      durationInMonths: 3,           // display: shown to the customer
    },
  }],
}

Handlers and listeners

The SDK separates the code that performs an action from the code that reacts to it.

PrefixPurposeRuns
handle<Type>Apply the discount, pause the subscription, cancel the customer.When the customer accepts.
on<Type>Side effects: analytics, toasts, route changes.After the action commits.

handleDiscount applies the discount in your billing system — either your code in open source and analytics modes, or Churnkey's server in connected mode against a connected provider. onDiscount runs after the action commits, for follow-ups like refreshing the customer's billing page or firing an analytics event.

Putting analytics inside handleDiscount would delay every transition behind a slow third-party request. Putting the billing call inside onDiscount would let the success screen render before the discount applied, and a failure would leave the flow inconsistent with billing state. Keeping them separate prevents both.

<CancelFlow
  session={token}                              // connected mode
  handleDiscount={undefined}                   // Churnkey applies the discount
  handlePlanChange={async (offer, customer) => // your code applies plan change
    myBilling.changePlan(offer.plans[0].id)
  }
  onAccept={(offer) =>
    analytics.track('cancel_flow.offer_accepted', { type: offer.type })
  }
/>

A handler that throws aborts the transition. The customer stays on the offer step with error set and can press the action again. No listener fires, since no action committed.

Integration levels

The SDK runs at three integration levels, selected by which props you pass. Each level is a superset of the one below.

LevelRequired propsSessions recordedChurnkey applies actions
Open sourcestepsnono
Analyticssteps, appId, customeryesno
ConnectedappId, sessionyesyes (per-offer opt-out via handle<Type>)

At the open-source level, the SDK runs entirely in your app. Your code defines every step, your handlers run every action, and nothing leaves the client.

Analytics adds two props: appId (your Churnkey workspace, free account) and customer (the user). The SDK reports every session to Churnkey. The dashboard shows reasons picked, offers converted, and save rate across the population. Your handlers still do all the billing work.

Connected adds a server-signed session token and a billing provider (Stripe, Chargebee, etc.) connected in the Churnkey dashboard. The SDK fetches the flow configuration from Churnkey — so the No-Code Editor can change the flow without a redeploy — and runs billing actions through the connected provider on accept. The Intelligence tier adds Adaptive Offers, an AI model that picks the offer for each session in place of static rules. Defining handle<Type> for a specific offer overrides Churnkey's server action for that type only.

See Integration levels for the upgrade walkthrough.

What runs where

The SDK is intentionally thin. Adaptive Offers, Feedback AI, Automatic AI Translations, segment-specific variants, and A/B tests all run on Churnkey's servers, gated behind the session token.

CapabilityClient (SDK)Server (connected mode)
Step navigation and renderingyes
Capturing reasons and feedbackyes
Recording session outcomesyes
Offer selection (segments, A/B, eligibility, cooldowns)yes
Adaptive Offers (Intelligence tier)yes
Applying billing actionsoptional via handle<Type>yes by default
Feedback AI, webhooksyes

Upgrading from open source to connected adds the session token on <CancelFlow> and a token-minting endpoint on your server. The component API doesn't change.

Next steps

  • Quickstart — render a working flow.
  • Integration levels — the open-source → analytics → connected ladder, with code for each.
  • Showcase — every built-in step type and the drop-in recipes.