Docs
Intercept cancellations with an AI-personalized save flow. Pick the method that matches your stack — most teams are live in a few minutes. The fastest path is below.
Start with the quickstartPick your integration
Five ways to add the save flow — jump straight to the one that fits your stack.
Point your Cancel button at a hosted flow URL.
Jump to sectionOne script tag plus two button attributes.
Jump to sectionTyped, keyless <CancelGuard> for any app.
Jump to sectionFlows, sessions and recovery stats.
Jump to sectionWire it up from Claude or Cursor with one prompt.
Jump to sectionEvery endpoint, auth, request/response shapes and error codes.
Open API referenceGet started
Three steps to a working save flow:
Every cancellation then runs your flow, personalizes the offer, and records the outcome under Sessions.
No code
The simplest option: point your existing “Cancel” button or link at the hosted flow page. Append ?email= so the AI can tailor the offer and apply discounts to the right customer.
<a href="https://retainrabbit.com/flow/YOUR_WORKSPACE_ID/YOUR_FLOW_ID?email=CUSTOMER_EMAIL">
Cancel subscription
</a>Any site
Add the script once, then put two attributes on your existing Cancel button. We open the flow in a popup, personalize the offer, and — if the customer still cancels — let your button do exactly what it did before.
<!-- 1. Add once, before </body> -->
<script src="https://retainrabbit.com/embed.js"
data-workspace="YOUR_WORKSPACE_ID"
data-flow="YOUR_FLOW_ID"></script>
<!-- 2. On your existing Cancel button -->
<button data-retainrabbit-cancel data-email="CUSTOMER_EMAIL">
Cancel subscription
</button>Prefer an in-page modal? Add data-mode="overlay" to the script tag. Cancel wired up in JavaScript? Call RetainRabbit.open({ email, onCancel }) from your handler instead.
npm
For React / Next.js apps. Wrap your app once, then wrap your Cancel button with <CancelGuard> — it intercepts the click, runs the flow, and only lets the real cancel through if the customer declines.
pnpm add @retainrabbit/reactimport { RetainRabbitProvider, CancelGuard } from "@retainrabbit/react";
<RetainRabbitProvider origin="https://retainrabbit.com" workspaceId="YOUR_WORKSPACE_ID">
{/* …your app… */}
</RetainRabbitProvider>
// Around your existing cancel button:
<CancelGuard flowId="YOUR_FLOW_ID" email={currentUser.email}>
<button>Cancel subscription</button>
</CancelGuard>The @retainrabbit npm packages publish under that scope — if pnpm addcan't find them yet, they're mid-release.
npm
Framework-agnostic, fully typed, no script tag. Guard a selector and you're done.
pnpm add @retainrabbit/jsimport { createRetainRabbit } from "@retainrabbit/js";
const rr = createRetainRabbit({ origin: "https://retainrabbit.com", workspaceId: "YOUR_WORKSPACE_ID" });
rr.guard("#cancel-btn", { flowId: "YOUR_FLOW_ID", email: currentUser.email });The @retainrabbit npm packages publish under that scope — if pnpm addcan't find them yet, they're mid-release.
Server-side
For programmatic use and backends. Mint a key in Integrations → API keys and send it as a bearer token. Read keys cover the GET endpoints; read+write also allows POST /sessions.
GET /flows — list flowsGET /flows/:id — one flow + steps + hostedUrlGET /sessions/:id — poll a session's outcomeGET /stats — recovery summary (recovered MRR, save rate)POST /sessions — start a session (read+write)curl -H "authorization: Bearer rk_live_…" \
https://retainrabbit.com/api/v1/statsAI-native
Connect RetainRabbit to your AI coding assistant (Claude Desktop, Cursor, Claude Code). It can list flows, read recovery stats, and — with get_install_snippet — write the integration into your codebase for you. Add this to your client's MCP config:
{
"mcpServers": {
"retainrabbit": {
"command": "npx",
"args": ["-y", "@retainrabbit/mcp"],
"env": {
"RETAINRABBIT_API_KEY": "rk_live_…",
"RETAINRABBIT_BASE_URL": "https://retainrabbit.com/api/v1"
}
}
}
}The @retainrabbit npm packages publish under that scope — if pnpm addcan't find them yet, they're mid-release.
Trust
By default the customer's email is trusted from your front end. To stop anyone passing an arbitrary email to grab a discount, sign the email server-side and turn on Require verified identity in Save policy. Your embed secret lives in Integrations.
import crypto from "crypto";
const verify = crypto
.createHmac("sha256", process.env.RETAINRABBIT_EMBED_SECRET)
.update(customerEmail.trim().toLowerCase())
.digest("hex");
// pass as data-verify={verify} (script) or verify={verify} (SDK)Key safety: rk_live_ API keys are server-side only — never put one in browser code. The script embed and the JS/React SDKs are keyless by design (they use your workspace id + the optional signed email), so the only place a key belongs is your backend or the MCP server.