Integration docs

Add churn prevention to your subscription app.

Connect Stripe, install the React SDK, launch adaptive cancel flows, recover failed payments, and send the customer signals Churnless needs to protect MRR.

Typical setup time
5 min
Create Churnless and Stripe keys
15 min
Place the cancel flow in billing settings
20 min
Add payment wall and product signal events
01

Create keys

Use a browser-safe publishable key in your app and a secret key only from your server.

02

Connect Stripe

Paste a restricted Stripe key so Churnless can read products, customers, subscriptions, invoices, and payment failures.

03

Install the SDK

Wrap your app with the provider, then drop the cancel flow where cancellation starts.

04

Send signals

Track usage, feedback, support, and lifecycle events so the save path can adapt to the customer.

Step 1

Create the right keys.

Churnless uses two key types. The publishable key can run in the browser. The secret key is only for server-side API calls, MCP, jobs, and customer imports.

Publishable key

Use `fk_pub_...` with the React SDK and pixel. It can start cancel flows and check payment state for known customers.

Secret key

Use `fk_live_...` only on your server. Never expose it to client bundles, mobile apps, or public config.

Stripe restricted key

In Churnless settings, paste a restricted Stripe key with the prefilled least-privilege permissions. Churnless uses read scopes to import customers, products, prices, subscriptions, invoices, and charges, plus limited write scopes for save actions such as pauses, discounts, and cancellation at period end. Start in test mode, run the import, then switch to your live Stripe key when the mapping looks right.

Step 2

Install the React SDK.

The SDK handles API calls, modal state, lifecycle events, and optional pixel forwarding. Use it anywhere your customer can manage billing.

Install
bash
npm install @furnkey/react
Provider
tsx
import { ChurnlessProvider } from "@furnkey/react";

export function AppProviders({ children, customer }) {
  return (
    <ChurnlessProvider
      publishableKey={process.env.NEXT_PUBLIC_CHURNLESS_KEY}
      customerId={customer.stripeCustomerId}
      productId="prod_adscreator"
      trackingConsent={customer.analyticsConsent}
    >
      {children}
    </ChurnlessProvider>
  );
}
Step 3

Replace the raw cancel button.

Pass the same customer ID you use in Stripe. Churnless starts a session, asks for the reason, chooses the next best step, and reports the outcome.

Cancel flow
tsx
import { CancelFlow } from "@furnkey/react";

export function BillingSettings({ customer }) {
  return (
    <CancelFlow
      customerId={customer.stripeCustomerId}
      onSaved={() => window.location.reload()}
      onCanceled={() => window.location.assign("/goodbye")}
      onError={(error) => console.error(error)}
    >
      {({ show, isLoading }) => (
        <button onClick={show} disabled={isLoading}>
          {isLoading ? "Loading..." : "Cancel subscription"}
        </button>
      )}
    </CancelFlow>
  );
}
Step 4

Block involuntary churn without blocking recovery.

Use the payment wall around authenticated app content. Customers with unresolved failures see a clear path to update payment details; everyone else sees the app.

Payment recovery wall
tsx
import { PaymentRecoveryWall } from "@furnkey/react";

export function ProtectedApp({ customer, children }) {
  return (
    <PaymentRecoveryWall
      customerId={customer.stripeCustomerId}
      fallback={children}
      pollInterval={60000}
    />
  );
}
Step 5

Send the product signals that predict churn.

The better the signal history, the better the cancellation path. Track meaningful usage, activation, support friction, billing friction, and feedback.

Browser pixel
html
<script>
  window.fk = window.fk || function(){(window.fk.q = window.fk.q || []).push(arguments)};
  fk("init", "fk_pub_...", {
    customerId: "cus_123",
    productId: "prod_adscreator",
    consent: true
  });
  fk("track", "feature_used", {
    feature: "campaign_export",
    value: 1
  });
</script>
<script async src="https://www.getchurnless.com/pixel/v1.js"></script>
Server event
ts
await fetch("https://www.getchurnless.com/api/v1/events", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": process.env.CHURNLESS_API_KEY
  },
  body: JSON.stringify({
    customerId: "cus_123",
    productId: "prod_adscreator",
    eventType: "feature_used",
    eventName: "campaign_exported",
    category: "usage",
    payload: {
      exportCount: 3,
      campaignId: "camp_789"
    }
  })
});

Usage

feature_used, project_created, export_completed

Lifecycle

trial_started, activation_completed, team_invited

Friction

support_ticket_opened, error_seen, downgrade_viewed

Named product actions for session proof

For Friction Radar product issues, emit allowlisted action names so the AI PM can cite product intent (not only rage clicks). Prefer churnless_action tags in recording, or track with stable names like billing.portal_open, checkout.submit, onboarding.invite_sent, and error.payment_failed. Names must match ^[a-zA-Z][a-zA-Z0-9_.:-]{0,99}$. Settings → Products shows your instrumentation score.

Optional

Replay consented journeys without exposing sensitive data.

Session recording is available on every plan, but it is disabled for every product until you enable it in Settings → Products. The existing trackingConsent value remains the only end-user consent control.

Privacy modes

Balanced masks every form value while keeping ordinary page text for journey context. With the separate prompt-capture opt-in enabled in recording settings, recognized prompt and composer values are recorded verbatim; sensitive fields stay masked regardless.

Strict masks all form values and ordinary page text.

Both modes exclude canvas pixels, inline images, fonts, console output, and cross-origin iframe contents.

Mark sensitive regions

Add data-churnless-prompt to a prompt composer that should be readable in Balanced replay when prompt capture is opted in. Use rr-mask, rr-block, and rr-ignore, or the equivalent data-churnless-mask, data-churnless-block, and data-churnless-ignore attributes, to override capture for sensitive areas.

Sampling is deterministic per session. The recorder downloads only after product enablement, analytics consent, Do Not Track, and sampling checks pass. Replays stay private and expire after 30 days.

Advanced

Use the API and MCP server for automation.

Server-side integrations can import customers, push events, inspect health scores, list cancel sessions, and let internal AI tools query churn context.

Reference docs

OpenAPI reference

Browse every REST endpoint, schema, and request example in the generated API reference.

MCP server
bash
CHURNLESS_API_KEY=fk_live_...
npx -p @furnkey/mcp-server churnless-mcp
Integration checklist

Ready to test with a real customer?

Create keys, connect Stripe in test mode, install the SDK, open the cancel flow from your billing page, and send at least one usage event for the same customer ID.