PLG event catalog

Apex ships with a curated catalog of eight PLG lifecycle events. Emit any subset of them from your product via the Apex SDK and Apex derives activation, expansion, and retention rollups without bespoke queries.

Using the catalog is optional — apex.track() accepts any event name — but the catalog is the path of least resistance for product-led teams.

Installing the SDK

npm install @apex-inc/sdk
import { init, identify, track, group, feature } from "@apex-inc/sdk";
import { EVENTS, type ActivatedEvent } from "@apex-inc/sdk/plg-events";

init({
  workspaceKey: "<your-workspace-key>",
  apiUrl: "https://app.apex.inc",
});

The eight events

signup

A user created an account. Fire once per user lifecycle.

import { EVENTS, type SignupEvent } from "@apex-inc/sdk/plg-events";

apex.track(EVENTS.SIGNUP, {
  method: "google",
  referralSource: "product-hunt",
} satisfies SignupEvent);

activated

User hit your product's core activation milestone. You define the milestone. Fire once per user.

import { EVENTS, type ActivatedEvent } from "@apex-inc/sdk/plg-events";

apex.track(EVENTS.ACTIVATED, {
  daysToActivation: 2,
  milestone: "first-experiment-shipped",
} satisfies ActivatedEvent);

feature_first_use

User used a named feature for the first time. Emit on every feature, only on first use. Use apex.feature() for recurring usage.

apex.track(EVENTS.FEATURE_FIRST_USE, {
  featureKey: "cohort-builder",
  daysFromSignup: 7,
});

aha_moment

The user saw the product's value. Often emitted after a specific sequence ("connected a source and ran their first experiment"). Definition is product-specific.

apex.track(EVENTS.AHA_MOMENT, {
  label: "first-winning-experiment",
  daysFromSignup: 14,
  confidence: 0.92,
});

expansion

Paying account increased spend (upgrade, more seats, more usage). Amount in positive units.

apex.track(EVENTS.EXPANSION, {
  amount: 300,
  currency: "USD",
  driver: "plan_upgrade",
  fromPlan: "growth",
  toPlan: "scale",
});

contraction

Paying account decreased spend without churning. Amount in positive units (representing the reduction).

apex.track(EVENTS.CONTRACTION, {
  amount: 200,
  currency: "USD",
  driver: "seat_removed",
});

reactivation

A churned account came back. Fire once on re-signup or re-activation.

apex.track(EVENTS.REACTIVATION, {
  daysInactive: 180,
  reason: "feature-launch-email",
});

churn

Account cancelled or let subscription lapse. Fire once on the churn transition.

apex.track(EVENTS.CHURN, {
  reason: "switched-vendor",
  cancelType: "voluntary",
  daysActive: 410,
});

Typed helper

If you prefer strict typing over string-name + object, use trackPlgEvent:

import { trackPlgEvent, EVENTS } from "@apex-inc/sdk/plg-events";
import * as apex from "@apex-inc/sdk";

trackPlgEvent(apex, EVENTS.ACTIVATED, {
  daysToActivation: 3,
  milestone: "first-campaign-sent",
});

trackPlgEvent<K> infers the payload shape from the event name so you get compile-time errors if you pass the wrong fields.

Groups and features

For B2B products that want account-level rollups:

apex.identify("chris@acme.com", { name: "Chris", plan: "growth" });
apex.group("acct_acme", { plan: "growth", arr: 120000 });
apex.feature("cohort-builder", { variant: "new-editor", enabled: true });

Every track and feature call after apex.group() stamps the active groupId so PQA (product-qualified account) analyses are a single query away.

What Apex does with these

  • Activation rate rollup on the org dashboard — activated events / signup events over time.
  • Time to aha — median daysFromSignup on aha_moment events.
  • Feature adoption — feature_first_use counts per featureKey.
  • Expansion MRR — sum of expansion.amount bucketed by month.
  • Churn rate — rolling 30-day churn count / active customer count.
  • PQL/PQA scoring — event combinations feed the built-in scoring engine.