@productcraft/rally 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,180 @@
1
+ # @productcraft/rally
2
+
3
+ Typed Node.js SDK for [ProductCraft Rally](https://productcraft.co) — waitlist management: public-form signups, variants for A/B/n landing pages, referrals, position + leaderboard, approval workflow with invite-to-app, signed outbound webhooks, CSV export.
4
+
5
+ ```bash
6
+ npm install @productcraft/rally
7
+ ```
8
+
9
+ The waitlist-entries endpoint can be called unauthenticated from a marketing-site form. Every other endpoint (admin, approval, analytics, exports, webhooks) requires a PlatformUser cookie or a workspace-scoped PAK (`pcft_live_…`).
10
+
11
+ ## Quick start — accept a signup from a public form
12
+
13
+ ```ts
14
+ import { Rally } from "@productcraft/rally";
15
+
16
+ // No auth — public surface
17
+ const rally = new Rally();
18
+
19
+ const { data, error } = await rally.client.POST(
20
+ "/v1/waitlists/{workspace_slug}/{waitlist_slug}/entries",
21
+ {
22
+ params: { path: { workspace_slug: "acme", waitlist_slug: "early-access" } },
23
+ body: {
24
+ email: "alice@example.com",
25
+ name: "Alice",
26
+ referrer: "twitter",
27
+ referral_code: "ada-123", // optional — bumps the referrer's position
28
+ metadata: { plan_interest: "pro" },
29
+ },
30
+ },
31
+ );
32
+ ```
33
+
34
+ The response includes the entry's id + assigned position. Round-trip the `id` into your "thanks" page so the visitor can share their own referral link.
35
+
36
+ If the waitlist has `settings.recaptcha_site_key` set, also pass a `recaptcha_token` from your client side.
37
+
38
+ ## Quick start — workspace-admin (authenticated)
39
+
40
+ ```ts
41
+ import { Rally } from "@productcraft/rally";
42
+
43
+ const rally = new Rally({
44
+ auth: { type: "apiKey", key: process.env.PCFT_KEY! },
45
+ });
46
+
47
+ // Create a waitlist
48
+ const { data } = await rally.client.POST(
49
+ "/v1/workspaces/{workspace_id}/waitlists",
50
+ {
51
+ params: { path: { workspace_id: "ws_..." } },
52
+ body: { name: "Early Access", slug: "early-access" },
53
+ },
54
+ );
55
+ ```
56
+
57
+ `{workspace_id}` is the workspace UUID returned by `@productcraft/platform-auth`'s introspect endpoint.
58
+
59
+ ## Configuration
60
+
61
+ ```ts
62
+ new Rally({
63
+ // Optional: required for workspace-admin calls. Public submit works without auth.
64
+ auth: { type: "apiKey", key: "pcft_live_..." }
65
+ | { type: "bearer", token: "eyJ..." }
66
+ | { type: "cookie", value: "auth_token=..." },
67
+ baseUrl: "https://api.rally.example.test", // optional override
68
+ fetch: customFetch, // optional
69
+ });
70
+ ```
71
+
72
+ ## Common operations
73
+
74
+ ### Public surface
75
+
76
+ ```ts
77
+ // Read the waitlist's public metadata (name, position-window, active variant, ...)
78
+ await rally.client.GET(
79
+ "/v1/waitlists/{workspace_slug}/{waitlist_slug}",
80
+ { params: { ... } },
81
+ );
82
+
83
+ // Read the public leaderboard (when enabled per-waitlist)
84
+ await rally.client.GET(
85
+ "/v1/waitlists/{workspace_slug}/{waitlist_slug}/leaderboard",
86
+ { params: { ... } },
87
+ );
88
+ ```
89
+
90
+ ### Entries (workspace-admin)
91
+
92
+ ```ts
93
+ // List entries
94
+ await rally.client.GET(
95
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries",
96
+ { params: { path: { workspace_id, waitlist_id }, query: { limit: 50 } } },
97
+ );
98
+
99
+ // Count
100
+ await rally.client.GET(
101
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/count",
102
+ { ... },
103
+ );
104
+
105
+ // Approve / reject in bulk
106
+ await rally.client.POST(
107
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/bulk",
108
+ { params: { ... }, body: { ids: [...], action: "approve" } },
109
+ );
110
+
111
+ // Send an invite (e.g. into a Heimdall app)
112
+ await rally.client.POST(
113
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/{entry_id}/invite-to-app",
114
+ { params: { ... }, body: { /* app + role + invite_template */ } },
115
+ );
116
+
117
+ // Export to CSV
118
+ await rally.client.GET(
119
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/export.csv",
120
+ { ... },
121
+ );
122
+ ```
123
+
124
+ ### Variants (A/B/n landing pages)
125
+
126
+ ```ts
127
+ // Create a variant
128
+ await rally.client.POST(
129
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/variants",
130
+ { params: { ... }, body: { slug: "headline-b", weight: 50 } },
131
+ );
132
+ ```
133
+
134
+ Front-end picks up the active variant from `GET /v1/waitlists/:workspace_slug/:waitlist_slug` and round-trips its id back in the entry submission via the `variant_id` field — Rally computes per-variant conversion without a separate impressions table.
135
+
136
+ ### Analytics
137
+
138
+ ```ts
139
+ // Conversion + per-variant counts
140
+ await rally.client.GET(
141
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/analytics",
142
+ { ... },
143
+ );
144
+
145
+ // Timeseries
146
+ await rally.client.GET(
147
+ "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/analytics/timeline",
148
+ { params: { path: { ... }, query: { bucket: "1d", lookback: "30d" } } },
149
+ );
150
+ ```
151
+
152
+ ### Webhooks
153
+
154
+ ```ts
155
+ // Subscribe to entry.created / entry.approved / entry.rejected
156
+ await rally.client.POST(
157
+ "/v1/workspaces/{workspace_id}/webhooks",
158
+ { params: { ... }, body: { url: "https://yourapp.com/hooks/rally", events: ["entry.created"] } },
159
+ );
160
+
161
+ // Rotate the signing secret without disabling the webhook
162
+ await rally.client.POST(
163
+ "/v1/workspaces/{workspace_id}/webhooks/{id}/rotate-secret",
164
+ { ... },
165
+ );
166
+
167
+ // Replay a delivery from history
168
+ await rally.client.GET(
169
+ "/v1/workspaces/{workspace_id}/webhooks/{id}/deliveries",
170
+ { ... },
171
+ );
172
+ ```
173
+
174
+ ## How this SDK is built
175
+
176
+ Generated from the live OpenAPI spec at `https://api.rally.productcraft.co/docs-json` via [`openapi-typescript`](https://openapi-ts.dev/) + [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/). The nightly `spec-refresh` workflow opens a PR whenever the spec changes.
177
+
178
+ ## License
179
+
180
+ [MIT](https://github.com/clauderanelagh/productcraft-node/blob/main/LICENSE).