Mixpanel Setup & Implementation | OpsBlu Docs

Mixpanel Setup & Implementation

How to implement Mixpanel analytics with SDK installation, event tracking, identity resolution, and data governance.

Mixpanel is an event-based product analytics platform focused on user behavior analysis through funnels, retention curves, and cohort comparisons. Like Amplitude, Mixpanel's value depends entirely on event quality and identity resolution. The critical difference is Mixpanel's identity model: the distinct_id system and the identify/alias flow have historically been a source of implementation bugs that permanently corrupt user profiles. Mixpanel has simplified this with their "Simplified ID Merge" system, but understanding the identity model remains the most important part of a correct implementation.

Why Proper Implementation Matters

Identity Resolution Mistakes are Permanent

Mixpanel's identity system assigns every user a distinct_id:

  • Before login: distinct_id is an auto-generated anonymous ID (UUID)
  • After login: You call mixpanel.identify('user-123') to merge the anonymous ID with your internal ID
  • This merge is permanent and irreversible
  • If you call identify() with the wrong user (shared device, wrong timing), all anonymous events are permanently attributed to the wrong person
  • Un-merging requires Mixpanel support intervention and may result in data loss

Event Naming Affects All Downstream Analysis

Every Mixpanel analysis (funnels, retention, flows) references events by name:

  • Event names are case-sensitive: "Sign Up" and "sign_up" are different events
  • Renaming events in Lexicon only changes the display name, not the underlying data
  • Saved reports, boards, and alerts reference the original event name
  • A taxonomy change mid-implementation splits historical data

Pricing Is Event-Based

Mixpanel's pricing tiers are based on monthly tracked users (MTU) or event volume:

  • Every track() call counts toward your quota
  • Page views, if tracked, count as events
  • Server-side events count toward your quota
  • Profile updates (people.set()) do not count as events but have storage implications

Pre-Implementation Planning

Access and Permissions

Mixpanel Account:

  1. Sign in at mixpanel.com
  2. Navigate to your organization and project
  3. Request Owner or Admin access for implementation

Required Access:

Platform Role Purpose
Mixpanel Owner/Admin Project settings, API credentials, Lexicon
Mixpanel Analyst Report creation, cohort management
Application Developer SDK integration
Server Developer HTTP API integration

Project Credentials (per environment):

Credential Where to Find Use
Project Token Settings > Project Settings Client-side SDK initialization
API Secret Settings > Project Settings Server-side API authentication
Service Account Settings > Service Accounts Automated imports and exports

Data Residency:

  • US servers: Default (api.mixpanel.com)
  • EU servers: Must be selected at project creation (api-eu.mixpanel.com)
  • Cannot be changed after project creation
  • EU residency required for GDPR compliance for EU-based companies

Event Taxonomy Design

Design your taxonomy before implementation:

Event Name Category Key Properties Description
Page Viewed Navigation page_name, page_url, page_type User views any page
Sign Up Started Onboarding signup_method User initiates registration
Sign Up Completed Onboarding signup_method, plan_type User completes registration
Feature Used Product feature_name, feature_context User engages a product feature
Item Added to Cart Commerce item_id, item_name, price, quantity User adds product to cart
Purchase Completed Commerce order_id, revenue, currency, items User completes purchase
Subscription Started Commerce plan_type, billing_period, mrr User starts subscription

Naming Conventions:

  • Event names: Title Case with spaces ("Purchase Completed", not "purchase_completed")
  • Property names: snake_case ("item_name", "plan_type")
  • Consistent across client and server implementations

Identity Strategy

Simplified ID Merge (recommended, default for new projects):

Anonymous Visit
  └─> distinct_id = "$device:UUID-123"  (auto-generated)
  └─> Page Viewed, Button Clicked (anonymous events)
  └─> User logs in
      └─> mixpanel.identify("USER-456")
          └─> distinct_id becomes "USER-456"
          └─> Anonymous events are automatically merged
          └─> All future events use "USER-456"

