windback-node
v0.2.1
Published
Official Node.js SDK for Windback — AI-powered churn recovery for SaaS
Downloads
197
Maintainers
Readme
windback-node
Official Node.js SDK for Windback — AI-powered churn recovery for SaaS.
Install
npm install windback-nodeQuick Start
import { Windback } from "windback-node"
const pb = new Windback("sk_live_your_api_key")
// Track a churn event when a customer cancels
await pb.trackChurn({
email: "[email protected]",
name: "Jane Doe",
provider: "stripe",
planName: "Pro",
mrr: 4900, // $49.00 in cents
currency: "USD",
tenureDays: 120,
cancelReason: "too_expensive",
cancelReasonText: "Costs more than our budget allows",
})Usage
Initialize
// Simple — just pass API key
const pb = new Windback("sk_live_your_api_key")
// With options
const pb = new Windback({
apiKey: "sk_live_your_api_key",
baseUrl: "https://api.windbackai.com", // default
timeout: 10000, // 10s default
retries: 2, // retry on 5xx errors
})Track Churn
Call this wherever your cancellation logic runs. Windback stores the event; use
generateVariants + sendVariant to trigger the winback email manually, or pass
cancelReason and call reportCancelReason to trigger it automatically.
const event = await pb.trackChurn({
email: "[email protected]", // required
provider: "stripe", // required — see providers below
mrr: 4900, // required, in cents
name: "Jane Doe", // optional
planName: "Pro", // optional
currency: "USD", // optional, default "USD"
tenureDays: 120, // optional
lastActiveAt: "2026-02-10T12:30:00Z", // optional, ISO 8601
cancelReason: "too_expensive", // optional — see cancel reasons below
cancelReasonText: "Too costly for our stage", // optional, free text
notes: "Requested downgrade first", // optional, internal notes
})
// returns { id, status, customerEmail, ... }Cancel Reason — Two Integration Patterns
Pattern A — All-in-one (cancel happens in real time)
Use this when the customer fills in the cancel reason on YOUR platform at the moment they cancel. Windback creates the event, picks the best winback strategy for the reason, and sends the email automatically.
await pb.submitCancelFlow({
email: "[email protected]", // required
provider: "stripe", // required
mrr: 4900, // required, in cents
currency: "USD", // required
cancelReason: "too_expensive", // required
cancelReasonText: "It's more than our budget", // optional
name: "Jane Doe",
planName: "Pro",
tenureDays: 120,
})
// Email is sent automatically. Returns { churnEventId, status, message }Pattern B — Webhook first, survey later
Use this when your payment provider (Stripe, Dodo, Razorpay) fires a webhook that Windback picks up automatically to create the churn event — and your cancel survey collects the reason separately (e.g. shown after the customer confirms cancellation).
// Step 1 — Windback creates the event from the payment webhook automatically.
// Your webhook handler just needs to be set up in the Windback dashboard.
// Step 2 — Once your cancel survey captures the reason, call:
await pb.reportCancelReason(event.id, {
cancelReason: "missing_features",
cancelReasonText: "We need Salesforce integration",
})
// Windback updates the event and sends the targeted winback email automatically.Cancel Reason Values
| Value | When to use |
|---|---|
| too_expensive | Price is the blocker |
| missing_features | They need something you don't have |
| not_using_enough | Low engagement / forgot about it |
| switching_competitor | Moving to a competing product |
| technical_issues | Bugs, downtime, or integration problems |
| poor_support | Unhappy with support experience |
| dont_need_anymore | Use case is gone (company pivot, etc.) |
| other | None of the above — use cancelReasonText |
Windback maps each reason to the best winback email strategy automatically:
| Reason | Email strategy |
|---|---|
| too_expensive | Discount offer |
| missing_features | Highlight unused / upcoming features |
| not_using_enough | Value recap |
| switching_competitor | Social proof |
| technical_issues | Pain point fix + apology |
| poor_support | Founder personal email |
| dont_need_anymore | Pause option |
| other | Feedback request |
Supported Providers
stripe | razorpay | paypal | wise | paddle | polar | dodo | chargebee | lemonsqueezy | custom
Other Methods
// Get a churn event
const event = await pb.getEvent("event_id")
// Generate AI recovery email variants manually
const result = await pb.generateVariants("event_id")
// Send a specific variant manually
await pb.sendVariant("event_id", "variant_id")Error Handling
import { Windback, WindbackError } from "windback-node"
try {
await pb.trackChurn({ ... })
} catch (error) {
if (error instanceof WindbackError) {
console.error(error.message) // "Windback: invalid API key"
console.error(error.status) // 401
}
}License
MIT
