Analytics System Documentation
This guide explains the analytics data collection system implemented for Meridian Mobile. For a platform-level view (philosophy, event lifecycle from tracking to storage, and backend implementation), see Platform Analytics (Event Pipeline). The system provides a lightweight, future-proof way to collect raw event data reliably for mobile launch and beyond.Overview
The analytics system is a “dumb pipe” that collects raw event data and stores it in MongoDB. It’s designed to be:- Reliable: Local queue survives app restarts and network failures
- Future-proof: Stable event envelope schema that works with any analytics tool (GA4, PostHog, BigQuery, etc.)
- Privacy-focused: No PII stored, client and server-side scrubbing
- Idempotent: Safe retries with unique event IDs
- Lightweight: Minimal overhead, batched ingestion
Architecture
Event Envelope Schema
Every event stored in MongoDB follows this structure:Mobile SDK Usage
Initialization
The analytics SDK is automatically initialized inAppContent.tsx on app start. No manual initialization needed.
Tracking Events
Basic Event Tracking
Screen View Tracking
Screen views are automatically tracked via navigation state changes. You can also manually track:User Identification
User identification is handled automatically inAuthContext.tsx:
Manual Usage (If Needed)
If you need to track events manually in a new screen:Event Taxonomy
Core Events (Auto-tracked)
session_start- App opened/foregrounded (auto-tracked on init)screen_view- Screen navigation (auto-tracked via navigation listener)login_completed- User successfully logs in (auto-tracked in AuthContext)logout- User logs out (auto-tracked in AuthContext)signup_started- User begins signup (tracked in RegisterScreen)signup_completed- User completes signup (tracked in AuthContext)
Meridian-Specific Events (Manual Tracking)
These events should be tracked manually in relevant screens:event_viewed- User views event detailsevent_rsvp- User RSVPs to eventroom_viewed- User views room detailsroom_saved- User saves a roomsearch_performed- User performs searchapi_error- API error occurred (redacted, no sensitive data)onboarding_started- User starts onboarding flowonboarding_completed- User completes onboarding
Example: Adding Event Tracking to a Screen
Backend API
Endpoint
POST/v1/events
Request Body
Response
Limits
- Max payload size: 1MB
- Max events per request: 50
- Max event size: 10KB
- Max properties size: 5KB per event
Validation
The endpoint validates:- Required fields:
event,ts,event_id,anonymous_id,session_id,platform,app_version,env - Platform enum:
ios,android,web - Environment enum:
prod,staging,dev - Event size limits
- PII scrubbing (removes
email,name,phone,password, etc.)
Database Setup
Create Indexes
Run the index creation script:- Unique index on
event_id(for idempotency) - Descending index on
ts(for time-based queries) - Composite index on
user_id + ts(for user activity queries) - Composite index on
anonymous_id + ts(for anonymous user queries)
Collection Name
Events are stored in theanalytics_events collection.
Querying Events
Privacy & PII Handling
Client-Side Scrubbing
The SDK automatically removes PII keys from properties:email,name,phone,password,ssn,credit_card,address
Server-Side Scrubbing
The backend also scrubs PII and enforces size limits.IP Address Handling
IP addresses are hashed using SHA-256 before storage (ip_hash field).
User Agent Handling
Only a basic summary is stored (user_agent_summary: ios, android, chrome, etc.), not the full user agent string.
Local Queue & Reliability
Queue Management
- Events are stored in AsyncStorage (
@meridian/analytics/queue) - Max queue size: 500 events (oldest dropped if exceeded)
- Queue persists across app restarts
Batching
- Max batch size: 20 events per request
- Auto-flush interval: 45 seconds
- Auto-flush triggers:
- App goes to background
- App comes to foreground
- Queue reaches batch size
Retry Logic
- Exponential backoff: 1s, 2s, 4s, 8s, max 30s
- Max retries: 3 per batch
- Retries on network errors and server errors (5xx)
- No retry on client errors (4xx)
Idempotency
- Each event has a unique
event_id(UUID) - Duplicate events are detected and ignored (not inserted)
- Safe to retry without creating duplicates
Session Management
Session ID
- Generated on app start
- Regenerated when app comes to foreground after 30+ minutes in background
- Stored in AsyncStorage (
@meridian/analytics/session_id)
Anonymous ID
- Generated on first app launch
- Persists across app restarts
- Never changes (unless app is uninstalled)
- Stored in AsyncStorage (
@meridian/analytics/anonymous_id)
User ID
- Set when user logs in (
analytics.identify(userId)) - Cleared when user logs out (
analytics.reset()) - Stored in AsyncStorage (
@meridian/analytics/user_id)
Debugging
Development Mode
In development (__DEV__), the SDK logs:
- Event queue counts
- Batch send attempts
- Retry attempts
- Payload details
Inspecting Queue
Backend Logging
The backend logs:- Received event counts
- Inserted/duplicate/dropped counts
- Validation errors
- Insert errors
Future Integration
The event envelope is designed to be compatible with any analytics tool:Google Analytics 4 (GA4)
PostHog
BigQuery / Data Warehouse
Testing
Local Testing
- Mobile: Enable debug mode, verify events queued in AsyncStorage
- Backend: Inspect
analytics_eventscollection in MongoDB - Integration: Send test events, verify insertion, check duplicates handled
Staging/Production
- Verification: Query sample documents, verify envelope shape
- Counts: Check event counts match expected usage patterns
- Failure Modes: Test offline queue, retries, duplicate handling
Testing Offline Queue
Troubleshooting
Events Not Appearing
- Check if analytics is initialized (should happen automatically)
- Check queue size (may be full, oldest events dropped)
- Check network connectivity
- Check backend logs for validation errors
- Verify MongoDB indexes are created
Duplicate Events
- Duplicates are handled gracefully (not inserted, counted in response)
- If seeing duplicates, check
event_idgeneration (should be UUID)
Queue Not Flushing
- Check if app state listener is working (background/foreground)
- Check if flush timer is running (45s interval)
- Manually flush:
await analytics.flush()
PII in Events
- Check client-side scrubbing (removes common PII keys)
- Check server-side scrubbing (also removes PII)
- Review event properties before tracking
Best Practices
- Track meaningful events: Don’t track every click, focus on user actions
- Use consistent event names: Follow the taxonomy (snake_case)
- Keep properties small: Max 5KB per event
- No PII in properties: Never include email, name, phone, etc.
- Use context overrides sparingly: Default context is usually sufficient
- Test in dev first: Verify events before deploying
Migration Notes
The new analytics system is independent of the old manual tracking:- Old routes (
/routes/analytics.js) remain functional - Old collections (
Visit,RepeatedVisit,Search) remain unchanged - Both systems can coexist during transition
- No data migration needed
Support
For questions or issues:- Check this documentation
- Review code comments in
analytics.tsandanalyticsRoutes.js - Check backend logs for validation errors
- Inspect MongoDB collection for data issues