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

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 Stripechurncut_app_id- Your ChurnCut App IDexpiration_timestamp- Unix timestamp for expiration
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:
<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:
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:
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:
Pro Plan
$49/month · Renews Feb 28, 2026
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:
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.
