@churndown/sdk 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +4 -0
- package/dist/index.mjs +4 -0
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @churndown/sdk
|
|
2
|
+
|
|
3
|
+
Official SDK for [Churndown](https://churndown.sh) — predict which users are about to churn so you can intervene.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @churndown/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import Churndown from "@churndown/sdk"
|
|
15
|
+
|
|
16
|
+
const churndown = new Churndown("cd_YOUR_API_KEY")
|
|
17
|
+
|
|
18
|
+
// Identify a user (call on sign up and every login)
|
|
19
|
+
await churndown.identify({
|
|
20
|
+
userId: "user_123",
|
|
21
|
+
email: "sam@example.com",
|
|
22
|
+
name: "Sam Shore",
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// Track a key action
|
|
26
|
+
await churndown.track("user_123", "send-message")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
That's it — two calls and you're done.
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
### `new Churndown(apiKey, config?)`
|
|
34
|
+
|
|
35
|
+
Create a new client instance.
|
|
36
|
+
|
|
37
|
+
| Parameter | Type | Required | Description |
|
|
38
|
+
|-----------|------|----------|-------------|
|
|
39
|
+
| `apiKey` | `string` | Yes | Your API key (starts with `cd_`) |
|
|
40
|
+
| `config.baseUrl` | `string` | No | Override the API URL. Defaults to `https://churndown.sh/api` |
|
|
41
|
+
|
|
42
|
+
### `churndown.identify(params)`
|
|
43
|
+
|
|
44
|
+
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.
|
|
45
|
+
|
|
46
|
+
| Parameter | Type | Required | Description |
|
|
47
|
+
|-----------|------|----------|-------------|
|
|
48
|
+
| `userId` | `string` | Yes | Your internal user ID |
|
|
49
|
+
| `email` | `string` | Yes | User's email address |
|
|
50
|
+
| `name` | `string` | No | User's display name |
|
|
51
|
+
| `image` | `string` | No | URL to user's avatar |
|
|
52
|
+
| `properties` | `Record<string, unknown>` | No | Any additional user data |
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
await churndown.identify({
|
|
56
|
+
userId: "user_123",
|
|
57
|
+
email: "sam@example.com",
|
|
58
|
+
name: "Sam Shore",
|
|
59
|
+
image: "https://example.com/avatar.jpg",
|
|
60
|
+
properties: {
|
|
61
|
+
plan: "pro",
|
|
62
|
+
company: "Acme Inc",
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `churndown.track(userId, event, properties?)`
|
|
68
|
+
|
|
69
|
+
Track a key action. Call this each time a user performs the action that signals they're active.
|
|
70
|
+
|
|
71
|
+
| Parameter | Type | Required | Description |
|
|
72
|
+
|-----------|------|----------|-------------|
|
|
73
|
+
| `userId` | `string` | Yes | The user's ID (same as in `identify`) |
|
|
74
|
+
| `event` | `string` | Yes | Event name |
|
|
75
|
+
| `properties` | `Record<string, unknown>` | No | Any additional event data |
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
await churndown.track("user_123", "send-message", {
|
|
79
|
+
channel: "general",
|
|
80
|
+
messageLength: 142,
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `churndown.importEvents(events)`
|
|
85
|
+
|
|
86
|
+
Import historical events to backfill data. Each event must include a `timestamp`.
|
|
87
|
+
|
|
88
|
+
| Parameter | Type | Required | Description |
|
|
89
|
+
|-----------|------|----------|-------------|
|
|
90
|
+
| `events[].userId` | `string` | Yes | The user's ID |
|
|
91
|
+
| `events[].event` | `string` | Yes | Event name |
|
|
92
|
+
| `events[].timestamp` | `string` | Yes | ISO 8601 timestamp |
|
|
93
|
+
| `events[].properties` | `Record<string, unknown>` | No | Any additional event data |
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
await churndown.importEvents([
|
|
97
|
+
{ userId: "user_123", event: "send-message", timestamp: "2026-01-15T10:30:00Z" },
|
|
98
|
+
{ userId: "user_123", event: "send-message", timestamp: "2026-02-01T14:22:00Z" },
|
|
99
|
+
{ userId: "user_456", event: "send-message", timestamp: "2026-02-10T09:15:00Z" },
|
|
100
|
+
])
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Frameworks
|
|
104
|
+
|
|
105
|
+
Works everywhere — server-side, client-side, edge functions, serverless. The SDK is just a thin `fetch` wrapper with no dependencies.
|
|
106
|
+
|
|
107
|
+
### Next.js (App Router)
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// app/api/auth/callback/route.ts
|
|
111
|
+
import Churndown from "@churndown/sdk"
|
|
112
|
+
|
|
113
|
+
const churndown = new Churndown(process.env.CHURNDOWN_API_KEY!)
|
|
114
|
+
|
|
115
|
+
export async function GET(request: Request) {
|
|
116
|
+
const user = await getUser(request)
|
|
117
|
+
|
|
118
|
+
await churndown.identify({
|
|
119
|
+
userId: user.id,
|
|
120
|
+
email: user.email,
|
|
121
|
+
name: user.name,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// ...
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Express
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import Churndown from "@churndown/sdk"
|
|
132
|
+
|
|
133
|
+
const churndown = new Churndown(process.env.CHURNDOWN_API_KEY!)
|
|
134
|
+
|
|
135
|
+
app.post("/api/messages", async (req, res) => {
|
|
136
|
+
// ... create message ...
|
|
137
|
+
|
|
138
|
+
await churndown.track(req.user.id, "send-message")
|
|
139
|
+
|
|
140
|
+
res.json({ ok: true })
|
|
141
|
+
})
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -14,13 +14,22 @@ interface TrackParams {
|
|
|
14
14
|
event: string;
|
|
15
15
|
properties?: Record<string, unknown>;
|
|
16
16
|
}
|
|
17
|
+
interface ImportEvent {
|
|
18
|
+
userId: string;
|
|
19
|
+
event: string;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
properties?: Record<string, unknown>;
|
|
22
|
+
}
|
|
17
23
|
declare class Churndown {
|
|
18
24
|
private apiKey;
|
|
19
25
|
private baseUrl;
|
|
20
26
|
constructor(apiKey: string, config?: Omit<ChurndownConfig, "apiKey">);
|
|
21
27
|
identify(params: IdentifyParams): Promise<void>;
|
|
22
28
|
track(userId: string, event: string, properties?: Record<string, unknown>): Promise<void>;
|
|
29
|
+
importEvents(events: ImportEvent[]): Promise<{
|
|
30
|
+
imported: number;
|
|
31
|
+
}>;
|
|
23
32
|
private post;
|
|
24
33
|
}
|
|
25
34
|
|
|
26
|
-
export { Churndown, type ChurndownConfig, type IdentifyParams, type TrackParams, Churndown as default };
|
|
35
|
+
export { Churndown, type ChurndownConfig, type IdentifyParams, type ImportEvent, type TrackParams, Churndown as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -14,13 +14,22 @@ interface TrackParams {
|
|
|
14
14
|
event: string;
|
|
15
15
|
properties?: Record<string, unknown>;
|
|
16
16
|
}
|
|
17
|
+
interface ImportEvent {
|
|
18
|
+
userId: string;
|
|
19
|
+
event: string;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
properties?: Record<string, unknown>;
|
|
22
|
+
}
|
|
17
23
|
declare class Churndown {
|
|
18
24
|
private apiKey;
|
|
19
25
|
private baseUrl;
|
|
20
26
|
constructor(apiKey: string, config?: Omit<ChurndownConfig, "apiKey">);
|
|
21
27
|
identify(params: IdentifyParams): Promise<void>;
|
|
22
28
|
track(userId: string, event: string, properties?: Record<string, unknown>): Promise<void>;
|
|
29
|
+
importEvents(events: ImportEvent[]): Promise<{
|
|
30
|
+
imported: number;
|
|
31
|
+
}>;
|
|
23
32
|
private post;
|
|
24
33
|
}
|
|
25
34
|
|
|
26
|
-
export { Churndown, type ChurndownConfig, type IdentifyParams, type TrackParams, Churndown as default };
|
|
35
|
+
export { Churndown, type ChurndownConfig, type IdentifyParams, type ImportEvent, type TrackParams, Churndown as default };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,10 @@ var Churndown = class {
|
|
|
39
39
|
async track(userId, event, properties) {
|
|
40
40
|
await this.post("/v1/track", { userId, event, properties });
|
|
41
41
|
}
|
|
42
|
+
async importEvents(events) {
|
|
43
|
+
const res = await this.post("/v1/import", { events });
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
42
46
|
async post(path, body) {
|
|
43
47
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
44
48
|
method: "POST",
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,10 @@ var Churndown = class {
|
|
|
14
14
|
async track(userId, event, properties) {
|
|
15
15
|
await this.post("/v1/track", { userId, event, properties });
|
|
16
16
|
}
|
|
17
|
+
async importEvents(events) {
|
|
18
|
+
const res = await this.post("/v1/import", { events });
|
|
19
|
+
return res;
|
|
20
|
+
}
|
|
17
21
|
async post(path, body) {
|
|
18
22
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
19
23
|
method: "POST",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@churndown/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Official Churndown SDK — identify users and track key actions",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
-
"dist"
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|
|
18
19
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|