Rules:

  • Call identify() only after confirmed authentication
  • Never use email as distinct_id (emails change; use internal user ID)
  • Call mixpanel.reset() on logout to generate a new anonymous ID
  • For shared devices: Always reset() between users

Original ID Merge (legacy projects):

  • Uses alias() instead of identify() for first-time user creation
  • More complex and error-prone
  • Migrate to Simplified ID Merge if possible (check project settings)

Implementation Walkthrough

Step 1: Install Mixpanel JavaScript SDK

npm Installation (recommended for SPAs):

npm install mixpanel-browser
import mixpanel from 'mixpanel-browser';

mixpanel.init('YOUR_PROJECT_TOKEN', {
  debug: process.env.NODE_ENV === 'development',
  track_pageview: true,           // Auto-track page views
  persistence: 'localStorage',    // 'cookie' or 'localStorage'
  // For EU data residency:
  // api_host: 'https://api-eu.mixpanel.com',
  ignore_dnt: false,              // Respect Do Not Track header
  // For proxy setup (bypass ad blockers):
  // api_host: 'https://analytics.yoursite.com',
});

Script Tag Installation:

<script type="text/javascript">
  (function(f,b){if(!b.__SV){var e,g,i,h;window.mixpanel=b;b._i=[];
  b.init=function(e,f,c){function g(a,d){var b=d.split(".");2==b.length&&
  (a=a[b[0]],d=b[1]);a[d]=function(){a.push([d].concat(Array.prototype.slice.call(
  arguments,0)))}}var a=b;"undefined"!==typeof c?a=b[c]=[]:c="mixpanel";a.people=
  a.people||[];a.toString=function(a){var d="mixpanel";"mixpanel"!==c&&(d+="."+c);
  a||(d+=" (stub)");return d};a.people.toString=function(){return a.toString(1)+
  ".people (stub)"};i="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");
  for(h=0;h<i.length;h++)g(a,i[h]);var j="set set_once union unset remove delete".split(" ");
  a.get_group=function(){function b(c){d[c]=function(){call2_args=arguments;call2=[c].concat(
  Array.prototype.slice.call(call2_args,0));a.push([e,call2])}}for(var d={},e=["get_group"].concat(
  Array.prototype.slice.call(arguments,0)),c=0;c<j.length;c++)b(j[c]);return d};
  b._i.push([e,f,c])};b.__SV=1.2;e=f.createElement("script");e.type="text/javascript";
  e.async=!0;e.src="https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";
  g=f.getElementsByTagName("script")[0];g.parentNode.insertBefore(e,g)}})(document,window.mixpanel||[]);

  mixpanel.init('YOUR_PROJECT_TOKEN', {
    track_pageview: true,
    persistence: 'localStorage'
  });
</script>

Step 2: Implement User Identity

// After successful authentication
function onUserLogin(user) {
  // Identify the user - merges anonymous profile with identified profile
  mixpanel.identify(user.id);  // e.g., 'USR-12345'

  // Set user profile properties
  mixpanel.people.set({
    '$email': user.email,              // Special property (used for notifications)
    '$name': user.name,                // Special property (display name)
    '$created': user.createdAt,        // Special property (signup date)
    'plan_type': user.plan,            // Custom property
    'company': user.company,
    'role': user.role
  });

  // Set once (only if not already set)
  mixpanel.people.set_once({
    'first_login_date': new Date().toISOString(),
    'initial_referrer': document.referrer,
    'initial_utm_source': getUTMParam('utm_source')
  });
}

// On logout
function onUserLogout() {
  mixpanel.track('Logged Out');
  mixpanel.reset();  // Clears distinct_id and super properties
}

Step 3: Track Events with Properties

// Page view (if not using auto-tracking)
mixpanel.track('Page Viewed', {
  page_name: document.title,
  page_url: window.location.href,
  page_type: getPageType(),  // 'product', 'blog', 'pricing'
  referrer: document.referrer
});

