Bonds Analytics Catalog
Comprehensive reference for all Mixpanel tracking in the Bonds app. Module: src/utils/analytics.js.
For product: This document lists every event, property, user attribute, and funnel tracked in the app. Use the table of contents to navigate.
For developers: When adding or changing events, update this document in the same PR. See the Developer Checklist at the bottom.
Table of Contents
- Conventions
- Identity & Properties
- Screens
- Domain Events
- Element Clicked Reference
- Funnels
- Recommended Mixpanel Reports
- Developer Checklist
1. Conventions
Naming Rules
| What | Convention | Example |
|---|---|---|
| Event names | Title Case, Object-Action, past tense | Simulator Session Completed |
| Property names | snake_case | chapter_index, skill_level |
| Screen names | lowercase, underscore-separated | learn_video, simulator_results |
When to Use Which Event Type
| Pattern | When to use | Example |
|---|---|---|
| Domain event (own name) | Funnel steps, business-critical actions | Onboarding Completed, Daily Question Voted |
Screen Viewed |
A distinct screen becomes visible | Screen Viewed { screen: 'journey' } |
Element Clicked |
UI interaction (button, tab, card, toggle) | Element Clicked { screen: 'profile', element: 'invite_partner' } |
Property Value Enums
| Property | Possible values |
|---|---|
platform |
ios, android, web |
user_type |
anonymous, authenticated |
step_type |
learn, practice, insight, act |
mode |
freeform, journey |
input_mode (simulator) |
voice, text |
method (auth) |
google, apple, email |
element_type |
button, tab, banner, card, toggle, link |
delivery (report) |
clipboard, slack |
2. Identity & Properties
2a. Identity Lifecycle
┌─────────────────┐ identifyUser() ┌──────────────────┐
│ Anonymous │ ─────────────────────→ │ Authenticated │
│ (random ID) │ │ (Bubble user ID) │
│ user_type: │ │ user_type: │
│ 'anonymous' │ resetIdentity() │ 'authenticated' │
│ │ ←───────────────────── │ │
└─────────────────┘ └──────────────────┘
| Stage | Trigger | What happens |
|---|---|---|
| App opens, no auth | initAnalytics() |
Anonymous distinct_id assigned, super props registered |
| User authenticates | identifyUser(userId, traits) via UserContext |
identify(userId) merges anonymous→identified, people props set |
| User logs out | resetIdentity() |
reset() clears identity, back to anonymous with fresh ID |
2b. Super Properties
Auto-attached to every event. Set by initAnalytics(), updated by identifyUser().
| Property | Type | Example | Description |
|---|---|---|---|
app_version |
string | v1.0.0-alpha-bc22c80df36e |
Build version with content hash |
platform |
string | ios |
Detected from window.natively (ios / android / web) |
user_type |
string | authenticated |
anonymous until first identifyUser(), then authenticated |
2c. People Properties (User Profile)
Stored on the Mixpanel user profile. Visible in the People section.
| Property | Type | Example | Set by | When updated |
|---|---|---|---|---|
$name |
string | "Sarah" |
identifyUser() |
First auth only |
$email |
string | "sarah@example.com" |
identifyUser() |
First auth only |
coins |
number | 150 |
identifyUser() + setUserProperties() |
Every setUserData call |
partner_connected |
boolean | true |
identifyUser() + setUserProperties() |
Every setUserData call |
partner_name_collected |
boolean | true |
setUserProperties() |
Every setUserData call where partnerName is present |
partner_gender |
string | "female" |
setUserProperties() |
Every setUserData call where partnerGender is present |
onboarding_complete |
boolean | true |
identifyUser() |
First auth only |
signup_date |
string (ISO) | "2026-01-15T10:30:00Z" |
set_once |
First auth ever (never overwritten) |
initial_source |
string | "Ross2" |
set_once via identifyUser() |
First auth ever (never overwritten) |
initial_utm_source |
string | "Facebook" |
set_once via identifyUser() |
First auth ever (never overwritten) |
initial_utm_medium |
string | "Paid" |
set_once via identifyUser() |
First auth ever (never overwritten) |
initial_utm_campaign |
string | "Default_Campaign" |
set_once via identifyUser() |
First auth ever (never overwritten) |
initial_through |
string | "partner-x" |
set_once via identifyUser() |
First auth ever (never overwritten) |
last_source |
string | "Ross2" |
set via identifyUser() / captureAttribution() |
Every new campaign click |
last_utm_source |
string | "Facebook" |
set via identifyUser() / captureAttribution() |
Every new campaign click |
last_utm_medium |
string | "Paid" |
set via identifyUser() / captureAttribution() |
Every new campaign click |
last_utm_campaign |
string | "Default_Campaign" |
set via identifyUser() / captureAttribution() |
Every new campaign click |
last_through |
string | "partner-x" |
set via identifyUser() / captureAttribution() |
Every new campaign click |
is_subscribed |
boolean | true |
setUserProperties() via EntitlementContext |
Every entitlement refresh (auth, foreground, post-purchase, post-restore) |
subscription_type |
string | "yearly" |
setUserProperties() via UserContext |
Every setUserData call where subscriptionType is present |
Cohort tag: the A/B/C payments cohort's source of truth is the Bubble User field
payment_variant(Statsig only seeds it once — seedocs/statsig-experiments.md). It's carried on every event by a single super-property,payment_variant(the effective value incl. admin overrides, set by<PaymentVariantSeeder>) — use this for all cohort filtering. The raw Statsig bucket is not mirrored to Mixpanel asexperiment_payments(kept to one property to avoid confusion); Statsig retains the exposure for experiment analysis on its side. There is nopayments_groupPeople property.
3. Screens
All values used with Screen Viewed. Fired via screen(name) helper which calls track('Screen Viewed', { screen: name }).
| Screen | When shown | Source | Notes |
|---|---|---|---|
welcome |
App loads without active session | App.jsx | |
signin |
User taps "Sign In" | App.jsx | |
signup |
After onboarding insight, before account creation | App.jsx | |
onboarding_intro |
One-time intro video before questions | App.jsx | |
onboarding |
User starts or resumes onboarding | App.jsx | |
onboarding_insight |
During insight generation/playback | App.jsx | |
journey |
Journey tab selected | MainTabs.jsx | |
simulator |
Simulator tab selected | MainTabs.jsx | |
fun |
Fun Zone tab selected | MainTabs.jsx | |
profile |
Profile tab selected | MainTabs.jsx | |
step_review |
User opens a completed step for review | StepReview.jsx | Extra props: step_type, chapter |
ftue_walkthrough |
First-time user walkthrough starts | MainTabs.jsx | |
daily_question |
Daily question overlay opened | DailyQuestion.jsx | |
push_prompt |
Push notification permission dialog shown | MainTabs.jsx | |
name_prompt |
Name entry dialog after signup | MainTabs.jsx | |
partner_name_prompt |
Partner name entry dialog (after user name dialog) | MainTabs.jsx | |
account_info |
Account info / danger zone screen | MainTabs.jsx | |
share_viewer |
Shared content viewer (external link) | ShareViewer.jsx |
4. Domain Events
App Lifecycle
App Opened
Fires once on app launch after analytics initialization.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: App.jsx
App Backgrounded
Fires when the app moves to the background (Natively lifecycle hook).
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: App.jsx
App Foregrounded
Fires when the app returns to the foreground (Natively lifecycle hook).
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: App.jsx
Logged Out
Fires when user logs out. Identity is reset after this event.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: App.jsx
Session Force Logout
Fires when Bubble force-logs the user out via setLoginState(false) while they were in an authenticated phase (no React-initiated logout pending). Most likely cause is the WebView losing its session cookie (_u2main) across a process restart, which triggers Bubble's "User is logged out" event server-side. Tracked into Mixpanel so we can monitor frequency and surface in retention/funnels alongside other auth events.
| Property | Type | Example | Required |
|---|---|---|---|
phase |
string | "main" |
yes |
source |
string | "bubble" |
yes |
Source: App.jsx (in setLoginState(false) handler when source === 'bubble')
Deep Link Opened
Fires when a deep link is processed. Attribution params (if present in the URL) are spread into the event properties.
| Property | Type | Example | Required |
|---|---|---|---|
tab |
string | null | "journey" |
no |
view |
string | null | "chapter_1" |
no |
step_status |
string | undefined | "completed" |
no |
source |
string | "Ross2" |
no (from attribution) |
utm_source |
string | "Facebook" |
no (from attribution) |
utm_medium |
string | "Paid" |
no (from attribution) |
utm_campaign |
string | "Default_Campaign" |
no (from attribution) |
through |
string | "partner-x" |
no (from attribution) |
Source: MainTabs.jsx
Attribution Captured
Fires when a URL with attribution params is opened (cold start or warm start), regardless of whether a deep link route is present. Independent of Deep Link Opened.
| Property | Type | Example | Required |
|---|---|---|---|
source |
string | "Ross2" |
no |
through |
string | "partner-x" |
no |
utm_source |
string | "Facebook" |
no |
utm_medium |
string | "Paid" |
no |
utm_campaign |
string | "Default_Campaign" |
no |
Source: analytics.js via captureAttribution()
Authentication
Sign In Started
Fires when user initiates sign-in (OAuth or email).
| Property | Type | Example | Required |
|---|---|---|---|
method |
string | "google" / "apple" / "email" |
yes |
Source: SignInScreen.jsx
Sign In Completed
Fires on first successful identity merge (anonymous → authenticated). Reads bonds_auth_method from localStorage to detect email auth; defaults to "oauth" for SSO.
| Property | Type | Example | Required |
|---|---|---|---|
method |
string | "google" / "apple" / "email" / "oauth" |
yes |
Source: UserContext.jsx
Sign In Failed
Fires when sign-in fails (wrong password, Bubble error).
| Property | Type | Example | Required |
|---|---|---|---|
error |
string | "wrong_password" / "not_found" |
yes |
Source: App.jsx (via setSignInError)
Sign In Blocked
Fires when sign-in detects a non-existing account (UID comparison or Bubble search).
| Property | Type | Example | Required |
|---|---|---|---|
reason |
string | "account_not_found" |
yes |
Source: App.jsx (via onSigninComplete)
Onboarding
Onboarding Step Completed
Fires after each onboarding question is answered.
| Property | Type | Example | Required |
|---|---|---|---|
step |
number | 2 |
yes |
question_id |
string | "comm_style" |
yes |
type |
string | "single-select" / "slider" / "multi-select" / "open-question" |
yes |
answer_value |
string | "Direct" |
yes |
partner_gender |
string|null | "female" / "male" / "other" / null |
step 0 only — partner gender follow-up |
Source: OnboardingFlow.jsx
Onboarding Completed
Fires when user finishes all onboarding steps.
| Property | Type | Example | Required |
|---|---|---|---|
total_steps |
number | 12 |
yes |
coins |
number | 12 |
yes |
loveLanguage |
string | "Words of Affirmation" |
no |
partnerLoveLanguage |
string | "Physical Touch" |
no |
skippedTiebreaker |
boolean | true |
no |
partner_gender |
string|null | "female" / "male" / "other" / null |
no |
Source: OnboardingFlow.jsx
Onboarding Abandoned
Fires when user exits onboarding via back button on step 0.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: App.jsx
Onboarding Resumed
Fires when onboarding session is resumed, either automatically on app reload (source: 'auto') or when user chooses to continue via the resume dialog (source: 'dialog').
| Property | Type | Example | Required |
|---|---|---|---|
phase |
string | "questions" / "signup" / "insight" |
yes |
step |
number/string | 5 / "signup" / "insight" |
yes |
source |
string | "auto" / "dialog" |
yes |
Source: App.jsx
Onboarding Intro Viewed
Fires when the one-time intro video screen is mounted.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingIntro.jsx
Onboarding Intro Completed
Fires when user taps the CTA after the intro video has finished playing.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingIntro.jsx
Onboarding Intro Skipped
Fires when user taps the CTA before the intro video finishes (skips remaining video).
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingIntro.jsx
Onboarding Intro Replayed
Fires when user taps replay to watch the intro video again.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingIntro.jsx
Onboarding Intro Backed Out
Fires when user navigates back from the intro screen to welcome.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingIntro.jsx
Onboarding Insight Started
Fires when insight generation begins after all questions answered.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingInsight.jsx
Onboarding Insight Generated
Fires when insight data is received from Bubble.
| Property | Type | Example | Required |
|---|---|---|---|
insight_id |
string | "ins_abc123" |
yes |
Source: OnboardingInsight.jsx
Onboarding Insight Playback Completed
Fires when TTS playback finishes and user taps Continue.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingInsight.jsx
Onboarding Insight Playback Skipped
Fires when user taps Continue before TTS playback finishes.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingInsight.jsx
Onboarding Insight CTA Tapped
Fires when user taps "Create Your Account" after insight playback.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingInsight.jsx
Onboarding Insight Timed Out
Fires when insight generation exceeds the 90s timeout.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OnboardingInsight.jsx
Onboarding Data Connected
Fires after signup when onboarding data is sent to Bubble for the new user.
| Property | Type | Example | Required |
|---|---|---|---|
coins |
number | 10 |
yes |
answer_count |
number | 10 |
yes |
insight_id |
string | "ins_abc123" |
yes |
Source: App.jsx
Onboarding Step Back
Fires when user navigates backward during onboarding.
| Property | Type | Example | Required |
|---|---|---|---|
from_step |
number | 8 |
yes |
to_step |
number | 7 |
yes |
Source: OnboardingFlow.jsx
Onboarding Mic Permission Denied
Fires when microphone permission is denied during onboarding open question.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: OpenQuestion.jsx
Onboarding Mic Unavailable
Fires when microphone/speech recognition is unavailable for a non-permission reason.
| Property | Type | Example | Required |
|---|---|---|---|
reason |
string | "network" / "no_api" |
yes |
Source: OpenQuestion.jsx
Onboarding Speech API Unavailable
Fires when SpeechRecognition API doesn't exist on the device and dialog auto-switches to text mode.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: InsightOtherDialog.jsx
Signup
Sign Up Started
Fires when user initiates sign-up (OAuth or email).
| Property | Type | Example | Required |
|---|---|---|---|
method |
string | "google" / "apple" / "email" |
yes |
Source: SignUpScreen.jsx
Sign Up Completed
Fires on successful signup. Detects email vs OAuth via bonds_auth_method localStorage flag.
| Property | Type | Example | Required |
|---|---|---|---|
method |
string | "email" / "oauth" |
yes |
Source: App.jsx
Sign Up Failed
Fires when sign-up fails (Bubble error).
| Property | Type | Example | Required |
|---|---|---|---|
error |
string | "already_exists" |
yes |
Source: App.jsx (via setSignUpError)
Sign Up Blocked
Fires when sign-up detects an existing account (UID comparison or Bubble search).
| Property | Type | Example | Required |
|---|---|---|---|
reason |
string | "existing_account" |
yes |
Source: App.jsx (via onSignupComplete)
Email Authentication
Email OTP Sent
Fires when an OTP code is successfully sent to the user's email.
| Property | Type | Example | Required |
|---|---|---|---|
flow |
string | "signup" / "forgot" |
yes |
Source: SignUpScreen.jsx, SignInScreen.jsx
Email OTP Verified
Fires when user successfully verifies the OTP code.
| Property | Type | Example | Required |
|---|---|---|---|
flow |
string | "signup" / "forgot" |
yes |
Source: SignUpScreen.jsx, SignInScreen.jsx
Email OTP Failed
Fires when OTP verification fails (wrong or expired code).
| Property | Type | Example | Required |
|---|---|---|---|
flow |
string | "signup" / "forgot" |
yes |
error |
string | "invalid" / "expired" |
yes |
Source: SignUpScreen.jsx, SignInScreen.jsx
Email OTP Resent
Fires when user taps resend code.
| Property | Type | Example | Required |
|---|---|---|---|
flow |
string | "signup" / "forgot" |
yes |
Source: SignUpScreen.jsx, SignInScreen.jsx
Password Reset Completed
Fires after a successful password reset + auto sign-in.
Source: SignInScreen.jsx
Sharing
Share Created
Fires when user creates a shareable link.
| Property | Type | Example | Required |
|---|---|---|---|
content_type |
string | "insight" / "simulator_results" / "activity" |
yes |
content_id |
string | "abc123" |
yes |
Source: InsightPlayback.jsx, SimulatorResults.jsx, ActivityView.jsx
Share Viewed
Fires when someone views a shared link.
| Property | Type | Example | Required |
|---|---|---|---|
content_type |
string | "insight" / "simulator_results" / "activity" |
yes |
content_id |
string | "abc123" |
yes |
Source: ShareViewer.jsx
Account
Account Deleted
Fires when user confirms account deletion.
Source: MainTabs.jsx (AccountInfoPage)
Onboarding Question Skipped
Fires when user skips an open-ended onboarding question.
| Property | Type | Example | Required |
|---|---|---|---|
question |
string | "What do you wish your partner would do more of" |
yes |
Source: OpenQuestion.jsx
Journey
Chapter numbering convention. Two property names coexist for historical reasons, both 1-based:
chapter— used by step-level events (Chapter Started,Chapter Completed,Journey Step Started/Completed/Abandoned,Journey Video Watched,Journey Practice Started/Completed). Value is the chapter'sindexfrom Bubble (thetemp_indexfield).chapter_number— used by chapter-level / UI events (Chapter Confirmation *,Chapter Start Tapped,Chapter Step Start Tapped,Chapter Paused State Shown,Chapter Menu Opened,Chapter Menu Chapter Selected).They agree numerically. When building a funnel across both groups, Mixpanel's "formula" column can alias one to the other.
dynamicsegmentation. Every chapter-scoped event carries a booleandynamicproperty (Phase 2 dynamic chapters vs predefined). Use it to split funnels and retention by chapter kind. Predefined chapters reportdynamic: false; LLM-generated 3-node chapters reportdynamic: true. ForSimulator Session *events the property is present only whenmode === 'journey'.
Chapter Started
Fires when the user pays 6 coins via the coin gate and a new chapter begins. Not tied to a specific step — mirrors Chapter Completed.
| Property | Type | Example | Required |
|---|---|---|---|
chapter |
number | 0 |
yes |
chapter_title |
string | "Active Listening" |
yes |
coin_cost |
number | 6 |
yes |
dynamic |
boolean | false |
yes |
Source: JourneyPath.jsx
Journey Step Started
Fires when a journey step begins (learn, practice, insight, or act).
| Property | Type | Example | Required |
|---|---|---|---|
step_type |
string | "learn" |
yes |
chapter |
number | 0 |
yes |
chapter_title |
string | "Active Listening" |
yes |
dynamic |
boolean | false |
yes |
Source: ChapterFlow.jsx
Journey Step Completed
Fires when a journey step finishes successfully.
| Property | Type | Example | Required |
|---|---|---|---|
step_type |
string | "practice" |
yes |
chapter |
number | 0 |
yes |
coins |
number | 3 |
yes |
replay |
boolean | false |
yes |
dynamic |
boolean | false |
yes |
Source: ChapterFlow.jsx
Journey Step Abandoned
Fires when user exits a journey step without completing it.
| Property | Type | Example | Required |
|---|---|---|---|
step_type |
string | "learn" |
yes |
chapter |
number | 0 |
yes |
dynamic |
boolean | false |
yes |
Source: ChapterFlow.jsx
Journey Video Watched
Fires when the learn video finishes playing.
| Property | Type | Example | Required |
|---|---|---|---|
chapter |
number | 0 |
yes |
dynamic |
boolean | false |
yes |
Source: LearnStep.jsx
Journey Practice Started
Fires when user starts a practice simulator session from the journey.
| Property | Type | Example | Required |
|---|---|---|---|
chapter |
number | 0 |
yes |
dynamic |
boolean | false |
yes |
Source: PracticeStep.jsx
Journey Practice Completed
Fires when practice simulator session ends with a score.
| Property | Type | Example | Required |
|---|---|---|---|
chapter |
number | 0 |
yes |
score |
number | 4 |
yes |
coins |
number | 3 |
yes |
attempt |
number | 1 |
yes |
dynamic |
boolean | false |
yes |
Source: PracticeStep.jsx
Chapter Completed
Fires when all steps in a chapter are done.
| Property | Type | Example | Required |
|---|---|---|---|
chapter |
number | 0 |
yes |
chapter_title |
string | "Active Listening" |
yes |
dynamic |
boolean | false |
yes |
Source: ChapterFlow.jsx
Chapter Start Tapped
Fires when the user taps the external "START CHAPTER · N COINS" button on the journey map. This button replaces the in-bubble START for the first step of a chapter (PRD Phase 1, page 2).
| Property | Type | Example | Required |
|---|---|---|---|
screen |
string | "journey_map" |
yes |
chapter_number |
number | 1 |
yes |
via |
string | "external_cta" |
yes |
step_type |
string | "learn" |
yes |
dynamic |
boolean | false |
yes |
Source: JourneyScreen.jsx
Chapter Step Start Tapped
Fires when the user taps the in-bubble START button on a non-first step (Practice/Insight/Act) on the journey map.
| Property | Type | Example | Required |
|---|---|---|---|
screen |
string | "journey_map" |
yes |
chapter_number |
number | 1 |
yes |
via |
string | "bubble" |
yes |
step_type |
string | "practice" / "insight" / "act" |
yes |
dynamic |
boolean | false |
yes |
Source: JourneyScreen.jsx
Chapter Paused State Shown
Fires when the paused view is rendered the day after a chapter is completed (PRD Phase 1, page 4). Deduped by completed → next pair so a single paused session fires the event once.
| Property | Type | Example | Required |
|---|---|---|---|
screen |
string | "chapter_paused" |
yes |
next_chapter_number |
number | 2 |
yes |
completed_chapter_number |
number | 1 |
yes |
dynamic |
boolean | false |
yes |
Source: JourneyScreen.jsx
Chapter Confirmation Shown
Fires when a predefined confirmation card is shown. Two sources — when building the Confirmation → Start → Complete funnel, union screen ∈ { ch1_predefined_confirmation, chapter_predefined_confirmation }:
- Ch1 — embedded as the last FTUE walkthrough slide (PRD Phase 1, page 5) with
screen: "ch1_predefined_confirmation". - Ch2 / Ch3 — standalone bottom sheet shown at session start if the active chapter is an unstarted Ch2 or Ch3 and the user hasn't yet responded (PRD Phase 1, page 8 — Figma 1590:1791). Evaluated once per session; not re-triggered on chapter scroll. Uses
screen: "chapter_predefined_confirmation".
| Property | Type | Example | Required | Notes |
|---|---|---|---|---|
screen |
string | "ch1_predefined_confirmation" | "chapter_predefined_confirmation" |
yes | |
step |
number | 4 |
Ch1 only | FTUE step index |
chapter_number |
number | 1, 2, 3 |
yes | |
chapter_id |
string | "1776754765374x…" |
Ch2/Ch3 | Bubble chapter id |
chapter_title |
string | "Slowing the Reaction" |
Ch2/Ch3 | |
is_replay |
boolean | false |
Ch1 only | |
dynamic |
boolean | false |
yes | chapter kind (Phase 2) |
Source: DrLeoWalkthrough.jsx (Ch1), JourneyPath.jsx (Ch2 / Ch3).
Chapter Confirmation Accepted
Fires when the user taps "Start Chapter" on the predefined confirmation card (Ch1 FTUE or Ch2/Ch3 standalone sheet).
| Property | Type | Example | Required | Notes |
|---|---|---|---|---|
screen |
string | "ch1_predefined_confirmation" | "chapter_predefined_confirmation" |
yes | |
chapter_number |
number | 1, 2, 3 |
yes | |
chapter_id |
string | "1776754765374x…" |
Ch2/Ch3 | |
chapter_title |
string | "Slowing the Reaction" |
Ch2/Ch3 | |
via |
string | "start_chapter" |
yes | |
dynamic |
boolean | false |
yes | chapter kind (Phase 2) |
Source: DrLeoWalkthrough.jsx (Ch1), JourneyPath.jsx (Ch2 / Ch3).
Chapter Confirmation Dismissed
Fires when the user taps "No thanks, later" on the predefined confirmation card.
| Property | Type | Example | Required | Notes |
|---|---|---|---|---|
screen |
string | "ch1_predefined_confirmation" | "chapter_predefined_confirmation" |
yes | |
chapter_number |
number | 1, 2, 3 |
yes | |
chapter_id |
string | "1776754765374x…" |
Ch2/Ch3 | |
chapter_title |
string | "Slowing the Reaction" |
Ch2/Ch3 | |
via |
string | "no_thanks_later" |
yes | |
dynamic |
boolean | false |
yes | chapter kind (Phase 2) |
Source: DrLeoWalkthrough.jsx (Ch1), JourneyPath.jsx (Ch2 / Ch3).
FTUE First Slide Shown
Fires when the FTUE first slide ("Generating your chapter…") is shown (PRD Phase 1, page 5).
| Property | Type | Example | Required |
|---|---|---|---|
screen |
string | "ftue_first_slide" |
yes |
step |
number | 0 |
yes |
is_replay |
boolean | false |
yes |
Source: DrLeoWalkthrough.jsx
FTUE Skipped
Fires when the user taps Skip on any FTUE step. Per PRD §6, Skip now jumps to the last FTUE step instead of closing the walkthrough.
| Property | Type | Example | Required |
|---|---|---|---|
screen |
string | "ftue_first_slide" | "ftue_walkthrough" |
yes |
step |
number | 0 |
yes |
is_replay |
boolean | false |
yes |
Source: DrLeoWalkthrough.jsx
Chapter Menu Opened
Fires on the OPEN transition of the chapter-list dropdown (chevron tap when the menu was closed). Does not fire on close — use the companion Element Clicked { element: 'chapter_menu_chevron' } event for bi-directional tap counts.
| Property | Type | Example | Required | Notes |
|---|---|---|---|---|
screen |
string | "journey_map" |
yes | |
chapter_number |
number | 2 |
yes | focal chapter at the moment of tap (1-based) |
chapter_id |
string | "1776754765374x…" |
yes | Bubble chapter id |
dynamic |
boolean | false |
yes | focal chapter's kind |
Source: JourneyScreen.jsx
Chapter Menu Chapter Selected
Fires when the user picks a chapter from the chapter-list dropdown. Funnels dropdown engagement → scroll navigation.
| Property | Type | Example | Required | Notes |
|---|---|---|---|---|
screen |
string | "journey_map" |
yes | |
from_chapter_number |
number | 1 |
yes | 1-based; the user's active chapter at tap time |
to_chapter_number |
number | 3 |
yes | 1-based; the chapter they picked |
to_chapter_id |
string | "1776754765374x…" |
yes | |
to_chapter_title |
string | "Slowing the Reaction" |
yes | |
dynamic |
boolean | false |
yes | target chapter's kind |
Source: JourneyPath.jsx
Insight
Insight Flow Started
Fires when an insight generation flow begins.
| Property | Type | Example | Required |
|---|---|---|---|
type |
string | "learn" / "activity" / "journey" |
yes |
learn_type_id |
string | "lt_01" |
when type=learn |
activity_id |
string | "act_01" |
when type=activity |
chapter |
number | 0 |
when type=journey |
replay |
boolean | false |
when type=journey |
dynamic |
boolean | false |
when type=journey |
Source: InsightFlow.jsx · InsightStep.jsx
Insight Question Answered
Fires per insight question response.
| Property | Type | Example | Required |
|---|---|---|---|
answer |
string | "yes" / "no" / custom text |
yes |
Source: InsightFlow.jsx · InsightStep.jsx
Insight Generated
Fires when all insight questions are answered and generation is triggered. Currently only fires for type: 'learn'.
| Property | Type | Example | Required |
|---|---|---|---|
type |
string | "learn" |
yes |
learn_type_id |
string | "lt_01" |
yes |
Source: InsightFlow.jsx
Insight Generation Timed Out
Fires when insight generation exceeds the timeout.
| Property | Type | Example | Required |
|---|---|---|---|
type |
string | "learn" / "activity" / "journey" |
yes |
learn_type_id |
string | "lt_01" |
when type=learn (always sent, may be null) |
activity_id |
string | "act_01" |
when type=activity (always sent, may be null) |
chapter |
number | 0 |
when type=journey |
dynamic |
boolean | false |
when type=journey |
Source: InsightFlow.jsx (learn/activity) · InsightStep.jsx (journey)
Insight Playback Completed
Fires when TTS playback finishes and user taps Continue.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: InsightFlow.jsx · InsightStep.jsx
Insight Playback Skipped
Fires when user taps Continue before TTS playback finishes.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: InsightFlow.jsx · InsightStep.jsx
Insight Flow Closed
Fires when user exits the insight flow without completing playback.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: InsightFlow.jsx
Simulator
Simulator Session Started
Fires when a simulator session begins (freeform or journey practice).
| Property | Type | Example | Required |
|---|---|---|---|
mode |
string | "journey" / "freeform" |
yes |
input_mode |
string | "voice" / "text" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Session Completed
Fires when AI evaluation is received and results are shown.
| Property | Type | Example | Required |
|---|---|---|---|
score |
number | 4 |
yes |
skill_level |
string | "Finding Your Groove" |
yes |
mode |
string | "journey" / "freeform" |
yes |
input_mode |
string | "voice" / "text" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Session Closed
Fires when user manually ends a session (via close dialog).
| Property | Type | Example | Required |
|---|---|---|---|
mode |
string | "journey" / "freeform" |
yes |
input_mode |
string | "voice" / "text" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Session Retried
Fires when user retries the session from the results screen.
| Property | Type | Example | Required |
|---|---|---|---|
mode |
string | "journey" / "freeform" |
yes |
input_mode |
string | "voice" / "text" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Session Error
Fires on unrecoverable session error. For the JSON-extraction boundaries (Stage 1, Stage 3), this only fires after parse retries are exhausted — see Simulator Issue Retry / Simulator Eval Retry below for the per-attempt signal.
| Property | Type | Example | Required |
|---|---|---|---|
code |
string | "TIMEOUT" / "EVAL_PARSE_FAILED" / "ISSUE_PARSE_FAILED" / "UNKNOWN" |
yes |
message |
string | "Connection lost" |
yes |
mode |
string | "journey" / "freeform" |
yes |
input_mode |
string | "voice" / "text" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Eval Retry
Fires per attempt when the Stage 3 evaluation JSON fails to parse and we re-fire the silent response.create on the same session. After today's tool-calling change this should be near-zero in production — the model is structurally forced into schema-valid JSON. Kept as belt-and-suspenders.
| Property | Type | Example | Required |
|---|---|---|---|
attempt |
number | 1 |
yes |
max_retries |
number | 2 |
yes |
raw_length |
number | 412 |
yes |
raw_text_hash |
string | "a3f1b29c" (FNV-1a fingerprint of raw response, no PII) |
yes |
mode |
string | "journey" / "freeform" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Eval Retry Recovered
Fires once when a Stage 3 eval succeeds after at least one retry. Lets analytics distinguish first-try clean sessions from "drift caught and recovered" sessions, both of which otherwise look identical at Simulator Session Completed.
| Property | Type | Example | Required |
|---|---|---|---|
attempts_total |
number | 2 (1 retry + final success = 2) |
yes |
mode |
string | "journey" / "freeform" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Issue Retry
Stage 1 (issue-extraction) counterpart to Simulator Eval Retry. Same shape, same near-zero post-tool-call expectation.
| Property | Type | Example | Required |
|---|---|---|---|
attempt |
number | 1 |
yes |
max_retries |
number | 2 |
yes |
raw_length |
number | 188 |
yes |
raw_text_hash |
string | "7e22c014" |
yes |
mode |
string | "journey" / "freeform" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Simulator Issue Retry Recovered
Stage 1 counterpart to Simulator Eval Retry Recovered.
| Property | Type | Example | Required |
|---|---|---|---|
attempts_total |
number | 2 (1 retry + final success = 2) |
yes |
mode |
string | "journey" / "freeform" |
yes |
chapter_id |
string | "ch_01" |
when mode=journey |
dynamic |
boolean | false |
when mode=journey |
Source: SimulatorSection.jsx
Coins Deducted
Fires when coins are spent to unlock stage 2 of a freeform simulator session.
| Property | Type | Example | Required |
|---|---|---|---|
amount |
number | 1 |
yes |
context |
string | "simulator" |
yes |
Source: SimulatorSection.jsx
Coins Gifted
Fires when the user receives a one-time coin gift (e.g. from the "Not Enough Coins" dialog).
| Property | Type | Example | Required |
|---|---|---|---|
amount |
number | 3 |
yes |
previousBalance |
number | 0 |
yes |
Source: NotEnoughCoinsDialog.jsx
Activities
Activity Create Started
Fires when user initiates creating a new activity.
| Property | Type | Example | Required |
|---|---|---|---|
act_type_id |
string | "affirmation" |
yes |
Source: ActivityView.jsx
Activity View Completed
Fires when user finishes viewing/listening to an activity.
| Property | Type | Example | Required |
|---|---|---|---|
activity_id |
string | "act_abc123" |
yes |
replay |
boolean | false |
yes |
Source: ActivityView.jsx
Activity View Closed
Fires when user closes an activity without completing it.
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: ActivityView.jsx
Daily Question
Daily Question Viewed
Fires when the daily question appears on screen.
| Property | Type | Example | Required |
|---|---|---|---|
question_id |
string | "dq_20260318" |
yes |
category |
string | "intimacy" |
yes |
Source: DailyQuestion.jsx
Daily Question Voted
Fires when user selects an answer.
| Property | Type | Example | Required |
|---|---|---|---|
question_id |
string | "dq_20260318" |
yes |
index |
number | 1 |
yes |
Source: DailyQuestion.jsx
Partner
Partner Connected
Fires when a partner is connected for the first time (partner_connected transitions false → true).
| Property | Type | Example | Required |
|---|---|---|---|
| (none) | — | — | — |
Source: UserContext.jsx
Payments
All payment events run on the React side via the Natively RevenueCat bridge and a direct RC REST read for entitlement state — Bubble is uninvolved.
Removed:
Paywall Viewed/Paywall Dismissed— these only fired from RC's hosted paywall (EntitlementContext.showPaywall()), which has been removed. The app uses a single custom paywall (PlansScreen), tracked viaPlans Screen Shown(see below).
Purchase Started
Fires when buy(packageId) is called.
package_id(string)source(string)
Purchase Completed
Fires when the purchasePackage callback returns status: 'SUCCESS'.
package_id(string)transaction_id(string|null)
Purchase Failed
Fires when the purchasePackage callback returns a non-SUCCESS status.
package_id(string)reason(string) — error message from RCuser_cancelled(bool) — true if reason looks like a user cancel
Purchase Restore Tapped
Fires when the user taps Restore Purchases.
source(string)
Purchase Restore Completed
Fires after the post-restore entitlement refresh.
became_subscribed(bool) — true if restore flipped state from non-paying → paying
Plans screen events
Custom in-app paywall at PlansScreen.jsx. Pushed from Profile → Subscription.
Screen Viewed { screen: 'plans' }— on mount.Element Clicked { screen: 'plans', element_type: 'card', element: 'plan_<label>' }— when a plan row is selected (<label>= key fromAPP_CONFIG.purchases.packages, e.g.monthly).Element Clicked { screen: 'plans', element_type: 'button', element: 'subscribe' }— Subscribe CTA tapped (followed by the standardPurchase Started/Purchase Completed/Purchase Failedchain).Element Clicked { screen: 'plans', element_type: 'link', element: 'restore' }— Restore link tapped (followed byPurchase Restore Tapped/Purchase Restore Completed).Element Clicked { screen: 'profile', element_type: 'button', element: 'subscription' }— Subscription row in Profile tapped (entry into the plans screen).
Purchase Started/Purchase Completed/Purchase Failed/Purchase Restore Tapped/Purchase Restore Completed already fire from EntitlementContext.buy/restore; the plans screen passes source: 'plans_screen' so the funnel can be segmented by entry point.
Entitlement Changed
Fires whenever isSubscribed flips on EntitlementContext (renewal, expiry, cross-device sync).
is_subscribed(bool)
Also: is_subscribed is set as a Mixpanel People property every time
EntitlementContext resolves, so the latest known state lives on the user
profile for funnel segmentation.
Cohort coin-gate events
Fired by the shared useCoinGate hook (src/hooks/useCoinGate.js), which
funnels every coin-spending action (chapter start, simulator session, activity
creation, etc.) through one cohort-aware path. Replaces the legacy local
if (coins < N) checks scattered across feature files.
The payments_group property on these events is the effective variant
('A' | 'B' | 'C'): the Bubble payment_variant source-of-truth value when
set, else the live Statsig assignment (user.paymentVariant || getVariant('payments', 'variant_display', 'A')).
When neither is available (Statsig not ready, user excluded from targeting, or
field unset) it defaults to 'A' (most permissive cohort — silent refills, no
paywall). A single super-property carries the variant on every event:
payment_variant (the effective value, including admin overrides — set by
<PaymentVariantSeeder>). Use it for all cohort funnels. (The raw Statsig
bucket is intentionally not mirrored to Mixpanel — Statsig keeps the exposure
for experiment analysis.)
Coin Gate Triggered— every timegate.request()orgate.requestAccess()fires. Properties:source,cost,coins_before,payments_group,is_subscribed. Use this as the top of any coin-gate funnel.Coin Gate Blocked— fires when the user is short on coins (regardless of cohort outcome). Properties:source,cost,payments_group.Coin Auto Refill Granted— Group A path (also fires for unbucketed users who fall through to A). Properties:source,amount(30),payments_group: 'A'.Coin Grant Dialog Shown— the visible "it's your lucky day / +N coins" grant notice (CoinGrantDialog) that accompanies a Group-A auto-refill. Properties:amount(30).Insufficient Coins Paywall Shown— Group B / C path. Properties:source,payments_group. Followed by the standardPurchase Startedchain when the user taps Go Premium.- The paywall is a bottom-sheet (
InsufficientCoinsPaywallDialog) with three CTAs, each firingElement Clicked { screen: 'insufficient_coins_paywall', element_type, element }:element: 'go_premium'(→ PlansScreen),'daily_questions'(→ Fun Zone tab),'give_feedback'(→ profile feedback). Insufficient Coins Paywall Dismissed— fires when the sheet is closed without picking a CTA (backdrop tap), closing theInsufficient Coins Paywall Shown→ CTA / dismiss funnel. Properties:source. A CTA-driven close does not fire this (theElement Clickedalready records that outcome).
- The paywall is a bottom-sheet (
Coin Balance Panel Shown— fires when the non-premium user taps the coin counter and the "Your coins" sheet (CoinBalancePanel) opens. Properties:coins. The counter tap itself firesElement Clicked { screen, element_type: 'button', element: 'coin_counter' }(screen ∈ the surface:home/journey/daily_question); the sheet's CTA firesElement Clicked { screen: 'coin_balance_panel', element: 'go_premium' }.Payment Variant Stamped— fires once when<PaymentVariantSeeder>seeds the Bubblepayment_variantfield on a fresh server-empty report. Properties:variant('A' | 'B' | 'C'),source('statsig'for a real assignment, or'unbucketed_default'when the user is unbucketed and gets control'A'). Does not fire for users who already have a value (Bubble source of truth / admin override) or when Statsig is unavailable (outage → retries next session).
Premium-user surfaces
Premium Badge Tapped— fires when a subscriber taps the top-bar premium badge. Properties:surface('home' | 'daily_question').Premium Status Viewed— fires when thePremiumStatusDialogopens. Properties:subscription_type,expires_at.Manage Subscription Tapped— fires when the user taps "Cancel subscription" in the status dialog (deep-links to the OS subscription manager). Properties:platform('ios' | 'android' | 'web').
Subscription lifecycle screens
Plans Screen Shown— fires every time the redesignedPlansScreenmounts or is surfaced as an overlay. Properties:source('profile' | 'post_onboarding_b' | 'insufficient_coins' | 'coin_panel' | 'expired_modal' | 'badge_tap'). Coexists with the olderScreen Viewed { screen: 'plans' }event for back-compat; new funnels should usePlans Screen Shownto segment by entry point.Plans Screen Dismissed— fires when the user leavesPlansScreenwithout converting (closes thePlans Screen Shown→ subscribe / restore / dismiss funnel). Properties:source(same enum asPlans Screen Shown),reason('back'= back chevron on the normal plans view;'already_subscribed'= back chevron or Continue on the "You're Premium" state). Does not fire on the mandatory Group-B gate (back is hidden — the only exits are subscribe or restore).Subscription Success Shown— fires when the post-purchase celebration screen mounts. Properties:package_id,subscription_type. The Continue button that exits the screen firesElement Clicked { screen: 'subscription_success', element_type: 'button', element: 'continue' }.Subscription Expired Modal Shown— fires when the expired-notice dialog appears (RC entitlement flipped subscribed → not since last session). Properties:last_type('yearly' | 'quarterly' | 'monthly' | null).Subscription Expired Action— fires when the user resolves the expired notice. Properties:last_type,action('renew' | 'continue'),bonus_granted(bool — true if Bubble was expected to grant 10 welcome-back coins).
People + super properties
subscription_type is pushed as a Mixpanel People property every time
UserContext.updateUser receives it from Bubble.
The cohort variant is carried by a single super-property:
payment_variant: "A" | "B" | "C"— the effective variant (Bubble source-of-truth field, including admin overrides), set by<PaymentVariantSeeder>(src/hooks/usePaymentVariant.js).
Use payment_variant for all cohort-segmented funnel analysis — it reflects the
experience the user actually got. The raw Statsig assignment is not mirrored
to Mixpanel as experiment_payments (the payments experiment is suppressed in
attachMixpanelMirror, src/utils/experiments.js, to keep one cohort property);
use Statsig's own exposure data for randomized (intent-to-treat) experiment
analysis.
System
Report Submitted
Fires when a bug report is sent (via clipboard copy or Slack relay).
| Property | Type | Example | Required |
|---|---|---|---|
platform |
string | "ios" |
yes |
has_logs |
boolean | true |
yes |
log_count |
number | 42 |
yes |
delivery |
string | "slack" / "clipboard" |
yes |
Source: logCapture.js
Profile Photo Updated
Fires when user crops and saves a new profile photo.
| Property | Type | Example | Required |
|---|---|---|---|
screen |
string | "profile" |
yes |
Source: MainTabs.jsx
FTUE Walkthrough Completed
Fires when the user finishes all steps of the first-time user experience walkthrough.
| Property | Type | Example | Required |
|---|---|---|---|
steps_seen |
number | 5 |
yes |
is_replay |
boolean | true |
yes |
Source: DrLeoWalkthrough.jsx
TTS Error Occurred
Fires when TTS streaming fails (network error, proxy down, etc.). Centralized in useTTS hook — all TTS consumers get tracking automatically.
| Property | Type | Example | Required |
|---|---|---|---|
backend |
string | "mse" / "pcm" |
yes |
error_message |
string | "Failed to fetch" |
yes |
screen |
string | "insight_playback" / "activity_view" / "simulator_results" |
yes (via caller context) |
Source: useTTS.js
Push Notification Prompt
Push Prompt Shown
Fires when the pre-permission dialog appears.
| Property | Type | Example | Required |
|---|---|---|---|
trigger |
string | "post_signup" / "session_start" / "reinstall" / "act_reminder" |
yes |
mode |
string | "first_time" / "settings_redirect" |
no (only present for act_reminder trigger) |
Source: MainTabs.jsx, useNotificationGate.jsx
Push Prompt Accepted
Fires when user taps "Enable notifications" in the pre-permission dialog. Tracked BEFORE the native OS prompt.
| Property | Type | Example | Required |
|---|---|---|---|
trigger |
string | "post_signup" / "session_start" / "reinstall" / "act_reminder" |
yes |
mode |
string | "first_time" / "settings_redirect" |
no (only present for act_reminder trigger) |
Source: MainTabs.jsx, useNotificationGate.jsx
Push Prompt Dismissed
Fires when user taps "Not now" in the pre-permission dialog.
| Property | Type | Example | Required |
|---|---|---|---|
trigger |
string | "post_signup" / "session_start" / "reinstall" / "act_reminder" |
yes |
Source: MainTabs.jsx, useNotificationGate.jsx
Push Settings Redirect Shown
Fires when the "previously denied" dialog appears in Profile.
Source: MainTabs.jsx
Push Settings Redirect Accepted
Fires when user taps "Open Settings" from the profile notification dialog.
Source: MainTabs.jsx
5. Element Clicked Reference
All Element Clicked events. Properties always include screen, element_type, and element. Extra context properties listed in the last column.
Welcome & Auth
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
welcome |
button |
lets_go |
— | WelcomeScreen |
welcome |
button |
sign_in |
— | WelcomeScreen |
signin |
button |
back |
— | SignInScreen |
signin |
button |
go_to_signup |
— | SignInScreen |
signin |
link |
continue_with_email |
— | SignInScreen |
signin |
link |
forgot_password |
— | SignInScreen |
signup |
button |
back |
— | SignUpScreen |
signup |
button |
go_to_signin |
— | SignUpScreen |
signup |
link |
continue_with_email |
— | SignUpScreen |
signup |
link |
go_to_signin |
— | SignUpScreen |
FTUE Walkthrough
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
ftue_walkthrough |
button |
next |
step |
DrLeoWalkthrough |
ftue_walkthrough |
button |
skip |
step |
DrLeoWalkthrough |
ftue_walkthrough |
button |
back |
step |
DrLeoWalkthrough |
Main Navigation
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
main |
tab |
journey / simulator / fun / profile |
— | MainTabs |
| {activeTab} | button |
back |
— | MainTabs (stack) |
Onboarding
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
onboarding |
button |
back |
step |
OnboardingFlow |
onboarding |
button |
error_go_back |
— | OnboardingFlow |
onboarding |
button |
retry_fetch |
— | OnboardingFlow |
onboarding_open |
button |
mic |
— | OpenQuestion |
onboarding_open |
button |
type |
— | OpenQuestion |
onboarding_open |
button |
skip |
— | OpenQuestion |
onboarding_open |
button |
share_anyway |
— | OpenQuestion |
Journey
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
journey |
button |
chapter_menu |
— | JourneyPath |
journey |
button |
chapter_select |
chapter |
JourneyPath |
journey |
button |
node |
node_id, node_type |
JourneyPath |
journey |
button |
start_lesson |
node_id, node_type |
JourneyPath |
journey |
button |
review_step |
node_id, node_type |
JourneyPath |
journey |
button |
repractice_step |
node_id, node_type |
JourneyPath |
journey |
button |
replay_insight |
node_id, node_type |
JourneyPath |
journey |
button |
milestone_review |
node_id |
JourneyPath |
journey |
button |
select_journey |
— | HomeSection |
journey |
button |
send / chat / notifications / previous_journey / next_journey / change_topic / conversation_coach / practical_actions / ask_question |
— | HomeSection |
Learn Video
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
learn_video |
button |
play |
— | LearnStep |
learn_video |
button |
pause |
— | LearnStep |
learn_video |
button |
speed |
speed |
LearnStep |
learn_video |
button |
rewind |
— | LearnStep |
learn_video |
button |
mute / unmute |
— | LearnStep |
learn_video |
button |
replay |
— | LearnStep |
learn_video |
button |
skip_to_questions / continue_to_questions |
— | LearnStep |
Practice
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
practice |
button |
start_practice |
chapter |
PracticeStep |
Act
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
act |
button |
accept |
chapter |
ActStep |
act |
button |
decline |
chapter |
ActStep |
act |
button |
retry |
chapter |
ActStep |
act_reminder |
button |
reminder_tomorrow |
chapter |
ReminderSheet |
act_reminder |
button |
reminder_3_days |
chapter |
ReminderSheet |
act_reminder |
button |
reminder_7_days |
chapter |
ReminderSheet |
act_reminder |
button |
skip_reminder |
chapter |
ReminderSheet |
Chapter Celebration
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
chapter_celebration |
button |
continue |
chapter |
ChapterCelebration |
Step Review
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
step_review |
button |
rewatch_video |
— | StepReview |
step_review |
button |
close_video |
— | StepReview |
step_review |
button |
play / pause / replay |
— | StepReview |
step_review |
button |
rewind |
— | StepReview |
step_review |
button |
speed |
speed |
StepReview |
step_review |
button |
mute / unmute |
— | StepReview |
Insight Questions
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
insight_questions |
button |
back |
— | InsightQuestions |
insight_questions |
button |
close |
— | InsightQuestions |
insight_questions |
button |
mic |
— | InsightQuestions |
insight_questions |
button |
type |
— | InsightQuestions |
insight_questions |
button |
yes / no |
— | InsightQuestions |
insight_questions |
button |
nudge_continue |
— | InsightQuestions |
insight_questions |
button |
nudge_custom |
— | InsightQuestions |
Insight Other Dialog
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
insight_other |
button |
mic_start / mic_stop |
— | InsightOtherDialog |
insight_other |
button |
share |
— | InsightOtherDialog |
insight_other |
button |
dialog_closed |
had_text |
InsightOtherDialog |
insight_other |
input |
typing_started |
— | InsightOtherDialog |
insight_other |
button |
open_settings |
— | InsightOtherDialog |
Insight Playback
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
insight_playback |
button |
play / pause |
— | InsightPlayback |
insight_playback |
button |
speed |
speed |
InsightPlayback |
insight_playback |
button |
replay |
— | InsightPlayback |
insight_playback |
button |
mute / unmute |
— | InsightPlayback |
insight_playback |
button |
continue |
— | InsightPlayback |
Fun Zone
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
fun |
banner |
daily_question |
— | FunZoneSection |
fun |
card |
activity_type |
type_id |
FunZoneSection |
fun |
card |
history_item |
section: 'activity', item_id |
FunZoneSection |
Activity View
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
activity_view |
button |
close |
— | ActivityView |
activity_view |
button |
listen |
— | ActivityView |
activity_view |
button |
play / pause |
— | ActivityView |
activity_view |
button |
speed |
speed |
ActivityView |
activity_view |
button |
replay |
— | ActivityView |
activity_view |
button |
retry |
— | ActivityView |
activity_view |
button |
continue |
— | ActivityView |
Type Detail
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
type_detail |
button |
create_new |
section, type_id |
TypeDetailScreen |
type_detail |
card |
history_item |
section, item_id |
TypeDetailScreen |
Simulator Results
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
simulator_results |
button |
play / pause |
— | SimulatorResults |
simulator_results |
button |
speed |
speed |
SimulatorResults |
simulator_results |
button |
replay |
— | SimulatorResults |
simulator_results |
button |
retry |
— | SimulatorResults |
simulator_results |
button |
done |
— | SimulatorResults |
simulator_results |
link |
read_more / read_less |
— | SimulatorResults |
Profile & Settings
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
profile |
button |
upload_photo |
— | MainTabs |
profile |
button |
save_name |
— | MainTabs |
profile |
button |
share_feedback |
— | MainTabs |
profile |
button |
how_bonds_works |
— | MainTabs |
profile |
button |
account_info |
— | MainTabs |
profile |
toggle |
dark_mode |
preference |
ThemeContext |
Account Info
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
account_info |
button |
logout |
— | MainTabs |
account_info |
button |
delete_account |
— | MainTabs |
Name Prompt
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
name_prompt |
button |
submit_name |
— | MainTabs |
name_prompt |
button |
prefer_not_to_say |
— | MainTabs |
Partner Name Prompt
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
partner_name_prompt |
button |
submit_partner_name |
— | MainTabs |
partner_name_prompt |
button |
skip_partner_name |
— | MainTabs |
Profile Save (Account Info)
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
profile |
button |
save_partner_name |
— | MainTabs (AccountInfoPage) |
Daily Question
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
daily_question |
button |
close |
— | DailyQuestion |
Share Viewer
| Screen | Type | Element | Extra | Source |
|---|---|---|---|---|
share_viewer |
button |
get_bonds |
— | ShareCTA |
Push Notifications
| Event | Properties | When | Source |
|---|---|---|---|
Push Permission Requested |
— | Before native OS prompt is shown | notifications.js |
Push Permission Responded |
granted (bool) |
After user responds to OS prompt or settings redirect | notifications.js |
Push Player ID Obtained |
— | First time player ID is fetched in a session | notifications.js |
6. Funnels
Onboarding → Insight → Signup Funnel
1. Screen Viewed { screen: 'welcome' }
2. Element Clicked { element: 'lets_go' }
3. Onboarding Intro Viewed ← one-time only
4. Onboarding Intro Completed ← if video finished
OR Onboarding Intro Skipped ← if CTA tapped before video ends
5. Screen Viewed { screen: 'onboarding' }
6. Onboarding Step Completed ← repeats per step
7. Onboarding Completed
8. Onboarding Insight Started
9. Onboarding Insight Generated
10. Onboarding Insight Playback Completed / Skipped
11. Onboarding Insight CTA Tapped
12. Screen Viewed { screen: 'signup' }
13. Sign Up Started { method }
14. Sign Up Completed
15. Onboarding Data Connected
16. Screen Viewed { screen: 'ftue_walkthrough' }
17. FTUE Walkthrough Completed
Drop-off: Onboarding Abandoned (back on step 0)
Onboarding Intro Backed Out (back from intro to welcome)
Resume: Onboarding Resumed { phase, step, source }
Replay: Onboarding Intro Replayed (user replays intro video)
Timeout: Onboarding Insight Timed Out
Back: Onboarding Step Back { from_step, to_step }
Mic err: Onboarding Mic Permission Denied / Onboarding Mic Unavailable
No API: Onboarding Speech API Unavailable
Skip: Element Clicked { screen: 'ftue_walkthrough', element: 'skip' }
Authentication Funnel
1. Element Clicked { element: 'sign_in' }
2. Screen Viewed { screen: 'signin' }
3. Sign In Started { method }
4. Sign In Completed { method }
Journey Chapter Funnel
Each chapter follows this step sequence:
0. Chapter Started { chapter, chapter_title, coin_cost: 6 } ← fires on coin-gate confirm
1. Journey Step Started { step_type: 'learn' }
2. Journey Video Watched
3. Journey Step Completed { step_type: 'learn' }
4. Journey Step Started { step_type: 'practice' }
5. Journey Practice Started
6. Simulator Session Started { mode: 'journey' }
7. Simulator Session Completed
8. Journey Practice Completed
9. Journey Step Completed { step_type: 'practice' }
10. Journey Step Started { step_type: 'insight' }
11. Insight Flow Started { type: 'journey' }
12. Insight Question Answered ← repeats
13. Insight Playback Completed / Skipped
14. Journey Step Completed { step_type: 'insight' }
15. Journey Step Started { step_type: 'act' }
16. Element Clicked { element: 'accept' or 'decline' }
17. Journey Step Completed { step_type: 'act' }
18. Chapter Completed
Drop-off at any stage: Journey Step Abandoned { step_type }
Simulator Funnel (Freeform)
1. Screen Viewed { screen: 'simulator' }
2. Simulator Session Started { mode: 'freeform' }
3. Coins Deducted { amount, context: 'simulator' } ← fires at stage 2 start
4. Simulator Session Completed { score, skill_level }
Alt exits:
- Simulator Session Closed (user quits mid-session)
- Simulator Session Error { code, message }
Loop: Simulator Session Retried → back to step 3
Insight Funnel (Standalone)
1. Insight Flow Started { type: 'learn' or 'activity' }
2. Insight Question Answered ← repeats
3. Insight Generated
4. Insight Playback Completed / Skipped
Timeout: Insight Generation Timed Out
Drop-off: Insight Flow Closed
Daily Question Funnel
1. Daily Question Viewed { question_id, category }
2. Daily Question Voted { question_id, index }
Partner Connection
1. Partner Connected
Note: Partner invitation is handled in Bubble, not tracked with Element Clicked in the React UI.
Push Notification Permission Funnel
Triggers: post_signup (MainTabs startup), session_start (7-day re-prompt), reinstall (fresh install), act_reminder (journey act reminder gate)
1. Push Prompt Shown
2. Push Prompt Accepted (vs Push Prompt Dismissed = drop-off)
3. Push Permission Requested
4. Push Permission Responded { granted: true }
5. Push Player ID Obtained
7. Recommended Mixpanel Reports
| Report | Event(s) | Insight |
|---|---|---|
| DAU / WAU / MAU | App Opened |
Daily/weekly/monthly active users |
| Session Duration | App Opened → App Backgrounded |
Average time in app per session |
| Retention | Cohort by signup_date, retained via App Opened |
Day 1/7/30 retention |
| Onboarding Conversion | Onboarding funnel above | Step-by-step drop-off rates |
| Journey Progress | Chapter Started → Chapter Completed by chapter |
Chapter start/complete rates |
| Simulator Engagement | Simulator Session Started → Completed |
Completion rate, avg score by mode |
| Feature Adoption | % of authenticated users who triggered each feature start | Which features resonate |
| Coin Economy | Coins Deducted vs coins earned (Journey, Onboarding) |
Earn/spend balance |
| Daily Question Engagement | Daily Question Viewed → Voted |
Participation rate |
| Partner Adoption | Partner Connected / total authenticated users |
Invite conversion |
8. Developer Checklist
When adding or changing analytics tracking:
- Event name is Title Case, Object-Action, past tense
- Property names are snake_case
- Import
trackfromsrc/utils/analytics.js(never importmixpanel-browserdirectly) - UI interactions use
Element Clickedwithscreen,element_type,element - Business/funnel actions get their own domain event name
- Update this document — add the event to the appropriate section
- Update funnels section if adding a new step to an existing flow
- If adding a new people property, add it to the People Properties table
Code patterns
Track a UI interaction:
import { track } from '../utils/analytics';
track('Element Clicked', {
screen: 'screen_name',
element_type: 'button', // button | tab | banner | card | toggle | link
element: 'element_name',
// ...extra context
});
Track a domain event:
track('Feature Action', { property: value });
// e.g. track('Partner Connected');
Track a screen view:
import { screen } from '../utils/analytics';
screen('screen_name');