@apex-inc/sdk 0.8.0 → 0.10.0
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 +15 -0
- package/dist/esm/generated/event-payloads.js +11 -0
- package/dist/esm/generated/trait-types.js +11 -0
- package/dist/esm/management.js +223 -0
- package/dist/generated/event-payloads.d.ts +855 -0
- package/dist/generated/event-payloads.d.ts.map +1 -0
- package/dist/generated/event-payloads.js +13 -0
- package/dist/generated/event-payloads.js.map +1 -0
- package/dist/generated/trait-types.d.ts +135 -0
- package/dist/generated/trait-types.d.ts.map +1 -0
- package/dist/generated/trait-types.js +13 -0
- package/dist/generated/trait-types.js.map +1 -0
- package/dist/index.d.ts +14 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/management.d.ts +140 -0
- package/dist/management.d.ts.map +1 -1
- package/dist/management.js +223 -0
- package/dist/management.js.map +1 -1
- package/package.json +6 -1
- package/skills/apex-integration-cookbook/SKILL.md +190 -1
package/README.md
CHANGED
|
@@ -30,6 +30,21 @@ identify("user_123", { email: "jane@example.com" });
|
|
|
30
30
|
track("page_viewed", { path: "/pricing" });
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
### Profile photos (`avatar_url`)
|
|
34
|
+
|
|
35
|
+
Pass `avatar_url` in traits to show end-user photos across Apex — the
|
|
36
|
+
Customers list, customer detail pages, and the Live Customers widget. It's a
|
|
37
|
+
[canonical attribute](https://app.apex.inc/docs/concepts/schema), so
|
|
38
|
+
`photoUrl` / `image` / `profile_image` map to it too. Apex validates on write
|
|
39
|
+
(https only) and falls back to colored initials when absent.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
identify("user_123", {
|
|
43
|
+
email: "jane@example.com",
|
|
44
|
+
avatar_url: user.photoURL, // OIDC `picture` claim works the same way
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
33
48
|
## PLG primitives
|
|
34
49
|
|
|
35
50
|
In addition to `track` / `identify` the SDK exposes three PLG-specific
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED FILE — DO NOT EDIT BY HAND.
|
|
3
|
+
*
|
|
4
|
+
* Typed Apex event payloads, generated from the Apex Spec registries by
|
|
5
|
+
* `app/scripts/generate-spec-types.ts`. Regenerate with:
|
|
6
|
+
*
|
|
7
|
+
* npx tsx app/scripts/generate-spec-types.ts
|
|
8
|
+
*
|
|
9
|
+
* The `codegen.test.ts` stale gate fails CI if this drifts from the spec.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED FILE — DO NOT EDIT BY HAND.
|
|
3
|
+
*
|
|
4
|
+
* Typed Apex identify() traits, generated from the Apex Spec attribute
|
|
5
|
+
* registry by `app/scripts/generate-spec-types.ts`. Regenerate with:
|
|
6
|
+
*
|
|
7
|
+
* npx tsx app/scripts/generate-spec-types.ts
|
|
8
|
+
*
|
|
9
|
+
* The `codegen.test.ts` stale gate fails CI if this drifts from the spec.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/dist/esm/management.js
CHANGED
|
@@ -28,6 +28,136 @@ export function createManagementClient(cfg) {
|
|
|
28
28
|
}
|
|
29
29
|
return res.json();
|
|
30
30
|
},
|
|
31
|
+
/**
|
|
32
|
+
* "Is our growth real?" — per-goal cumulative lift (journeys + experiments)
|
|
33
|
+
* vs the do-nothing global holdout, with 95% CIs. Forward-only; goals still
|
|
34
|
+
* collecting are flagged `isDecisive: false`. (Is Our Growth Real, Phase 4.)
|
|
35
|
+
*/
|
|
36
|
+
async getGrowthReality(opts) {
|
|
37
|
+
const days = opts?.days ?? "all";
|
|
38
|
+
const res = await fetch(`${root}/api/growth/reality?days=${days}`, { headers });
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const text = await res.text().catch(() => "");
|
|
41
|
+
throw new Error(`Apex getGrowthReality failed (${res.status}): ${text}`);
|
|
42
|
+
}
|
|
43
|
+
return res.json();
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Channel Economics Cockpit (Layer 1) — gross-margin-adjusted LTV:CAC,
|
|
47
|
+
* payback, and per-channel cohort curves by acquisition channel (raw
|
|
48
|
+
* first-touch), plus a directional multi-touch lens. Reads real contacts +
|
|
49
|
+
* spend + revenue; metrics with missing inputs come back null, never
|
|
50
|
+
* fabricated. `days` defaults to all-time. Wraps GET /api/channels/economics.
|
|
51
|
+
*/
|
|
52
|
+
async getChannelEconomics(opts) {
|
|
53
|
+
const days = opts?.days ?? "all";
|
|
54
|
+
const res = await fetch(`${root}/api/channels/economics?days=${days}`, { headers });
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const text = await res.text().catch(() => "");
|
|
57
|
+
throw new Error(`Apex getChannelEconomics failed (${res.status}): ${text}`);
|
|
58
|
+
}
|
|
59
|
+
return res.json();
|
|
60
|
+
},
|
|
61
|
+
// ─── Vertical Widget Packs read surface (2026-07-02) ─────────────────
|
|
62
|
+
// Thin wrappers over the pack read APIs. Param names match the API
|
|
63
|
+
// query params exactly (shipped params ARE the SDK signature). All
|
|
64
|
+
// endpoints require the `analytics:read` API-key scope; responses
|
|
65
|
+
// apply the same small-n suppression as the dashboard widgets.
|
|
66
|
+
/**
|
|
67
|
+
* SaaS pack — MRR / ARR / monthly waterfall (new / expansion /
|
|
68
|
+
* reactivation / contraction / churn) + NRR / GRR for the last
|
|
69
|
+
* complete month, from materialized monthly snapshots (UTC calendar
|
|
70
|
+
* months; the current month is flagged `inProgress`). Wraps
|
|
71
|
+
* GET /api/saas/recurring-revenue.
|
|
72
|
+
*/
|
|
73
|
+
async getSaasRecurringRevenue(opts) {
|
|
74
|
+
const q = opts?.months ? `?months=${opts.months}` : "";
|
|
75
|
+
const res = await fetch(`${root}/api/saas/recurring-revenue${q}`, { headers });
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const text = await res.text().catch(() => "");
|
|
78
|
+
throw new Error(`Apex getSaasRecurringRevenue failed (${res.status}): ${text}`);
|
|
79
|
+
}
|
|
80
|
+
return res.json();
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* SaaS pack — expansion vs contraction MRR, expanded-account counts,
|
|
84
|
+
* and seat growth. Wraps GET /api/saas/expansion.
|
|
85
|
+
*/
|
|
86
|
+
async getSaasExpansion(opts) {
|
|
87
|
+
const q = opts?.months ? `?months=${opts.months}` : "";
|
|
88
|
+
const res = await fetch(`${root}/api/saas/expansion${q}`, { headers });
|
|
89
|
+
if (!res.ok) {
|
|
90
|
+
const text = await res.text().catch(() => "");
|
|
91
|
+
throw new Error(`Apex getSaasExpansion failed (${res.status}): ${text}`);
|
|
92
|
+
}
|
|
93
|
+
return res.json();
|
|
94
|
+
},
|
|
95
|
+
/**
|
|
96
|
+
* DTC pack — per-product funnel (views → adds → purchases → refunds)
|
|
97
|
+
* via the shared item aggregator. Wraps GET /api/dtc/merchandising.
|
|
98
|
+
*/
|
|
99
|
+
async getDtcMerchandising(opts) {
|
|
100
|
+
const q = opts?.days ? `?days=${opts.days}` : "";
|
|
101
|
+
const res = await fetch(`${root}/api/dtc/merchandising${q}`, { headers });
|
|
102
|
+
if (!res.ok) {
|
|
103
|
+
const text = await res.text().catch(() => "");
|
|
104
|
+
throw new Error(`Apex getDtcMerchandising failed (${res.status}): ${text}`);
|
|
105
|
+
}
|
|
106
|
+
return res.json();
|
|
107
|
+
},
|
|
108
|
+
/**
|
|
109
|
+
* DTC pack — refund rate (money back) and return rate (goods back —
|
|
110
|
+
* separate metrics, never summed), reasons, refund cycle time, AOV,
|
|
111
|
+
* repeat purchase. Wraps GET /api/dtc/returns.
|
|
112
|
+
*/
|
|
113
|
+
async getDtcReturns(opts) {
|
|
114
|
+
const q = opts?.days ? `?days=${opts.days}` : "";
|
|
115
|
+
const res = await fetch(`${root}/api/dtc/returns${q}`, { headers });
|
|
116
|
+
if (!res.ok) {
|
|
117
|
+
const text = await res.text().catch(() => "");
|
|
118
|
+
throw new Error(`Apex getDtcReturns failed (${res.status}): ${text}`);
|
|
119
|
+
}
|
|
120
|
+
return res.json();
|
|
121
|
+
},
|
|
122
|
+
/**
|
|
123
|
+
* Marketplace pack — the six pillars (liquidity, supply & demand,
|
|
124
|
+
* economics GMV-vs-take, retention, trust, concentration), sliceable
|
|
125
|
+
* by category / geo / price_band. Low-volume slices are suppressed
|
|
126
|
+
* (k-anonymity floor). Wraps GET /api/marketplace/metrics.
|
|
127
|
+
*/
|
|
128
|
+
async getMarketplaceMetrics(opts) {
|
|
129
|
+
const params = new URLSearchParams();
|
|
130
|
+
if (opts?.days)
|
|
131
|
+
params.set("days", String(opts.days));
|
|
132
|
+
if (opts?.category)
|
|
133
|
+
params.set("category", opts.category);
|
|
134
|
+
if (opts?.geo)
|
|
135
|
+
params.set("geo", opts.geo);
|
|
136
|
+
if (opts?.price_band)
|
|
137
|
+
params.set("price_band", opts.price_band);
|
|
138
|
+
const qs = params.toString();
|
|
139
|
+
const res = await fetch(`${root}/api/marketplace/metrics${qs ? `?${qs}` : ""}`, { headers });
|
|
140
|
+
if (!res.ok) {
|
|
141
|
+
const text = await res.text().catch(() => "");
|
|
142
|
+
throw new Error(`Apex getMarketplaceMetrics failed (${res.status}): ${text}`);
|
|
143
|
+
}
|
|
144
|
+
return res.json();
|
|
145
|
+
},
|
|
146
|
+
/**
|
|
147
|
+
* Agency pack — per-client (= linked workspace) spend, revenue, ROI,
|
|
148
|
+
* MoM series, and the transparent at-risk rule. NOTE: this endpoint is
|
|
149
|
+
* a FINANCIAL boundary gated to org owner/admin SESSIONS (no API-key
|
|
150
|
+
* lane yet — CISO decision 2026-07-02); calling it with an API key
|
|
151
|
+
* returns 401/403. Wraps GET /api/agency/clients.
|
|
152
|
+
*/
|
|
153
|
+
async getClientPortfolio(opts) {
|
|
154
|
+
const res = await fetch(`${root}/api/agency/clients?orgId=${encodeURIComponent(opts.orgId)}`, { headers });
|
|
155
|
+
if (!res.ok) {
|
|
156
|
+
const text = await res.text().catch(() => "");
|
|
157
|
+
throw new Error(`Apex getClientPortfolio failed (${res.status}): ${text}`);
|
|
158
|
+
}
|
|
159
|
+
return res.json();
|
|
160
|
+
},
|
|
31
161
|
async assignVariant(experimentId, visitorId) {
|
|
32
162
|
const cookie = [
|
|
33
163
|
`apex_vid=${encodeURIComponent(visitorId)}`,
|
|
@@ -70,6 +200,99 @@ export function createManagementClient(cfg) {
|
|
|
70
200
|
throw new Error(`Apex promoteExperiment failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
71
201
|
return res.json();
|
|
72
202
|
},
|
|
203
|
+
// ─── Conversions (Goals & Conversions Convergence) ────────────────
|
|
204
|
+
//
|
|
205
|
+
// A Conversion is the merchant's owned-event definition of a valued
|
|
206
|
+
// action ("Purchase", "Signup"). One workspace registry, one
|
|
207
|
+
// `isPrimary`. Experiments and journeys reference a Conversion's
|
|
208
|
+
// owned-event source as their objective; the headline funnel and
|
|
209
|
+
// scoring use the primary one. These wrap the same `/api/conversion-goals`
|
|
210
|
+
// endpoints the dashboard + MCP server use.
|
|
211
|
+
conversions: {
|
|
212
|
+
/** List the workspace's owned-event Conversions (archived excluded by default). */
|
|
213
|
+
async list(opts) {
|
|
214
|
+
const res = await fetch(`${root}/api/conversion-goals`, { headers });
|
|
215
|
+
if (!res.ok)
|
|
216
|
+
throw new Error(`Apex conversions.list failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
217
|
+
const json = (await res.json());
|
|
218
|
+
const all = Array.isArray(json) ? json : [];
|
|
219
|
+
return opts?.includeArchived ? all : all.filter((c) => !c.archivedAt);
|
|
220
|
+
},
|
|
221
|
+
/**
|
|
222
|
+
* Define a new owned-event Conversion. `custom_event` (with `eventName`)
|
|
223
|
+
* is the most common shape and matches an Apex Spec event sent via track.
|
|
224
|
+
* Set `isPrimary` to make it the default experiment objective + headline metric.
|
|
225
|
+
*/
|
|
226
|
+
async create(params) {
|
|
227
|
+
const body = {
|
|
228
|
+
name: params.name,
|
|
229
|
+
type: params.type,
|
|
230
|
+
metricType: "binary",
|
|
231
|
+
isPrimary: params.isPrimary ?? false,
|
|
232
|
+
...(params.eventName ? { eventName: params.eventName } : {}),
|
|
233
|
+
...(params.selector ? { selector: params.selector } : {}),
|
|
234
|
+
...(params.targetUrl ? { targetUrl: params.targetUrl } : {}),
|
|
235
|
+
};
|
|
236
|
+
const res = await fetch(`${root}/api/conversion-goals`, {
|
|
237
|
+
method: "POST", headers, body: JSON.stringify(body),
|
|
238
|
+
});
|
|
239
|
+
if (!res.ok)
|
|
240
|
+
throw new Error(`Apex conversions.create failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
241
|
+
return res.json();
|
|
242
|
+
},
|
|
243
|
+
/** Promote a Conversion to primary (only one can be primary at a time). */
|
|
244
|
+
async setPrimary(conversionId) {
|
|
245
|
+
const res = await fetch(`${root}/api/conversion-goals`, {
|
|
246
|
+
method: "PATCH", headers, body: JSON.stringify({ id: conversionId, isPrimary: true }),
|
|
247
|
+
});
|
|
248
|
+
if (!res.ok)
|
|
249
|
+
throw new Error(`Apex conversions.setPrimary failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
250
|
+
return res.json();
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
// ─── Targets (Goals & Conversions Convergence) ────────────────────
|
|
254
|
+
//
|
|
255
|
+
// A Target is an aggregate KPI number to hit by a date (MRR, pipeline,
|
|
256
|
+
// "1,000 signups by Q3"). Bind one to a Conversion via
|
|
257
|
+
// `sourceConversionId` to auto-count progress from that conversion's
|
|
258
|
+
// owned-event completions; otherwise `current` is a manual number.
|
|
259
|
+
// `currentIsAuto` flags an auto-computed value; `sourceMissing` flags a
|
|
260
|
+
// bound Conversion that has since been archived. Wraps `/api/targets`.
|
|
261
|
+
targets: {
|
|
262
|
+
async list() {
|
|
263
|
+
const res = await fetch(`${root}/api/targets`, { headers });
|
|
264
|
+
if (!res.ok)
|
|
265
|
+
throw new Error(`Apex targets.list failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
266
|
+
const json = (await res.json());
|
|
267
|
+
if (Array.isArray(json))
|
|
268
|
+
return json;
|
|
269
|
+
return json.data ?? [];
|
|
270
|
+
},
|
|
271
|
+
async create(params) {
|
|
272
|
+
const res = await fetch(`${root}/api/targets`, {
|
|
273
|
+
method: "POST", headers, body: JSON.stringify(params),
|
|
274
|
+
});
|
|
275
|
+
if (!res.ok)
|
|
276
|
+
throw new Error(`Apex targets.create failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
277
|
+
return res.json();
|
|
278
|
+
},
|
|
279
|
+
/** Patch a Target. Pass `sourceConversionId: null` to unbind (back to manual). */
|
|
280
|
+
async update(id, patch) {
|
|
281
|
+
const res = await fetch(`${root}/api/targets`, {
|
|
282
|
+
method: "PATCH", headers, body: JSON.stringify({ id, ...patch }),
|
|
283
|
+
});
|
|
284
|
+
if (!res.ok)
|
|
285
|
+
throw new Error(`Apex targets.update failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
286
|
+
return res.json();
|
|
287
|
+
},
|
|
288
|
+
async delete(id) {
|
|
289
|
+
const res = await fetch(`${root}/api/targets?id=${encodeURIComponent(id)}`, {
|
|
290
|
+
method: "DELETE", headers,
|
|
291
|
+
});
|
|
292
|
+
if (!res.ok)
|
|
293
|
+
throw new Error(`Apex targets.delete failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
294
|
+
},
|
|
295
|
+
},
|
|
73
296
|
// ─── Communications ───────────────────────────────────────────────
|
|
74
297
|
/** Append a content variant to a communication (composer-columns model; an experiment arm). */
|
|
75
298
|
async createCommVariant(commId, opts) {
|