Instrumenting segments
A Segment is a saved rule over events and traits. For a Segment to match anyone, your codebase has to fire those events and set those traits.
When you export a Segment to Meta or Google, that export is an Audience in the ad platform's vocabulary. Inside Apex it stays a Segment.
The chain
Your codebase → apex.track(...) / apex.identify(...) → Apex stores the event + traits → Segment predicate matches → Journey or letter fires
Every segment predicate points at one of three things:
- An event — fired via
apex.track("event_name", { ...properties }). Predicates:event_fired,event_not_fired. - A trait — set via
apex.identify(userId, { trait_name: value }). Predicate:attribute. - A channel preference — set by your end-users via the public preferences page. Predicate:
channel_opted_in/channel_opted_out. No codebase work required for this third type.
Starter segments shipped with your workspace
Apex ships starter Segments. Each requires specific instrumentation. Here is exactly what to fire.
Active customers (90d)
Predicate: purchase_completed fired in the last 90 days.
Add to your codebase — call this from your checkout-success handler:
import { track } from "@apex-inc/sdk";
track("purchase_completed", {
orderId: order.id,
total: order.total, // e.g. 149.99
currency: order.currency, // e.g. "USD"
});
If you're using the script tag (Shopify, WordPress, Webflow), the same call is apex.track("purchase_completed", { ... }).
Inactive 30+ days
Predicate: session_started NOT fired in the last 30 days.
Add to your codebase — call this once per user session, typically on app load:
import { track } from "@apex-inc/sdk";
track("session_started", {
path: window.location.pathname, // optional
});
You don't need to fire this for the audience to MATCH (the predicate is event_not_fired). But you DO need it firing for active users so Apex knows who's currently engaged. Without it, every user looks inactive.
Trial users
Predicate: plan trait equals "trial".
Add to your codebase — call this after login, and again whenever the user's plan changes:
import { identify } from "@apex-inc/sdk";
identify(user.id, {
email: user.email,
plan: user.plan, // "trial" | "starter" | "pro" | ...
});
Engaged, not yet purchased
Predicate: session_started in last 30 days AND no purchase_completed ever.
Wire BOTH events from above. This audience composes the two — wire each event the way the prior two sections describe.
All marketing-opted-in
Predicate: email channel preference is opted-in (or default).
No codebase work required. End-users set their channel preferences via the public preferences page at /preferences/[token]. The default state is opted-in until they explicitly opt out — this audience matches everyone except explicit opt-outs.
Per-framework code
The SDK has the same surface across frameworks. Pick yours:
React / Next.js
import { track, identify } from "@apex-inc/sdk";
// In an event handler:
function handleCheckoutSuccess(order) {
track("purchase_completed", {
orderId: order.id,
total: order.total,
currency: order.currency,
});
}
// At login (inside an effect or auth callback):
async function onLogin(user) {
identify(user.id, { email: user.email, plan: user.plan });
}
Vue / Nuxt
import { track, identify } from "@apex-inc/sdk";
// Inside a composable or methods:
function checkoutSuccess(order) {
track("purchase_completed", {
orderId: order.id,
total: order.total,
currency: order.currency,
});
}
Vanilla / Script tag (Shopify, WordPress)
After the snippet loads:
<script>
apex.track("purchase_completed", {
orderId: "{{ order.id }}",
total: {{ order.total }},
currency: "{{ order.currency }}",
});
</script>
For Shopify specifically, drop this in your "Order Status" page (Settings → Checkout → Additional scripts).
Server (any language)
There is no apex Python or Ruby package for product events. From your backend, POST /api/v1/events with an apex_sk_ key. See Server Events.
curl -X POST https://app.apex.inc/api/v1/events \
-H "Content-Type: application/json" \
-H "x-api-key: apex_sk_..." \
-d '{
"event": "purchase_completed",
"user_id": "cus_123",
"properties": { "orderId": "ord_1", "total": 149.99, "currency": "USD" }
}'
Verifying your events are landing
Three ways:
-
Segment builder. Open
/dashboard/customers/segments, edit any segment that uses an event. The event picker autocompletes events Apex has actually received from your workspace. Events with non-zero counts have arrived. -
The campaigns wizard's "Send a test event" button. When you click "Use template" on a campaign, the wizard's first screen has a button that fires a test event with
is_test=1. Clicking it confirms the pipe end-to-end. After that, fire from your real code; the wizard auto-advances when it sees a non-test arrival. -
The MCP server's
wire_audience_seedtool. If you're using Cursor or Claude Code with the Apex MCP, ask your AI agent to wire instrumentation directly: "Use the Apex MCP'swire_audience_seedtool with seedId='aud-seed-active-customers-90d'." The agent reads the seed's instrumentation requirements, generates the code for your framework, edits the right file, and fires a test event to verify the wiring landed.
Authoring your own segments
The starters are starting points. Once your codebase is firing events that fit your business (subscription_renewed, feature_xyz_used, support_ticket_opened), build Segments from those:
- Open
/dashboard/customers/segments. - New segment → Match by rules → Add an
event_firedpredicate → start typing your event name. - Copy the snippet for your framework from the wire panel.
Related
- Tracking Events — the full
track()API reference. - Identity — the
identify()API and trait stitching. - Segments — the predicate language overview.
- Channels — channel preferences and opt-in.