@apex-inc/sdk 0.20.2 → 0.25.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/dist/esm/management.js +74 -45
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/management.d.ts +103 -41
- package/dist/management.d.ts.map +1 -1
- package/dist/management.js +74 -45
- package/dist/management.js.map +1 -1
- package/package.json +2 -2
- package/skills/apex-adoption/SKILL.md +44 -92
- package/skills/apex-communications/SKILL.md +1 -1
- package/skills/apex-experimentation/SKILL.md +2 -1
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: apex-adoption
|
|
3
|
-
description: Build on Apex
|
|
3
|
+
description: Build on Apex Adoption from the SDK (createManagementClient) — define Milestones, compose one Adaptive Journey (Nudge and/or Celebration), hours or days, unique Communications. A Milestone is NOT a Target.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Apex Adoption (SDK)
|
|
7
7
|
|
|
8
8
|
Name live hosts with `apex.workspace.environments` (or Foundations) before you trust Production adoption numbers. Unmatched hosts stay Unclassified.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Adoption tracks whether each person has finished a Milestone. A Milestone is **This person** unless they set **Anyone here** (`finishWhen: "anyone_here"`). Anyone here = first person on the same Account (`identify({ account: { id } })`) finishes it for the company. Only that person gets the Celebration. Trait finishes stay This person. Company facts (snippet, SSO, billing) → Anyone here. Person skills (ran a report, invited a teammate) → This person. `apex.adoption.update(id, { finishWhen: "anyone_here" })` can flip a live event row; that backfills leftover Nudges.
|
|
11
|
+
|
|
12
|
+
`compose` stamps **one Adaptive Journey** when they want letters — Trigger → Wait until they finish → Celebration and/or Nudge. Just-track is `scaffold: "none"`. Do not clone two templates.
|
|
13
|
+
|
|
14
|
+
## Words
|
|
15
|
+
|
|
16
|
+
Milestone, Nudge, Celebration, Adaptive Journey, Communication, Segment. A Milestone is not a Target.
|
|
11
17
|
|
|
12
18
|
## Bootstrap
|
|
13
19
|
|
|
@@ -20,137 +26,83 @@ const apex = createManagementClient({
|
|
|
20
26
|
});
|
|
21
27
|
```
|
|
22
28
|
|
|
23
|
-
## Define a
|
|
24
|
-
|
|
25
|
-
A milestone is "adopted feature X", defined by an event or a trait. It starts inactive.
|
|
29
|
+
## Define a Milestone
|
|
26
30
|
|
|
27
31
|
```ts
|
|
28
32
|
const milestone = await apex.adoption.create({
|
|
29
33
|
name: "Ran first report",
|
|
30
34
|
featureKey: "reporting",
|
|
31
35
|
adoptedWhen: { kind: "event", eventName: "report_run" },
|
|
32
|
-
gapPolicy: { kind: "days_since_signup", days:
|
|
36
|
+
gapPolicy: { kind: "days_since_signup", days: 0, hours: 1 },
|
|
37
|
+
enrollMode: "future",
|
|
38
|
+
finishWhen: "this_person",
|
|
33
39
|
priority: 10,
|
|
34
|
-
// active defaults to false — turn it on when you're ready to nudge.
|
|
35
40
|
});
|
|
36
41
|
|
|
37
42
|
await apex.adoption.update(milestone.id, { active: true });
|
|
38
43
|
```
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
A milestone has two possible journeys, and they're opposites: a **nudge**
|
|
43
|
-
(fires when a user *hasn't* adopted) and a **celebration** (fires once the
|
|
44
|
-
moment they *do* adopt — congratulate them + tell them what's next).
|
|
45
|
-
`compose` creates the milestone and scaffolds its journey legs in a single
|
|
46
|
-
call. Use `scaffold: "both"` to produce the whole milestone unit — exactly what
|
|
47
|
-
the dashboard sentence on-ramp does:
|
|
45
|
+
## Compose: one Adaptive Journey
|
|
48
46
|
|
|
49
47
|
```ts
|
|
50
|
-
const { milestone,
|
|
51
|
-
await apex.adoption.compose({
|
|
52
|
-
name: "Started first experiment",
|
|
53
|
-
featureKey: "experiments",
|
|
54
|
-
adoptedWhen: { kind: "event", eventName: "experiment_started" },
|
|
55
|
-
gapPolicy: { kind: "days_since_signup", days: 7 },
|
|
56
|
-
scaffold: "both", // nudge + celebration. Also: "celebration" (default), "nudge", "none".
|
|
57
|
-
});
|
|
58
|
-
// Both ids are DRAFTS — edit copy in the canvas, then publish each.
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
For a single leg, pass `scaffold: "celebration"` (default) or `"nudge"` and an
|
|
62
|
-
optional `journeyName`:
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
const { milestone, journeyId } = await apex.adoption.compose({
|
|
48
|
+
const { milestone, journeyId, sends } = await apex.adoption.compose({
|
|
66
49
|
name: "Started first experiment",
|
|
67
50
|
featureKey: "experiments",
|
|
68
51
|
adoptedWhen: { kind: "event", eventName: "experiment_started" },
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
gapPolicy: { kind: "days_since_signup", days: 0, hours: 1 },
|
|
53
|
+
enrollMode: "future",
|
|
54
|
+
scaffold: "both", // also: "nudge" | "celebration" | "none"
|
|
71
55
|
});
|
|
72
|
-
// journeyId is a DRAFT
|
|
56
|
+
// journeyId is a DRAFT. Preview each send's Communication, then publish.
|
|
73
57
|
```
|
|
74
58
|
|
|
75
|
-
|
|
59
|
+
`sends` is `{ role, stepId, commId }[]`. `nudgeJourneyId` is the same id. `celebrationJourneyId` is null on new rows.
|
|
76
60
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
depthMilestone.id, // index 1
|
|
81
|
-
]);
|
|
82
|
-
```
|
|
61
|
+
Each Send has its own Communication. Never reuse `comm-adoption-nudge` / `comm-adoption-celebration`.
|
|
62
|
+
|
|
63
|
+
Extra Nudges: `nudgeClocks: [{ hours: 1 }, { hours: 3 }]`. First is the Wait deadline; later are more hours or days on that arm. One lift for the protocol.
|
|
83
64
|
|
|
84
|
-
|
|
85
|
-
(step-by-step drop-off). Unordered milestones stay on the order-agnostic
|
|
86
|
-
"Coverage" view — a funnel over an unordered set would imply a false sequence.
|
|
65
|
+
Save as a Segment: `saveNotYetSegment` / `saveDoneSegment`.
|
|
87
66
|
|
|
88
|
-
|
|
67
|
+
## Order
|
|
89
68
|
|
|
90
69
|
```ts
|
|
91
|
-
await apex.adoption.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
70
|
+
await apex.adoption.reorder([
|
|
71
|
+
activationMilestone.id,
|
|
72
|
+
depthMilestone.id,
|
|
73
|
+
]);
|
|
96
74
|
```
|
|
97
75
|
|
|
98
76
|
## List + read metrics
|
|
99
77
|
|
|
100
78
|
```ts
|
|
101
79
|
const milestones = await apex.adoption.list();
|
|
102
|
-
|
|
103
80
|
const { metrics, health } = await apex.adoption.metrics();
|
|
104
|
-
|
|
105
|
-
|
|
81
|
+
const person = await apex.adoption.progress(endUserId);
|
|
82
|
+
const company = await apex.adoption.accountProgress(accountId);
|
|
106
83
|
```
|
|
107
84
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
## Feature groupings
|
|
111
|
-
|
|
112
|
-
Milestones group under tenant-owned "features" (how the builder organizes your own events). Manage them from the IDE too:
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
await apex.adoption.features.create({ label: "Subscriptions", eventNames: ["subscription_started"] });
|
|
116
|
-
const features = await apex.adoption.features.list();
|
|
117
|
-
```
|
|
85
|
+
Lift is one number for the protocol. Trait Milestones do not report lift (`liftMeasurable: false`).
|
|
118
86
|
|
|
119
|
-
##
|
|
87
|
+
## Stills
|
|
120
88
|
|
|
121
|
-
|
|
89
|
+
If they already have the picture, `attach_adoption_milestone_still` (MCP) or `POST /api/adoption/milestones/:id/assets`.
|
|
122
90
|
|
|
123
|
-
|
|
124
|
-
// Friendly conditions (compiled + validated server-side):
|
|
125
|
-
await apex.segments.create({
|
|
126
|
-
name: "Power users who haven't upgraded",
|
|
127
|
-
conditions: {
|
|
128
|
-
match: "all",
|
|
129
|
-
groups: [
|
|
130
|
-
{ match: "all", conditions: [
|
|
131
|
-
{ verb: "has", event: "report_run", window_days: 30 },
|
|
132
|
-
{ attribute: "plan", op: "is_not", value: "pro" },
|
|
133
|
-
] },
|
|
134
|
-
],
|
|
135
|
-
},
|
|
136
|
-
});
|
|
91
|
+
Signed-in product pages: Playwright on their machine, then upload. Apex cannot sign in for them.
|
|
137
92
|
|
|
138
|
-
|
|
93
|
+
```
|
|
94
|
+
npx @apex-inc/mcp-server screenshot --milestone <id> --state not_yet --url https://app.theirproduct.com/reports --storage-state .apex/auth.json
|
|
95
|
+
npx @apex-inc/mcp-server screenshot --milestone <id> --state done --url https://app.theirproduct.com/reports --storage-state .apex/auth.json
|
|
139
96
|
```
|
|
140
97
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Built a feature but haven't wired Apex yet? Register the event's name so milestones/segments can reference it. **Defining doesn't make it happen** — a developer still calls `apex.track("...")` from the app.
|
|
98
|
+
Save the login file with `npx playwright codegen --save-storage .apex/auth.json <url>` (or reuse their test login). Apex never receives that file.
|
|
144
99
|
|
|
145
|
-
|
|
146
|
-
await apex.events.define({ name: "report_exported", description: "User exported a report to CSV" });
|
|
147
|
-
// then, in your app code: apex.track("report_exported"); // define → wire → verify
|
|
148
|
-
```
|
|
100
|
+
Public pages: Capture on the card / hosted recapture. A login wall writes nothing.
|
|
149
101
|
|
|
150
102
|
## Notes
|
|
151
103
|
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
104
|
+
- A Milestone is per person, per feature — not a Target.
|
|
105
|
+
- New rows default to `enrollMode: "future"`. Do not publish a short clock on `"all"`.
|
|
106
|
+
- `gapPolicy.hours` wins over `days`. `0` is right away. 1 hour is not right away.
|
|
107
|
+
- Nudge honors holdout, opt-out, and the Nudge cap. Celebration is holdout-exempt.
|
|
108
|
+
- The Adaptive Journey is a draft until they publish it.
|
|
@@ -154,7 +154,7 @@ did: publish opened a filled-in draft; `activateExperiment` starts the test.
|
|
|
154
154
|
|
|
155
155
|
`in_experiment` is a hard stop, not an intent question — the content is a
|
|
156
156
|
treatment mid-measurement. `{ confirmLiveExperiment: true }` overrides it and
|
|
157
|
-
**marks
|
|
157
|
+
**marks the numbers untrustworthy: Apex will not call a winner and will not learn from mixed counts**.
|
|
158
158
|
Offer the alternatives first: wait for it to finish, or conclude it so the
|
|
159
159
|
learning is captured.
|
|
160
160
|
|
|
@@ -69,7 +69,7 @@ Interactive flows: MCP prompts `new-experiment` and `experiment-review` orchestr
|
|
|
69
69
|
|
|
70
70
|
## Screenshots, exposure & the wiring gate
|
|
71
71
|
|
|
72
|
-
- **Screenshots are first-class.** SDK / Cursor experiments author the variant in local code. Apex does **not** auto-capture a public URL on create — that page does not have the unpublished variant, and a guessed host (a sibling workspace's brand domain) shows the wrong site on both arms. Capture both arms on localhost with `?_apex_preview=control|variant_a&_apex_exp=<id>`, then `attach_experiment_asset({ experimentId, variantKey, imageBase64 })
|
|
72
|
+
- **Screenshots are first-class.** SDK / Cursor experiments author the variant in local code. Apex does **not** auto-capture a public URL on create — that page does not have the unpublished variant, and a guessed host (a sibling workspace's brand domain) shows the wrong site on both arms. Capture both arms on localhost with `?_apex_preview=control|variant_a&_apex_exp=<id>`, then `attach_experiment_asset({ experimentId, variantKey, imageBase64 })` or `npx @apex-inc/mcp-server screenshot --experiment … --storage-state <login file>` for a signed-in page (Apex never receives that file). After deploy, `recapture_experiment_screenshots` refreshes the live host. Snippet/DOM experiments created in the dashboard still auto-capture a public URL whose variant already lives there.
|
|
73
73
|
- **Target URL is this workspace.** `target_url` must be a host named on the **active** workspace (`list_workspace_environments` + the workspace site). If the merchant already named a workspace, switch to it — do not ask which workspace again. If they have multiple hosts on that workspace, ask which URL. Never guess `apex.inc` or any other workspace's domain.
|
|
74
74
|
- **Mobile/Capacitor experiments capture on-device.** When the experiment runs on an authed in-app screen (servers can't reach it), tell the dev to call `Apex.captureVariantScreenshot({ experimentId, variantKey })` on the variant's screen, keyed to the resolved variant in a `useEffect`, in a debug build (`Apex.initialize({ ..., debug: true })`). It no-ops in production and lands the shot on the dashboard card + gallery like web/agent captures.
|
|
75
75
|
- **Exposure auto-fires.** When a variant resolves via `useApexVariant` (web) or `Apex.getVariant()` (mobile), the SDK fires the canonical `experiment_exposure` event — the denominator for results. You don't fire it manually.
|
|
@@ -154,6 +154,7 @@ Match Apex UI terminology in user-facing copy: e.g. **Adaptive traffic allocatio
|
|
|
154
154
|
- Hiding the page until `/assign` returns (worse LCP; not the React-hook promise).
|
|
155
155
|
- Changing variant implementation mid-flight without versioning (invalidates analysis).
|
|
156
156
|
- Only using p-values without effect size or business context.
|
|
157
|
+
- Shipping onto a live test. Before you edit a page, commit, or push — even if they never said experiment — call `check_live_tests` (or `checkLiveTests` on the management client) with the **full file contents**, not the diff hunk. Before you change a letter, Adaptive Journey, or Milestone, pass those ids too. If something live is on those files or ids, **stop and ask**. Three outs: end the test then ship; wait; or they say this change is not the thing under test (they mark that on the experiment page). Do not edit. Do not commit.
|
|
157
158
|
|
|
158
159
|
## Related
|
|
159
160
|
|