// Product interaction
mixpanel.track('Item Added to Cart', {
  item_id: 'SKU-12345',
  item_name: 'Premium Widget',
  category: 'Widgets',
  price: 49.99,
  quantity: 1,
  currency: 'USD'
});

// Purchase with revenue
mixpanel.track('Purchase Completed', {
  order_id: 'ORD-2024-12345',
  revenue: 129.98,
  currency: 'USD',
  item_count: 2,
  discount_code: 'SAVE10',
  payment_method: 'credit_card',
  items: [
    { id: 'SKU-12345', name: 'Premium Widget', price: 49.99, quantity: 1 },
    { id: 'SKU-67890', name: 'Deluxe Gadget', price: 79.99, quantity: 1 }
  ]
});

// Also track revenue on the user profile for LTV calculation
mixpanel.people.track_charge(129.98, {
  order_id: 'ORD-2024-12345',
  currency: 'USD'
});

// Feature usage
mixpanel.track('Feature Used', {
  feature_name: 'Export Dashboard',
  feature_context: 'analytics_page',
  export_format: 'csv'
});

Step 4: Register Super Properties

Super properties are automatically appended to every event:

// Set super properties after login (persisted across sessions)
mixpanel.register({
  'user_plan': 'pro',
  'app_version': '2.4.1',
  'platform': 'web'
});

// Set super properties that only apply once (first value wins)
mixpanel.register_once({
  'first_touch_utm_source': getUTMParam('utm_source'),
  'first_touch_utm_medium': getUTMParam('utm_medium'),
  'first_touch_utm_campaign': getUTMParam('utm_campaign')
});

// Remove a super property
mixpanel.unregister('deprecated_property');

Step 5: Server-Side Event Tracking

For backend events that cannot be captured client-side:

const Mixpanel = require('mixpanel');

const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN', {
  // For EU data residency:
  // host: 'api-eu.mixpanel.com',
});

// Track server-side event
function trackServerEvent(distinctId, eventName, properties) {
  mixpanel.track(eventName, {
    distinct_id: distinctId,
    ...properties,
    $source: 'server'  // Tag as server-generated
  });
}

// Update user profile from server
function updateUserProfile(distinctId, properties) {
  mixpanel.people.set(distinctId, properties);
}

// Example: Subscription renewal from Stripe webhook
async function handleStripeRenewal(subscription) {
  const userId = subscription.metadata.mixpanel_distinct_id;

  trackServerEvent(userId, 'Subscription Renewed', {
    plan_type: subscription.plan.id,
    revenue: subscription.plan.amount / 100,
    currency: subscription.currency.toUpperCase(),
    billing_period: subscription.plan.interval,
    renewal_count: parseInt(subscription.metadata.renewal_count || '1')
  });

  // Track charge on profile for LTV
  mixpanel.people.track_charge(userId, subscription.plan.amount / 100, {
    plan_type: subscription.plan.id,
    type: 'renewal'
  });

  // Update profile
  updateUserProfile(userId, {
    'subscription_status': 'active',
    'last_renewal_date': new Date().toISOString(),
    'total_renewals': parseInt(subscription.metadata.renewal_count || '1')
  });
}

// Batch import for historical data
const importBatch = [
  { event: 'Purchase Completed', properties: { distinct_id: 'USR-1', revenue: 99, time: 1704067200 }},
  { event: 'Purchase Completed', properties: { distinct_id: 'USR-2', revenue: 149, time: 1704153600 }}
];

mixpanel.import_batch(importBatch, { strict_mode: true });

Step 6: Configure Lexicon

Lexicon is Mixpanel's data dictionary. Curate it to maintain taxonomy quality:

  1. In Mixpanel, go to Data Management > Lexicon

  2. For each event:

    • Set Status: Expected, Live, or Deprecated
    • Add Description: Business context and when this event fires
    • Set Owner: Team or individual responsible
    • Tag related events (e.g., all commerce events)
  3. For each property:

    • Set Data Type: String, Number, Boolean, DateTime, List
    • Add Description: What values are expected
    • Mark as Required or Optional
    • Set Example Values
  4. Configure Data Views to hide deprecated or internal events from non-technical users

