PostHog Event Tracking — Hello & Library Apps
Goal
Measure where users progress and where they drop out — from first website submission through activation (first video received). Custom events fill the gaps that auto-captured $pageview events can't cover: actions within a page, AI interactions, and cross-app conversion.
What We Want to Answer
| Question | How We Answer It |
|---|---|
| Where do users drop out of onboarding? | $pageview funnel across step URLs |
| Which objectives do users choose? | onboarding_objective_selected |
| Do users engage with AI features? | onboarding_ai_action with type breakdown |
| How many users hit the auth wall? | onboarding_auth_shown vs onboarding_published |
| Do published flows get shared? | library_flow_url_copied + library_try_yourself_clicked |
| How quickly do flows get their first response? | onboarding_published → library_first_video_received |
Design Principles
- URL-based funnels first — PostHog auto-captures
$pageviewon every route. Each step has its own URL. No custom events for navigation. - Custom events only where URLs aren't enough — In-page actions (generating questions, uploading a logo, picking an objective) are invisible to pageview tracking.
- Properties over events — One
onboarding_ai_actionwith atypeproperty instead of five separate events. Fewer events = simpler taxonomy. - Never break the app — All tracking calls are wrapped in try/catch. Analytics failures are swallowed silently.
Events Mapped to User Journey
Onboarding Flow (Hello App)
┌─────────────────────────────────────────────────────────────────────────────────┐
│ ONBOARDING FLOW │
│ │
│ /start │
│ ┌────────────────────────┐ │
│ │ Website URL form │ │
│ │ │──── onboarding_started ──────────────────┐ │
│ │ [Resume dialog] │──── onboarding_draft_resumed │ │
│ └────────────────────────┘ │ │
│ │ │ │
│ ▼ $pageview │ │
│ /d/{id}/objective │ │
│ ┌────────────────────────┐ │ │
│ │ Pick testimonial goal │ │ │
│ │ │──── onboarding_objective_selected │ │
│ │ (predefined or custom)│ { objective_id, is_custom } │ Events │
│ └────────────────────────┘ │ fire │
│ │ │ at key │
│ ▼ $pageview │ decision│
│ /d/{id}/plan-interview │ points │
│ ┌────────────────────────┐ │ within │
│ │ Confirm company info │ │ each │
│ │ USPs, target audience │──── onboarding_company_confirmed │ page │
│ │ │ { has_usps, usp_count } │ │
│ └────────────────────────┘ │ │
│ │ │ │
│ ▼ $pageview │ │
│ /d/{id}/configure ─── OR ── /d/{id}/studio │ │
│ ┌────────────────────────────────────────────────┐ │ │
│ │ Configure & customize the flow │ │ │
│ │ │ │ │
│ │ AI actions: │ │ │
│ │ Generate questions ─── onboarding_ai_action │ │ │
│ │ Regenerate questions { type: "..." } │ │ │
│ │ Generate more │ │ │
│ │ Generate image │ │ │
│ │ Translate │ │ │
│ │ │ │ │
│ │ Content: │ │ │
│ │ Add language ───── onboarding_language_added │ │ │
│ │ Upload asset ───── onboarding_asset_uploaded │ │ │
│ │ { asset_type, source } │ │ │
│ │ │ │ │
│ │ Navigation: │ │ │
│ │ → Studio ──── onboarding_exit_path │ │ │
│ │ → Publish ─── { path: "studio"|"publish" } │ │ │
│ └────────────────────────────────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌────────────────────────┐ │ │
│ │ "Test & Share" click │──── onboarding_publish_initiated │ │
│ │ │ { is_authenticated } │ │
│ └────────────────────────┘ │ │
│ │ │ │
│ ┌─────┴──────┐ │ │
│ ▼ ▼ │ │
│ Authed Not authed │ │
│ (publish) ┌────────────────┐ │ │
│ │ │ /d/{id}/publish│ │ │
│ │ │ Auth gate │──── onboarding_auth_shown │ │
│ │ └───────┬────────┘ │ │
│ │ │ (signs in) │ │
│ └────────┬───────┘ │ │
│ ▼ │ │
│ ┌────────────────────────┐ │ │
│ │ Publish to org │──┬─ onboarding_published ───────────────┘ │
│ │ │ │ { flow_id, organization_id } │
│ │ │ └─ onboarding_publish_failed (on error) │
│ └────────────────────────┘ │
│ │ │
└──────────────┼─────────────────────────────────────────────────────────────────┘
│
▼ redirect to Library
Post-Publish Activation (Library App)
┌─────────────────────────────────────────────────────────────────────────────┐
│ LIBRARY APP │
│ │
│ /dashboard/{flowId} ($pageview auto-captured) │
│ ┌──────────────────────────────────────────────┐ │
│ │ Flow management page │ │
│ │ │ │
│ │ Share actions: │ │
│ │ Copy URL ──── library_flow_url_copied │ │
│ │ Try it ──── library_try_yourself_clicked│ │
│ │ │ │
│ │ Activation: │ │
│ │ 0→1+ videos ── library_first_video_received │
│ │ (auto-detected via polling) │ │
│ │ │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ click on respondent │
│ /dashboard/{flowId}/{respondentId} │
│ ┌──────────────────────────────────────────────┐ │
│ │ Respondent detail page │ │
│ │ │ │
│ │ On page load ── library_video_viewed │ │
│ │ { flow_id, respondent_id } │ │
│ │ │ │
│ │ Export CSV ───── library_csv_exported │ │
│ │ │ │
│ └──────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────────┘
Event Reference
Onboarding Events
| # | Event | Where It Fires | Why It Exists |
|---|---|---|---|
| 1 | onboarding_started | StartStepForm — after draft created | Marks true funnel entry (user committed a URL, not just loaded the page) |
| 2 | onboarding_draft_resumed | StartStepForm — resume dialog | Measures return-user rate and which step they left off at |
| 3 | onboarding_objective_selected | ObjectiveStepForm — on select | Understand distribution of use cases (testimonials, case studies, etc.) |
| 4 | onboarding_company_confirmed | CompanyStepForm — Continue click | Gate between passive (auto-extracted) and active (user confirmed) data |
| 5 | onboarding_ai_action | ConfigureStepForm, StudioStepForm | Measure AI feature adoption — are users generating, regenerating, translating? |
| 6 | onboarding_language_added | ConfigureStepForm, StudioStepForm | Track multi-language adoption and total language count |
| 7 | onboarding_asset_uploaded | StudioStepForm | Understand which asset sources users prefer (upload vs AI vs Unsplash) |
| 8 | onboarding_exit_path | ConfigureStepForm | Measure configure → studio vs configure → direct publish split |
| 9 | onboarding_publish_initiated | StudioStepForm — "Test & Share" | Captures intent to publish (before auth gate may interrupt) |
| 10 | onboarding_auth_shown | AuthGateForm — on mount | Measure how many users hit the auth wall, compare to publish success |
| 11 | onboarding_published | usePublishFlow — on success | Core conversion event — draft became a live flow |
| 12 | onboarding_publish_failed | usePublishFlow — on error | Track publish failures for debugging and reliability monitoring |
Library Events
| # | Event | Where It Fires | Why It Exists |
|---|---|---|---|
| 13 | library_flow_url_copied | FlowManageUI — copy button | First signal of distribution intent |
| 14 | library_try_yourself_clicked | FlowManageUI — "Try it yourself" link | User previewing their own flow (engagement signal) |
| 15 | library_video_viewed | RespondentViewWithDelete — on mount | Track which respondent videos users actually watch |
| 16 | library_csv_exported | useVideoDashboardExport — export click | Data export as power-user engagement signal |
| 17 | library_first_video_received | FlowManageUI — 0→1+ video transition | Key activation metric — flow received its first real response |
Funnels
Primary Onboarding Funnel
Built from auto-captured $pageview events plus the final custom conversion event:
Each arrow is a drop-off point. The funnel answers: At which step do most users abandon?
Auth Conversion Funnel
Specific to unauthenticated users:
Answers: What percentage of unauthenticated users complete signup and publish?
Post-Publish Activation Funnel
Measures what happens after a flow goes live:
Answers: Do users share their flow? Does it actually receive responses?
What We Intentionally Don't Track
| Would-be Event | Reason |
|---|---|
onboarding_step_viewed | Auto-captured via $pageview on each step URL |
onboarding_abandoned | Derived from $pageleave + funnel drop-off analysis |
library_page_viewed | Auto-captured via $pageview |
library_login / library_signup | Handled by Clerk identity resolution in shared PostHog provider |
| Per-field edits (company name, question text) | Too granular, not actionable — adds noise |
| Video playback controls (play/pause/seek) | Belongs to Project B (end-user analytics), not Project A |
Technical Implementation
Type-Safe Event Module
All events are defined in packages/core/src/web/posthog-events.ts:
// Typed event map — compile-time safety
export type HCEventMap = {
onboarding_started: HCEvents.OnboardingStarted;
onboarding_published: HCEvents.OnboardingPublished;
library_flow_url_copied: HCEvents.LibraryFlowUrlCopied;
// ...
};
// Generic capture function — wrong properties = compile error
export function captureHCEvent<T extends HCEventName>(
event: T,
properties: HCEventMap[T]
): void;
// Convenience wrappers for each event
export function trackOnboardingStarted(props: HCEvents.OnboardingStarted): void;
export function trackLibraryFlowUrlCopied(props: HCEvents.LibraryFlowUrlCopied): void;
Where Events Are Called
apps/hello/
├── components/steps/
│ ├── StartStepForm.tsx → onboarding_started, onboarding_draft_resumed
│ ├── ObjectiveStepForm.tsx → onboarding_objective_selected
│ ├── CompanyStepForm.tsx → onboarding_company_confirmed
│ ├── ConfigureStepForm.tsx → onboarding_ai_action, onboarding_language_added,
│ │ onboarding_exit_path
│ ├── StudioStepForm.tsx → onboarding_ai_action, onboarding_asset_uploaded,
│ │ onboarding_language_added, onboarding_publish_initiated
│ └── AuthGateForm.tsx → onboarding_auth_shown
├── hooks/
│ └── usePublishFlow.ts → onboarding_published, onboarding_publish_failed
packages/app-library/src/free/
│ └── FlowManageUI.tsx → library_flow_url_copied, library_try_yourself_clicked,
│ library_first_video_received
apps/library/
├── components/
│ └── RespondentViewWithDelete.tsx → library_video_viewed
├── hooks/
│ └── useVideoDashboardExport.ts → library_csv_exported
Relationship to PostHog Projects
These events go to Project A (HappyClient Users) — tracking our users, not their end-users. Project B (Video Flow End-Users) is a separate concern handled by @repo/app-video-flow.
See posthog-analytics.md for the two-project architecture.
Related Documents
- hello-app-architecture.md — Onboarding app data flow and component hierarchy
- posthog-analytics.md — Two-project PostHog setup (Project A vs Project B)
- hello-vs-library-user-flow.md — End-to-end user journey