B2B SaaS Events
Canonical event model for reliable routing across conversion destinations.
TrackStack uses a canonical event layer so product code stays stable while destination mappings can evolve.
Funnel-first event design
Instrument events around funnel stage transitions, not every click. This keeps data stable for routing, attribution, and reporting.
TrackStack canonical events map cleanly to AARRR-style analysis:
| Funnel lens | TrackStack canonical events |
|---|---|
| Acquisition | PageView, ContentView, SignUp |
| Activation | StartTrial, FirstAction, AddPaymentInfo |
| Retention | FeatureUsed, SubscriptionRenew |
| Revenue | Purchase, PlanUpgrade, Refund |
| Referral (optional) | Custom referral events if your product has invitation loops |
Funnel event set
| Stage | Canonical events |
|---|---|
| Awareness | PageView, ContentView |
| Interest | FeatureUsed |
| Acquisition | SignUp, StartTrial |
| Activation | FirstAction, AddPaymentInfo |
| Revenue | Purchase, PlanUpgrade, SubscriptionRenew, Refund |
Winning by Design double-funnel (bowtie) mapping
Use a "double funnel" lens to separate new demand capture from post-sale expansion/retention.
Practical guidance:
- Funnel 1 (pre-sale): prioritize
SignUp,StartTrial,FirstAction, andPurchase. - Funnel 2 (post-sale): prioritize
FeatureUsed,SubscriptionRenew, andPlanUpgrade. - Keep one canonical payload contract across both funnels.
- Add custom events only for lifecycle steps not covered by canonical names.
Canonical payload shape
type CanonicalCapture = {
event:
| "PageView"
| "ContentView"
| "FeatureUsed"
| "SignUp"
| "StartTrial"
| "FirstAction"
| "AddPaymentInfo"
| "Purchase"
| "PlanUpgrade"
| "SubscriptionRenew"
| "Refund";
eventId: string;
email?: string;
phone?: string;
externalId?: string;
value?: number;
currency?: string;
transactionId?: string;
plan?: string;
pageUrl?: string;
pageTitle?: string;
referrer?: string;
platformParams?: Record<string, Record<string, unknown>>;
};
Capture pattern
Use one canonical payload shape and let the edge adapt it per destination.
trackstack.capture("SignUp", {
eventId: "evt_signup_001",
email: "jane@example.com",
pageUrl: "https://app.gettrackstack.com/sign-up",
});
Multiple events on one page
TrackStack supports many conversion events on a single page view (for example: ContentView -> FeatureUsed -> StartTrial).
- Call
capture()for each canonical action; do not combine unrelated actions into one payload. - The SDK queues and batches events before sending to
/events(up to 100 events per request). eventIdis optional per event. Provide it only when you need deterministic dedup across systems.
trackstack.capture("ContentView", {
email: "jane@example.com",
contentType: "product_page",
contentIds: ["pricing-growth"],
});
trackstack.capture("FeatureUsed", {
email: "jane@example.com",
feature: "roi-calculator",
});
trackstack.capture("StartTrial", {
email: "jane@example.com",
plan: "Growth",
});
When each event should happen
Use this as your default trigger policy to keep data quality high:
| Event | Trigger moment | Avoid |
|---|---|---|
PageView | Route/page becomes visible | Firing multiple times for the same page load |
ContentView | User sees meaningful marketing or product content | Treating all UI sections as content views |
SignUp | Account creation succeeds | Firing on form submit before success |
StartTrial | Trial is actually provisioned | Sending when user only opens pricing modal |
FirstAction | First in-product value action completes | Counting passive navigation as activation |
AddPaymentInfo | Valid payment method saved | Firing on payment form open |
Purchase | Initial paid conversion is confirmed | Sending from both client and server without dedup |
PlanUpgrade | Billing plan increases | Treating seat count edits as upgrades unless revenue changes |
SubscriptionRenew | Renewal succeeds | Emitting on invoice generation before payment outcome |
Refund | Refund is processed | Emitting on request submitted but not approved |
Example event JSON
{
"events": [
{
"event": "Purchase",
"eventId": "evt_123",
"userData": {
"email": "jane@example.com",
"country": "US"
},
"customData": {
"value": 1299,
"currency": "USD",
"transactionId": "txn_123",
"plan": "Growth"
},
"platformParams": {
"meta": {
"content_type": "saas"
}
}
}
]
}
Event quality tips
- If you provide
eventId, keep it globally unique and stable for retries. - Standardize canonical event names across teams before launch.
- Include
value,currency, andtransactionIdfor revenue events. - Use
platformParamsonly for explicit platform overrides. - For critical billing events (
Purchase,PlanUpgrade,SubscriptionRenew,Refund), prefer server-side capture.