How to Set Up In-App Event Schemas (Best Practices)
Design robust event schemas for mobile attribution. Learn naming conventions, parameter standards, and implementation patterns that scale.

How to Set Up In-App Event Schemas (Best Practices)
Poor event schemas create data debt that compounds over time. Once your app is live with thousands of users, fixing event naming or parameter structures becomes expensive and error-prone.
Getting the schema right before implementation saves months of cleanup work.
Here's how to design event tracking that scales from initial launch to millions of users.
Why Event Schemas Matter
An event schema defines:
- Which user actions you track
- How you name those events
- What data you capture with each event
- Data types and validation rules
Good schemas enable:
- Consistent cross-platform analysis
- Accurate attribution and optimization
- Clean data warehouse integration
- Easy debugging and troubleshooting
Bad schemas create:
- Data fragmentation across platforms
- Inconsistent reporting
- Expensive migration and cleanup projects
- Analysis paralysis when data doesn't match
Core Principles
1. Consistency Above All
Use identical naming across:
- iOS and Android apps
- MMP (Adjust, AppsFlyer, Branch)
- Product analytics (Mixpanel, Amplitude)
- Ad networks (Facebook, Google, TikTok)
- Data warehouse
Bad:
- iOS: "userRegistered"
- Android: "user_registration"
- MMP: "signup_complete"
- Facebook: "CompleteRegistration"
Good:
- All platforms: "registration_completed"
2. Be Specific, Not Generic
Event names should clearly describe what happened without requiring additional context.
Bad:
- "button_clicked"
- "action_completed"
- "event_1"
Good:
- "checkout_initiated"
- "subscription_purchased"
- "onboarding_completed"
3. Track Value, Not Activity
Focus on events that drive business decisions, not every user interaction.
Track in MMP:
- Purchase completed
- Subscription started
- Trial activated
- Registration completed
Don't track in MMP:
- Screen viewed
- Button tapped
- App backgrounded
- Settings changed
Save low-value events for product analytics tools like Mixpanel or Amplitude. MMPs charge per event—tracking everything gets expensive fast.
4. Design for the Future
Your schema should accommodate:
- New product features
- Additional platforms (web, tablet, TV)
- International expansion
- A/B testing variations
Build extensibility into naming conventions from the start.
Naming Conventions
Event Names
Use a consistent pattern. The most scalable approach is:
Format: object_action
Examples:
purchase_completedsubscription_startedtrial_activatedcart_abandonedcontent_shared
Alternative format: action_object
Examples:
completed_purchasestarted_subscriptionactivated_trial
Choose one and stick with it. Mixing formats creates confusion.
Naming Rules
Use snake_case:
purchase_completed✓purchaseCompleted✗purchase-completed✗PURCHASE_COMPLETED✗
Use past tense for completed actions:
registration_completed✓registration_complete✗register✗
Be specific about timing:
checkout_initiated(user started checkout)checkout_completed(user finished checkout)payment_submitted(payment processing started)payment_confirmed(payment successful)
Avoid abbreviations:
registration_completed✓reg_completed✗user_reg✗
Exception: Universally understood abbreviations (ID, URL, API)
Parameter Names
Parameters (properties) follow the same rules as events.
Use snake_case:
product_id✓productId✗
Be explicit:
revenue_usd✓amount✗total✗
Include units when relevant:
duration_seconds✓duration✗file_size_mb✓file_size✗
Standard Event Schema Template
Core Events (Track in Every App)
1. Install
Event: app_installed
Platform: Auto-tracked by MMP
Parameters: None (MMP adds attribution data automatically)
2. First Open
Event: app_opened_first_time
Trigger: First app launch after install
Parameters:
- platform: "ios" | "android"
- app_version: String
- os_version: String
3. Registration
Event: registration_completed
Trigger: User successfully creates account
Parameters:
- user_id: String (your internal ID)
- registration_method: "email" | "google" | "apple" | "facebook"
- is_organic: Boolean
4. Purchase
Event: purchase_completed
Trigger: Successful purchase confirmation
Parameters:
- revenue: Number (decimal, e.g., 9.99)
- currency: String (ISO 4217, e.g., "USD")
- transaction_id: String (unique, prevents duplicates)
- product_id: String
- product_category: String
- quantity: Integer
- is_first_purchase: Boolean
5. Subscription Started
Event: subscription_started
Trigger: User begins paid subscription
Parameters:
- revenue: Number (subscription price)
- currency: String
- transaction_id: String
- subscription_id: String
- subscription_type: "monthly" | "yearly" | "weekly"
- trial_period_days: Integer (0 if no trial)
- is_renewal: Boolean
E-Commerce Events
Add to Cart
Event: cart_item_added
Parameters:
- product_id: String
- product_name: String
- product_category: String
- price: Number
- currency: String
- quantity: Integer
Checkout Initiated
Event: checkout_initiated
Parameters:
- cart_value: Number
- currency: String
- item_count: Integer
- cart_items: Array of product_ids
Checkout Completed
Event: checkout_completed
Parameters:
- Same as purchase_completed
- plus checkout_method: "credit_card" | "paypal" | "apple_pay" | etc.
Content/Media App Events
Content Viewed
Event: content_viewed
Parameters:
- content_id: String
- content_type: "article" | "video" | "podcast"
- content_category: String
- duration_seconds: Integer (for video/audio)
Content Completed
Event: content_completed
Parameters:
- content_id: String
- content_type: String
- completion_percentage: Integer (100 for full completion)
- time_spent_seconds: Integer
SaaS/Productivity App Events
Feature Activated
Event: feature_activated
Parameters:
- feature_name: String
- feature_tier: "free" | "premium" | "enterprise"
- activation_source: "onboarding" | "settings" | "prompt"
Project Created
Event: project_created
Parameters:
- project_id: String
- project_type: String
- template_used: String | null
- is_first_project: Boolean
Data Types and Validation
Standard Data Types
String:
- User IDs
- Product IDs
- Categories
- Enums ("email" | "google" | "facebook")
Number (Decimal/Float):
- Revenue
- Prices
- Percentages
Integer:
- Quantities
- Counts
- Duration in seconds
- Completion percentages
Boolean:
- is_first_purchase
- is_organic
- is_renewal
Array:
- List of product IDs
- Feature flags
- Tags/categories
Timestamp:
- Usually auto-added by MMP
- Use ISO 8601 format if manually tracking
Validation Rules
Revenue:
- Type: Float/Decimal
- Precision: 2 decimal places
- Range: 0 to 999999.99
- Never negative
- Always include currency parameter
Currency:
- Type: String
- Format: ISO 4217 (uppercase)
- Examples: "USD", "EUR", "GBP"
- Never use symbols ($, €, £)
IDs:
- Type: String (not integer—preserves leading zeros)
- Never null or empty
- Unique per entity
- Consistent format (UUIDs, sequential IDs, etc.)
Enums:
- Lowercase with underscores
- Finite, documented set of values
- Don't let users supply arbitrary values
Implementation Patterns
Centralized Event Tracking
Create a single tracking module that all parts of your app use.
iOS (Swift):
class AnalyticsManager {
static let shared = AnalyticsManager()
enum Event {
case registrationCompleted(method: RegistrationMethod)
case purchaseCompleted(revenue: Decimal, currency: String, productId: String, transactionId: String)
case subscriptionStarted(revenue: Decimal, currency: String, type: SubscriptionType, transactionId: String)
var name: String {
switch self {
case .registrationCompleted: return "registration_completed"
case .purchaseCompleted: return "purchase_completed"
case .subscriptionStarted: return "subscription_started"
}
}
var parameters: [String: Any] {
switch self {
case .registrationCompleted(let method):
return ["registration_method": method.rawValue]
case .purchaseCompleted(let revenue, let currency, let productId, let transactionId):
return [
"revenue": revenue,
"currency": currency,
"product_id": productId,
"transaction_id": transactionId
]
case .subscriptionStarted(let revenue, let currency, let type, let transactionId):
return [
"revenue": revenue,
"currency": currency,
"subscription_type": type.rawValue,
"transaction_id": transactionId
]
}
}
}
func track(_ event: Event) {
// Track to MMP
let adjustEvent = ADJEvent(eventToken: getEventToken(for: event.name))
event.parameters.forEach { key, value in
adjustEvent?.addCallbackParameter(key, value: "\(value)")
}
Adjust.trackEvent(adjustEvent)
// Track to analytics
Mixpanel.mainInstance().track(event: event.name, properties: event.parameters)
}
private func getEventToken(for eventName: String) -> String {
// Map event names to MMP tokens
// Centralized configuration prevents errors
let eventTokens = [
"registration_completed": "abc123",
"purchase_completed": "def456",
"subscription_started": "ghi789"
]
return eventTokens[eventName] ?? ""
}
}
enum RegistrationMethod: String {
case email = "email"
case google = "google"
case apple = "apple"
case facebook = "facebook"
}
enum SubscriptionType: String {
case monthly = "monthly"
case yearly = "yearly"
case weekly = "weekly"
}
Usage:
AnalyticsManager.shared.track(
.registrationCompleted(method: .email)
)
AnalyticsManager.shared.track(
.purchaseCompleted(
revenue: 9.99,
currency: "USD",
productId: "premium_monthly",
transactionId: transaction.id
)
)
Benefits:
- Type safety (compiler catches errors)
- Centralized event name definitions
- Guaranteed consistency across tracking platforms
- Easy to update all platforms from one location
- Self-documenting
Event Mapping Configuration
Store event mapping in configuration files, not code.
events_config.json:
{
"registration_completed": {
"adjust_token": "abc123",
"facebook_event": "CompleteRegistration",
"google_event": "sign_up",
"description": "User successfully created account",
"parameters": {
"user_id": "string",
"registration_method": "enum[email,google,apple,facebook]"
}
},
"purchase_completed": {
"adjust_token": "def456",
"facebook_event": "Purchase",
"google_event": "purchase",
"description": "User completed purchase",
"parameters": {
"revenue": "decimal",
"currency": "string",
"transaction_id": "string",
"product_id": "string"
}
}
}
Benefits:
- Non-developers can update event mappings
- Easy to validate schema consistency
- Can generate documentation automatically
- Single source of truth
Documentation Template
Document every event you track. Use this template:
## Event: purchase_completed
**Description:** Fires when a user successfully completes a purchase
**Trigger:** After payment confirmation, before showing success screen
**Platforms:** iOS, Android, Web
**MMP Token:** def456
**Frequency:** Average 0.05 per user per day
**Parameters:**
| Name | Type | Required | Description | Example |
|------|------|----------|-------------|---------|
| revenue | Decimal | Yes | Purchase amount | 9.99 |
| currency | String | Yes | ISO 4217 code | "USD" |
| transaction_id | String | Yes | Unique transaction ID | "txn_abc123" |
| product_id | String | Yes | Product identifier | "premium_monthly" |
| product_category | String | No | Product category | "subscription" |
| is_first_purchase | Boolean | Yes | First purchase for user | true |
**Example Implementation:**
iOS:
```swift
AnalyticsManager.shared.track(
.purchaseCompleted(
revenue: 9.99,
currency: "USD",
productId: "premium_monthly",
transactionId: transaction.id
)
)
Notes:
- Always use unique transaction_id to prevent duplicate revenue tracking
- Currency must be uppercase ISO 4217
- Revenue must include exactly 2 decimal places
## Common Mistakes to Avoid
### 1. Inconsistent Naming
**Problem:** Different naming across platforms
**Example:**
- iOS: `userCompletedPurchase`
- Android: `purchase_complete`
- MMP: `Purchase Completed`
**Fix:** Enforce snake_case everywhere, document in schema
### 2. Tracking Too Many Events
**Problem:** Tracking every user interaction
**Impact:**
- Higher MMP costs (pay per event)
- Data noise obscures important signals
- Harder to maintain and debug
**Fix:** Limit MMP tracking to 5-15 high-value conversion events
### 3. Missing Transaction IDs
**Problem:** Not using unique IDs for revenue events
**Impact:**
- Duplicate revenue tracking if event fires multiple times
- Inflated revenue reports
- Impossible to debug specific transactions
**Fix:** Always include transaction_id for revenue events
### 4. Dynamic Event Names
**Problem:** Building event names at runtime
**Example:**
```swift
// BAD
track(event: "purchase_\(productType)_completed")
// Results in: purchase_subscription_completed, purchase_addon_completed, etc.
Impact:
- Unlimited event proliferation
- Can't configure in MMP dashboard
- Breaks reporting
Fix: Use parameters, not dynamic names
// GOOD
track(event: "purchase_completed", parameters: ["product_type": productType])
5. Arbitrary Parameter Values
Problem: Letting users supply freeform values
Example:
track(event: "registration_completed", parameters: ["method": userInputMethod])
// Could be anything: "Email", "GOOGLE", "fb", "Facebook Inc.", etc.
Impact:
- Data fragmentation
- Impossible to aggregate
- Reporting breaks down
Fix: Use validated enums
enum RegistrationMethod: String {
case email = "email"
case google = "google"
case facebook = "facebook"
}
Testing and Validation
Pre-Production Validation
1. Schema Documentation Review
- All events documented with descriptions
- Parameter types and validation rules defined
- Required vs optional parameters marked
- Example values provided
2. Code Review Checklist
- Event names use snake_case
- Event names match documentation exactly
- Parameters match documented schema
- Revenue events include transaction_id
- Currency codes are uppercase ISO 4217
- Enum values are validated
3. Test Event Firing
- Events appear in MMP dashboard
- Parameter values are correct types
- Revenue amounts display correctly
- Transaction IDs are unique
- Events fire only once per action
Post-Production Monitoring
Weekly:
- Check for unexpected event names (indicates schema violations)
- Verify event volume matches user activity
- Look for duplicate transaction IDs
Monthly:
- Review parameter value distributions
- Identify unused events (consider removing)
- Check for new edge cases requiring schema updates
FAQs
What events should I track in my MMP?
Track high-value conversion events: install, first open, registration/signup, purchase, subscription, add to cart, and key feature usage. Avoid tracking low-value events like screen views or button clicks at the MMP level—use product analytics tools for those.
Should I use the same event names across all platforms?
Yes, absolutely. Use identical event names and parameter keys across iOS, Android, MMP, analytics platforms, and ad networks. This consistency enables cross-platform analysis and prevents data fragmentation. Document your schema and enforce it through code reviews.
How many events should I track?
Focus on 5-15 core conversion events. More isn't better—each event adds complexity and cost. Track events that directly influence marketing decisions and revenue optimization, not every user interaction.
Can I change event names after launch?
You can, but it's painful. Changing event names requires updating code, MMP configuration, ad network mappings, dashboards, and documentation. Historical data uses old names, creating reporting gaps. Get names right before launch.
Should I version my event schema?
For complex apps, yes. Add a schema_version parameter to events. This lets you evolve the schema while maintaining backward compatibility for analytics that span the transition period.
Event schemas are infrastructure. Like database schemas, they're expensive to change once you have production data flowing. Invest time in getting naming conventions, data types, and validation rules right before implementation, and you'll save months of cleanup work later.
Related Resources

Do You Actually Need an MMP? (2025 Reality Check)
Mobile measurement partners cost thousands per month. Here's how to determine if an MMP is worth it for your app—or if you can skip it entirely.

How to Export and Use MMP Data for Analysis (2025)
Extract raw attribution data from your MMP for custom analysis, LTV modeling, and BI integrations. Complete guide to data exports, APIs, and warehouse connections.

How to Build Effective MMP Reporting Dashboards (2025)
Create mobile attribution dashboards that actually drive decisions. Learn which metrics to track, how to structure reports, and dashboard templates for growth teams.