@apex-inc/sdk
v0.28.0
Published
Event tracking and identity SDK for Apex — currently in private beta.
Downloads
1,092
Readme
@apex-inc/sdk
Event tracking and identity SDK for Apex.
Works in Node.js 18+ and browser environments. Zero runtime dependencies.
Docs: https://app.apex.inc/docs
Install
npm install @apex-inc/sdkQuick Start
import { init, track, identify } from "@apex-inc/sdk";
init({
workspaceKey: "your-workspace-key",
apiUrl: "https://app.apex.inc",
});
identify("user_123", { email: "[email protected]" });
track("page_viewed", { path: "/pricing" });Profile photos (avatar_url)
Pass avatar_url in traits to show end-user photos across Apex — the
Customers list, customer detail pages, and the Live Customers widget. It's a
canonical attribute, so
photoUrl / image / profile_image map to it too. Apex validates on write
(https only) and falls back to colored initials when absent.
identify("user_123", {
email: "[email protected]",
avatar_url: user.photoURL, // OIDC `picture` claim works the same way
});PLG primitives
In addition to track / identify the SDK exposes three PLG-specific
primitives. Use them when you want Apex to derive activation,
expansion, and retention rollups automatically:
import { init, identify, group, feature, track } from "@apex-inc/sdk";
import { EVENTS, type ActivatedEvent } from "@apex-inc/sdk/plg-events";
init({ workspaceKey: "your-workspace-key", apiUrl: "https://app.apex.inc" });
// Identify the authenticated user.
identify("[email protected]", { visitorId: "apex_vid_from_cookie" });
// Group them into their account / team (B2B).
group("acct_acme", { plan: "growth", arr: 120_000 });
// Track feature usage. Include experiment variant if you have one.
feature("cohort-builder", { variant: "new-editor", enabled: true });
// Emit curated PLG lifecycle events with compile-time-typed payloads.
track(EVENTS.ACTIVATED, {
daysToActivation: 3,
milestone: "first-experiment-shipped",
} satisfies ActivatedEvent);The PLG catalog ships eight curated events: signup, activated,
feature_first_use, aha_moment, expansion, contraction,
reactivation, churn. Each has a typed interface so your IDE
catches missing fields at build time. See the PLG event catalog
docs for the full
schema.
Server Events API (server-to-server events)
For backend workflows that don't have a browser session — Stripe webhooks,
CRM events, scheduled jobs — use sendServerEvent / sendServerEvents.
These hit the /api/v1/events endpoint with API-key auth and
idempotency-key support.
import { sendServerEvent, newIdempotencyKey } from "@apex-inc/sdk";
await sendServerEvent(
{ apiKey: process.env.APEX_API_KEY! }, // apex_sk_…
{
type: "purchase_completed",
email: customer.email,
data: { value: invoice.amount_paid / 100, currency: "USD", order_id: invoice.id },
},
{ idempotencyKey: stripeEvent.id }, // or newIdempotencyKey()
);Batches of up to 100 events go through sendServerEvents. Use the same
visitorId / email you'd use in the browser snippet so cross-channel
stitching just works.
Management client (API key)
createManagementClient is the server-side client for everything your
dashboard can do. It is grouped by surface:
import { createManagementClient } from "@apex-inc/sdk";
const apex = createManagementClient({
apiKey: process.env.APEX_API_KEY!, // apex_sk_…
workspaceKey: "my-shop",
apiUrl: "https://app.apex.inc",
});
await apex.experiments.list(); // list · get · getResults · activate · promote
await apex.experiments.checkLiveTests({ files }); // is a live test reading this file?
await apex.experiments.createAdHoldout({ // hold 20% of known people back from these Meta ad sets
name: "Spring bows · do the ads cause installs?",
network: "meta", adAccountId: "act_1", adSetIds: ["as_1", "as_2"], holdoutPercent: 0.2,
}); // draft; activate(id) draws the list
await apex.experiments.previewAdExclusion(id); // Meta: each ad set's exclusions before/after; one id added, nothing else
await apex.experiments.attachAdExclusion(id, { acknowledge: true }); // Apex puts the list on, after the shop saw the preview
await apex.experiments.verifyAdExclusion(id); // is the do-not-show list on every ad set? nothing counts until it is
await apex.links.create({ slug: "spring", destinationUrl: "https://…", utmCampaign: "spring" });
await apex.conversions.list();
await apex.adoption.metrics();
await apex.communications.list();
await apex.mobile.skan.getSchema(); // what Apple's 0–63 values mean for your app
await apex.mobile.skan.setSchema({ schema }); // workspace admin; Apex validates and stamps updatedAt
await apex.beliefs.suggestions.list(); // settled experiments worth writing down as a belief
await apex.beliefs.suggestions.accept({ experimentId, statement }); // one belief, evidence state "one study"beliefs.suggestions is the "Worth writing down" rail: a settled experiment
is offered only when the result is clean (decisive, exclusion attached the
whole window for an Ad holdout, no sample-ratio mismatch, no guardrail
breach, both arms over the events floor). Ask for one experiment and
blocker says why it is not offered. Accepting writes one belief with the
experiment as its evidence; a second test that agrees moves it to validated.
mobile.skan is the same schema the "Say what Apple's numbers mean" setup
step saves, and the one the Capacitor plugin reads on the device to set
conversion values for you. Fine values are 0–63; coarse values are
low / medium / high; one value per event per window.
The flat names (apex.listExperiments(), apex.activateExperiment(id), …)
still work as aliases of the experiments.* methods.
Multi-workspace orgs
If your organization runs more than one workspace (e.g. a SaaS product
plus a partner marketplace), each workspace has its own workspaceKey.
Initialize one SDK instance per workspace; event identity stitches at
the org level via OrgPerson so a single human tracked in both shows
up as one person in cross-workspace cohorts and reports.
License
MIT — see LICENSE
