@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 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.2",
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",