Analytics
Every number in the dashboard is computed from persisted ProductEvent rows (and the GuideSession / StepInteraction rows derived from them at ingestion time). Nothing is hard-coded.
Event schema
Each event carries eventId (client idempotency key), name, customName (for custom), occurredAt, anonymousId, userId, accountId, sessionId, guideId, guideVersion, stepId, stepIndex, url, properties and SDK name/version. Duplicate eventIds are ignored (unique (projectId, eventId)).
Lifecycle names: guide_viewed, guide_started, step_viewed, step_completed, guide_completed, guide_skipped, guide_dismissed, guide_abandoned, checklist_task_completed, checklist_completed, checklist_dismissed, survey_displayed, survey_submitted, survey_dismissed, resource_center_opened, article_viewed, messenger_opened, conversation_started, conversation_handed_off, scout_answered, page_view, identify, custom. Autocapture and error tracking add page_leave, click, rage_click and exception; their properties, the page metrics, error grouping, and funnel and retention definitions are in product-analytics.md. Events with a URL also store its pathname in path (not when URL collection is off).
Definitions (denominators)
| Metric | Definition |
|---|---|
| Unique users reached | distinct anonymousId with ≥1 event in range (identified users keep their anonymous id, so nobody is double counted) |
| Guide views / starts / completions | count of the respective events in range |
| Guide sessions | GuideSession rows started in range (one per guide run, keyed by the SDK's guideSessionId) |
| Completion rate | sessions completed ÷ sessions started in range |
| Abandonment | sessions dismissed + skipped + abandoned ÷ sessions started |
| Step drop-off | (step viewed − step completed) ÷ step viewed, per step |
| Checklist completion rate | checklists completed in range ÷ checklists with any progress update in range |
| Survey response rate | survey_submitted ÷ survey_displayed in range |
| NPS | % promoters (9–10) − % detractors (0–6) over all numeric answers |
Comparison periods use the same definitions over the immediately preceding window of equal length.
Performance
- Overview and per-guide reports use `GROUP BY` aggregates and `COUNT(DISTINCT …)` SQL with the
indexes listed in
docs/data-model.md; the event explorer and exports are paginated (100 rows per page, streamed CSV). - The worker maintains `GuideDailyStat` rollups every 15 minutes; switch the timeseries queries to the
rollup table when a project exceeds a few million events per month.
- Survey distributions and checklist per-task rates are computed over the most recent 5,000 rows.
Protections
Rate limits per key/IP, batch caps (50 events), property size caps (8 KB), sensitive-key stripping, future timestamp clamping, retention windows, and per-project analyticsEnabled kill switch.