EV

Event Dictionary

Analytics & Tracking Reference · Segment-reconciled

Confirmed Undocumented Unobserved
Analytics Reference

Every event, reconciled against live Segment traffic.

A catalog of analytics events reconciled across three vectors: what the repos instrument, what actually fires (observed in SEGMENT_EVENTS in Snowflake across 7 production sources), and which of those reach Amplitude. Segment is the source of truth for existence and volume; Amplitude is one destination among several. Names in code are Title Case; Segment stores them UPPER_SNAKE — both are shown. Observed through 2026-08-26.

409
Events cataloged
222
Firing in Segment
28M
Events recorded
42
Not reaching Amplitude
187
Instrumented, never seen

Reconciliation — three states

The gap between what the code defines and what Segment actually receives is the most important thing this dictionary surfaces. Click a card to filter the table.

97

✅ Confirmed

Instrumented in code and observed firing in Segment. Volume and last-seen date come straight from the warehouse, so these are events you can trust.

125

☁️ Undocumented

Firing in Segment but no instrumentation located in the indexed repos — mostly other product surfaces (SolidCareer, LCMS, Admin, VILT) that were never inventoried, plus newer frontend apps.

187

🚫 Unobserved

Instrumented in code but zero warehouse traffic. Either it never fires, or it's filtered at the Segment source/destination before landing. Verified against TRACKS — no event reaches the warehouse without a table.

Destination gap. 42 events are firing in Segment but do not appear in the Amplitude taxonomy — including the highest-volume events in the product. Amplitude only receives the frontend and backend sources, so this is a destination-coverage question, not a documentation gap. Filter the table by Not in Amplitude to review.
Data quality. 63 events carry rows with an epoch-zero timestamp — every LCMS event and a large share of PLATFORM_BACKEND. Lifecycle dates in this dictionary use received_at (Segment's ingest clock) for that reason; treat timestamp on server-side sources as unreliable.

The Events Table

Search by name; filter by reconciliation state, source, lifecycle, domain, or emission vector. Click a row for observed payload, volume, code name, and source file.

EventSourceVectorDomainReconciliationLifecycleVolumeLast seen

Emission Vectors

Six ways platform-backend and platform-frontend emit analytics events — each needs a different search to enumerate, which is why a naive grep undercounts. A seventh row covers events observed in the warehouse that no indexed repo accounts for: the other product surfaces (SolidCareer, LCMS, Admin, VILT, Live Training) have never had their instrumentation inventoried.

VectorWhereEvent name comes fromCount
SegmentEvent / …ContractBE app/*/Events/*.phpeventName literal / EVENT_NAME const / interpolated126
Track (abstract base)BE Stripe, subscriptions, PasswordReset…getName() override or class name14
HasSegment traitBE listeners calling $this->track()class name → words (default)21
OccurredActionTrackBE Eloquent observersliteral passed to ::dispatch() (Created/Updated/Deleted X)69
trackEvent (store / object)FE mixin + { event: … } object form + helpersstring arg, event: key, or wrapper arg49
direct dispatch / HotjarFE dispatch('segment/track'), window.hj('event')string literal5
(observed only)Not located — other product surfaces + newer frontend appsobserved in SEGMENT_EVENTS; no code vector found125

How Events Flow

Everything converges on Segment, which fans out to the Snowflake warehouse, Amplitude (product analytics), ChurnZero, and Hotjar. The warehouse destination is what this dictionary observes: it writes one table per event name into SEGMENT_EVENTS.<SOURCE>, so it sees every source, not just the two that feed Amplitude. Names are stored UPPER_SNAKE, and some are re-derived downstream from properties (e.g. user_type=Manager → MANAGER_*).

// Backend  (source: PLATFORM_BACKEND)
Event class / Track / HasSegment / OccurredActionTrack  →  Segment.track()  →  Segment  →  Snowflake · Amplitude · ChurnZero
// Frontend (source: PLT_PROD_FRONTEND)
trackEvent() | dispatch('segment/track')  →  Segment  →  Snowflake · Amplitude
window.hj('event', …)                     →  Hotjar   (snake_cased, consent-gated)
// Other surfaces — warehouse only, never inventoried in code
SolidCareer · LCMS · Admin · VILT · Live Training       →  Segment  →  Snowflake

// The warehouse destination is the observation point for this dictionary
Segment  →  SEGMENT_EVENTS.<SOURCE>.<EVENT_NAME>   (one table per event, plus TRACKS)

Base Payload Envelope

Backend events carry a common Segment track envelope; the two base contracts nest the event-specific data differently.

SegmentEvent · newer
{
  "userId": "<uuid>",
  "event": "User Viewed A Lesson",
  "properties": {
    "accountExternalId": …, "contactExternalId": …,
    "meta": { "origin":…, "accountUser":{…} },
    "data": { /* event-specific */ }
  },
  "integrations": { "ChurnZero": true }
}
SegmentEventContract · older + Track/HasSegment
{
  "userId": "<uuid>",
  "event": "Mark Order As Paid",
  "properties": { /* eventDetails / getProperties() */ },
  "context": { "active": true, "ip": … },
  "timestamp": …
}

Methodology & Freshness

Built by extracting all six emission vectors from source, then reconciling against the Amplitude taxonomy. Regenerate with .assemble-events.py → .build-event-dictionary.py.

  • Backend vectors: grep -rlE "extends SegmentEvent(Contract)?", extends Track, use HasSegment + $this->track(, and OccurredActionTrack::dispatch( literals.
  • Frontend vectors: trackEvent('…'), trackEvent({ event: … }), helper wrappers (updateAssignment('…')), dispatch('segment/track'), window.hj('event', …).
  • Reconciliation normalizes both sides to UPPER_SNAKE and matches against the Amplitude export.
  • To make authoritative: connect Amplitude (or export the Segment tracking plan) for the full taxonomy + properties + volume; and scan the Vue 3 repos (platform, platform-tools, platform-admin, project-puffin) that also feed PLT_PROD_FRONTEND to resolve the platform-only rows.
Known naming issues surfaced. Colliding Segment names (CourseVersionSectionStoredEvent emits "Course Stored"); a lang-file dup makes StripeMembershipRenewing emit "Stripe Marked An Invoice As Paid"; digit-splitting produces malformed "Mark14 Day Or2 Day…"; FE fires both "User Bookmarked a Lesson" and lowercase "User bookmarked a lesson".