ChurnCut/Docs

Quickstart

Integrate ChurnCut into your application in minutes and start retaining customers.

Prerequisites

  • An active Stripe account with subscriptions (live or test)
  • Your App ID and Secret Key from ChurnCut (available in Settings)
  • Ability to add a JavaScript snippet to your web app
  • A developer available for HMAC authentication setup
ChurnCut Settings page showing where to find your Secret Key and Organization ID under the API access section
Find your Secret Key and Organization ID in Settings → API access

Step 1: Generate HMAC signature

To keep the cancellation flow secure and accessible only to your authenticated users, you need to generate an HMAC token on your backend.

The token is generated by concatenating:

  • external_user_id - Customer ID from Stripe
  • churncut_app_id - Your ChurnCut App ID
  • expiration_timestamp - Unix timestamp for expiration
javascript
const crypto = require('crypto');

function generateChurncutToken(secretKey, customerId, appId, expirationTimestamp) {
  const message = customerId + appId + expirationTimestamp;

  return crypto
    .createHmac('sha256', secretKey)
    .update(message)
    .digest('hex');
}

// Example usage
const secretKey = "your-secret-key";
const customerId = "cus_XXXXXXXXXXXXXX";
const appId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
const expirationTimestamp = "1767139200";

const token = generateChurncutToken(secretKey, customerId, appId, expirationTimestamp);
console.log(token);

Important: The HMAC token must be generated on your backend. Never expose your Secret Key on the client.

Step 2: Add the script

Include the ChurnCut script in your web application:

html
<script src="https://app.churncut.com/static/js/churncut.js"></script>

Step 3: Start the cancellation flow

Once you have the HMAC token, you can start the cancellation flow by calling startCancellationFlow:

javascript
startCancellationFlow(
  "cus_XXXXXXXXXXXXXX",    // Stripe customer ID
  "sub_XXXXXXXXXXXXXX",    // Current Stripe subscription ID
  "your-hmac-token",       // HMAC token generated in step 1
  "1767139200",            // Expiration timestamp (same used to generate token)
  "your-app-id"            // Your ChurnCut App ID
);

Complete examples by framework

Choose your framework to see a complete example with cancel button and HMAC token:

jsx
import { useCallback } from 'react';

function CancelButton({ customerId, subscriptionId }) {
  const handleCancel = useCallback(async () => {
    // 1. Get HMAC token from your backend
    const res = await fetch(
      \`/api/churncut-token?customerId=\${customerId}&subscriptionId=\${subscriptionId}\`
    );
    const { token, expiration, appId } = await res.json();

    // 2. Start the cancellation flow
    window.startCancellationFlow(
      customerId,
      subscriptionId,
      token,
      expiration,
      appId
    );
  }, [customerId, subscriptionId]);

  return (
    <button onClick={handleCancel} className="cancel-btn">
      Cancel subscription
    </button>
  );
}

Cancel button with subscription preview

A common pattern is to show the user's subscription details with a cancel button. Here's a complete component you can adapt:

Preview

Pro Plan

$49/month · Renews Feb 28, 2026

StatusActive
Payment method•••• 4242
jsx
import { useCallback, useState, useEffect } from 'react';

function SubscriptionSettings({ customerId }) {
  const [subscription, setSubscription] = useState(null);

  useEffect(() => {
    // Fetch subscription details from your backend
    fetch(`/api/subscription?customerId=${customerId}`)
      .then(res => res.json())
      .then(data => setSubscription(data));
  }, [customerId]);

  const handleCancel = useCallback(async () => {
    // 1. Get HMAC token from your backend
    const res = await fetch(
      `/api/churncut-token?customerId=${customerId}&subscriptionId=${subscription.id}`
    );
    const { token, expiration, appId } = await res.json();

    // 2. ChurnCut handles the rest — shows personalized offers,
    //    collects feedback, and only cancels if the user confirms
    window.startCancellationFlow(
      customerId,
      subscription.id,
      token,
      expiration,
      appId
    );
  }, [customerId, subscription]);

  if (!subscription) return <div>Loading...</div>;

  return (
    <div className="subscription-card">
      <h3>{subscription.planName}</h3>
      <p>${subscription.amount}/month · Renews {subscription.renewalDate}</p>

      <div className="subscription-details">
        <div><span>Status</span><span>{subscription.status}</span></div>
        <div><span>Payment</span><span>•••• {subscription.last4}</span></div>
      </div>

      <div className="subscription-actions">
        <button onClick={() => navigate('/change-plan')}>
          Change plan
        </button>
        <button onClick={handleCancel} className="cancel-btn">
          Cancel subscription
        </button>
      </div>
    </div>
  );
}

Tip: ChurnCut handles showing personalized offers, collecting feedback, and only cancels if the user confirms. You just need to call startCancellationFlow.

Alternative method: Cancellation by email

If you prefer to identify users by email instead of Stripe IDs, you can use this variant:

javascript
startCancellationFlow(
  null,                       // No customer ID
  null,                       // No subscription ID
  "your-hmac-token",          // HMAC token
  "1767139200",               // Expiration timestamp
  "your-app-id",              // Your App ID
  "user@example.com"          // User email
);

Warning: Use this method only if each user has a unique email in Stripe and users only have one active subscription at a time. If these conditions are not met, you may experience unexpected behavior.

Next steps