@churndown/sdk
v0.0.7
Published
Official Churndown SDK — identify users and track key actions
Downloads
51
Maintainers
Readme
@churndown/sdk
Official SDK for Churndown — predict which users are about to churn so you can intervene.
Install
npm install @churndown/sdkQuick start
import Churndown from "@churndown/sdk"
const churndown = new Churndown("cd_YOUR_API_KEY")
// Identify a user (call on sign up and every login)
await churndown.identify({
userId: "user_123",
email: "[email protected]",
name: "Sam Shore",
})
// Track a key action
await churndown.track("user_123", "send-message")That's it — two calls and you're done.
API
new Churndown(apiKey, config?)
Create a new client instance.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| apiKey | string | Yes | Your API key (starts with cd_) |
| config.baseUrl | string | No | Override the API URL. Defaults to https://churndown.sh/api |
churndown.identify(params)
Identify a user. Call this when a user signs up or logs in. Safe to call multiple times — it creates the user on the first call and updates on subsequent calls.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| userId | string | Yes | Your internal user ID |
| email | string | Yes | User's email address |
| name | string | No | User's display name |
| image | string | No | URL to user's avatar |
| properties | Record<string, unknown> | No | Any additional user data |
| timestamp | string | No | ISO 8601 signup timestamp (defaults to now) |
await churndown.identify({
userId: "user_123",
email: "[email protected]",
name: "Sam Shore",
image: "https://example.com/avatar.jpg",
properties: {
plan: "pro",
company: "Acme Inc",
},
})churndown.track(userId, event, options?)
Track a key action. Call this each time a user performs the action that signals they're active.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| userId | string | Yes | The user's ID (same as in identify) |
| event | string | Yes | Event name |
| options.properties | Record<string, unknown> | No | Any additional event data |
| options.timestamp | string | No | ISO 8601 timestamp (defaults to now) |
await churndown.track("user_123", "send-message", {
properties: { channel: "general", messageLength: 142 },
})
// With a historical timestamp
await churndown.track("user_123", "send-message", {
timestamp: "2026-01-15T10:30:00Z",
})churndown.importEvents(events)
Import historical events to backfill data. Each event must include a timestamp.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| events[].userId | string | Yes | The user's ID |
| events[].event | string | Yes | Event name |
| events[].timestamp | string | Yes | ISO 8601 timestamp |
| events[].properties | Record<string, unknown> | No | Any additional event data |
await churndown.importEvents([
{ userId: "user_123", event: "send-message", timestamp: "2026-01-15T10:30:00Z" },
{ userId: "user_123", event: "send-message", timestamp: "2026-02-01T14:22:00Z" },
{ userId: "user_456", event: "send-message", timestamp: "2026-02-10T09:15:00Z" },
])Frameworks
Works everywhere — server-side, client-side, edge functions, serverless. The SDK is just a thin fetch wrapper with no dependencies.
Next.js (App Router)
// app/api/auth/callback/route.ts
import Churndown from "@churndown/sdk"
const churndown = new Churndown(process.env.CHURNDOWN_API_KEY!)
export async function GET(request: Request) {
const user = await getUser(request)
await churndown.identify({
userId: user.id,
email: user.email,
name: user.name,
})
// ...
}Express
import Churndown from "@churndown/sdk"
const churndown = new Churndown(process.env.CHURNDOWN_API_KEY!)
app.post("/api/messages", async (req, res) => {
// ... create message ...
await churndown.track(req.user.id, "send-message")
res.json({ ok: true })
})License
MIT
