What are we tracking?
We use PostHog for analytics in both the web and desktop applications. The web application also uses Outlit for pageview tracking (configured via the VITE_OUTLIT_PUBLIC_KEY environment variable). You can opt-out from the settings menu in the desktop app.
User Journey Events
1. Visiting the Website
Events tracked on the web app (apps/web):
| Event | Description | Properties |
|---|---|---|
download_clicked | User clicks a download button | platform, spec, source, timestamp |
reminder_requested | User requests a mobile reminder | platform, timestamp, email |
os_waitlist_joined | User joins waitlist for Linux/Windows | platform, timestamp, email |
The web app also has PostHog autocapture and pageview tracking enabled.
2. Getting Started
Events tracked when users first launch the desktop app:
| Event | Description | Properties |
|---|---|---|
show_main_window | Main window is shown (fires on every app launch) | - |
account_skipped | User proceeds without account (local mode) | - |
user_signed_in | User signs in (triggers $identify to link anonymous ID) | - |
ai_provider_configured | User configures an AI provider | provider |
data_imported | User imports data from another app | source |
trial_started | User starts a trial | plan |
3. Before Meetings
Events tracked when preparing for meetings:
| Event | Description | Properties |
|---|---|---|
note_created | User creates a new note | has_event_id (whether linked to calendar) |
file_uploaded | User uploads a file | file_type (audio/transcript), token_count |
4. During Meetings
Events tracked during active sessions:
| Event | Description | Properties |
|---|---|---|
session_started | Listening session starts | has_calendar_event |
tab_opened | User opens a tab | view (tab type) |
search_performed | User performs a search | - |
5. After Meetings
Events tracked when working with completed sessions:
| Event | Description | Properties |
|---|---|---|
note_edited | User edits a note | has_content |
note_enhanced | AI enhancement is triggered | is_auto, template_id |
message_sent | User sends a chat message | - |
session_exported | User exports a session | format (pdf/vtt), view_type, has_transcript, has_enhanced, has_memo, word_count |
session_deleted | User deletes a session/note | includes_recording |
recording_deleted | User deletes a recording | - |
6. Settings & Account Management
Events tracked when managing settings and account:
| Event | Description | Properties |
|---|---|---|
settings_changed | User changes settings | autostart, notification_detect, save_recordings, telemetry_consent |
upgrade_clicked | User clicks upgrade button | plan |
user_signed_out | User signs out | - |
7. Notification Interactions
Events tracked when users interact with system notifications:
| Event | Description | Properties |
|---|---|---|
collapsed_confirm | User clicks collapsed notification to open app | - |
expanded_accept | User accepts expanded notification | - |
dismiss | User dismisses notification | - |
collapsed_timeout | Collapsed notification times out | - |
User Properties
Properties set to track user context and configuration:
| Property | Description | Type |
|---|---|---|
is_local_mode | Whether user is in local-only mode | set |
is_signed_up | Whether user has signed up | set |
platform | Operating system (macos, linux, windows) | set |
os_version | Operating system version | set |
app_version | Application version | set |
account_created_date | When account was created | set_once |
telemetry_opt_out | Whether user opted out of telemetry | set |
has_configured_ai | Whether user has configured an AI provider | set |
plan | Current subscription plan | set |
trial_end_date | When trial ends | set |
Analytics Commands
Available methods for tracking events and properties:
event(payload)- Track a custom event with optional propertiessetProperties(payload)- Set user properties (supportssetandset_oncetypes)identify(userId, payload)- Link anonymous device ID to authenticated user ID using PostHog's$identifyeventsetDisabled(disabled)- Enable or disable analytics trackingisDisabled()- Check if analytics is currently disabled
User Identification
When a user signs in, the identify command is called to link their anonymous device ID (machine fingerprint) to their authenticated user ID. This uses PostHog's $identify event with the $anon_distinct_id property, enabling:
- Attribution of pre-login activity to the authenticated user
- Unified user profiles across anonymous and authenticated sessions
- Accurate conversion funnel analysis from first visit to active usage
The identification flow works as follows: before sign-in, events are tracked using the device fingerprint as the distinct ID. Upon successful authentication, identify(userId, payload) is called, which sends a $identify event to PostHog with both the new user ID and the anonymous device ID, merging the two identities.
Conversion Funnel Tracking
To track the user journey from website visit to active usage, use PostHog's funnel analysis with these key events:
download_clicked(web) - User downloads the appshow_main_window(desktop) - User launches the appuser_signed_in(desktop) - User signs in (triggers identity merge)note_created(desktop) - User creates their first notenote_enhanced(desktop) - User uses AI enhancement
PostHog's $identify event links anonymous device IDs to authenticated user IDs, enabling accurate conversion analysis across the entire user journey.
Feature Flags
Hyprnote uses PostHog feature flags to control feature rollouts. The system is split into two layers:
crates/flag- A standalone Rust client for evaluating PostHog feature flags via the/flagsAPI. It returns typedFlagValueresults with support for boolean flags, multivariate variants, and JSON payloads.plugins/flag- A Tauri plugin that wraps the flag client with caching and provides aFlagPluginExttrait. Each feature is defined in theFeatureenum with aFlagStrategy:Debug- enabled only in debug buildsPosthog(key)- evaluated via PostHog API with response cachingHardcoded(bool)- always returns the given value
To check a flag from Rust:
use hypr_plugin_flag::FlagPluginExt;
let enabled = app.flag().is_enabled(Feature::Chat).await;
Crate Architecture
The analytics stack is split across several crates:
crates/posthog- Low-level PostHog HTTP client for sending events, setting properties, and identifying users.crates/analytics- Higher-level client that composes PostHog and Outlit clients viaAnalyticsClientBuilder. Used by both the desktop app and the AI server.crates/flag- PostHog feature flag evaluation client (see above).
Server-Side Analytics
The apps/ai server also reports analytics for proxy requests:
- LLM proxy (
crates/llm-proxy) - Reports$ai_generationevents with model, token counts, latency, and cost. - STT proxy (
crates/transcribe-proxy) - Reports$stt_requestevents with provider and duration.
Both proxies attribute events to users via device fingerprint and optional user_id.
How to make changes?
See .cursor/commands/add-analytics.md for detailed instructions.