Step 7: Set Up Group Analytics (B2B)

For account-level analytics in B2B products:

// Define the group (account/company)
mixpanel.set_group('company', 'ACME Corp');

// Set group profile properties
mixpanel.get_group('company', 'ACME Corp').set({
  'plan': 'enterprise',
  'employee_count': 500,
  'industry': 'technology',
  'account_manager': 'jane@yourcompany.com'
});

// Events automatically include group membership
mixpanel.track('Feature Used', {
  feature_name: 'Advanced Export'
  // 'company' group automatically appended
});

Group Analytics must be enabled in project settings and requires a paid plan.

Verification and QA

Live View

  1. In Mixpanel, go to Events (or use the search bar to find "Live View")
  2. Filter by your test user's distinct_id
  3. Perform actions on your website
  4. Verify in real-time:
    • Events appear with correct names (case-sensitive)
    • Properties are populated with correct values and types
    • distinct_id is correct (anonymous before login, user ID after)
    • Super properties are appended to every event

Identity Validation

  1. Create a new browser session (incognito)
  2. Browse several pages (anonymous events)
  3. Log in as a test user
  4. In Mixpanel User Profiles, search for your test user
  5. Verify:
    • Anonymous events are merged with the identified profile
    • Profile properties are set correctly
    • No duplicate profiles exist for the same user
  6. Log out and verify reset() generates new anonymous ID
  7. Log in as a different user -- verify no cross-contamination

Lexicon Validation

  1. Go to Data Management > Lexicon
  2. Check for:
    • Unexpected events: Events not in your taxonomy (typos, test events)
    • Undescribed events: Events without descriptions or owners
    • Type mismatches: Properties with unexpected data types
    • Deprecated events: Old events still receiving data

Common Issues

Issue Cause Fix
Events not appearing Wrong project token Verify token in init() matches project settings
Identity merge failure identify() called before login confirmed Only call after successful authentication
Duplicate user profiles reset() not called on logout Add reset() to logout flow
Super properties missing register() called before init() Ensure SDK initialized before registering
EU compliance failure Using US endpoint Set api_host: 'https://api-eu.mixpanel.com'
Server events missing Wrong distinct_id Ensure server uses same user ID as client-side identify()
Profile charges wrong Currency not normalized Always use same currency code
Events blocked by ad blocker Default Mixpanel domain blocked Set up first-party proxy

First-Party Proxy Setup

To bypass ad blockers, route Mixpanel requests through your own domain:

# Nginx reverse proxy configuration
location /mp/ {
    proxy_pass https://api.mixpanel.com/;
    proxy_set_header Host api.mixpanel.com;
    proxy_set_header X-Real-IP $remote_addr;
}

location /mp-decide/ {
    proxy_pass https://decide.mixpanel.com/;
    proxy_set_header Host decide.mixpanel.com;
}
// Point SDK to your proxy
mixpanel.init('YOUR_TOKEN', {
  api_host: 'https://yoursite.com/mp'
});

Deployment Artifacts

  • Project tokens and API secrets: Per environment, stored securely
  • Tracking plan: Events, properties, types, and descriptions (sync with Lexicon)
  • Identity strategy document: identify() timing, reset() behavior, shared device handling
  • Super property definitions: Which properties to register and when
  • Server-side integration: Import API endpoint, authentication, payload format
  • Lexicon export: Event and property definitions with owners
  • Proxy configuration (if applicable): Nginx/CDN config for first-party tracking
  • Data residency: US or EU endpoint configuration

Linked Runbooks

Change Log and Owners

  • Document who manages Lexicon curation and event approval
  • Track super property changes with rationale
  • Maintain project token rotation schedule
  • Record identity strategy changes (especially migration from Original to Simplified ID Merge)
  • Review quarterly: event volume, Lexicon health, profile merge accuracy