Amplitude Event Tracking | OpsBlu Docs

Amplitude Event Tracking

How to implement custom event tracking in Amplitude. Covers event naming conventions, required and optional parameters, ecommerce events, debugging with.

Overview

Event tracking in Amplitude captures discrete user actions and their context, forming the foundation for behavioral analytics. A well-designed event taxonomy enables powerful funnel analysis, cohort segmentation, and retention studies while remaining maintainable as your product evolves.

This guide covers event design principles, implementation patterns, and quality assurance practices for Amplitude.

Event Model Architecture

Event Structure

Every Amplitude event consists of:

  1. Event Name: Action being tracked (e.g., product_viewed, purchase_completed)
  2. Event Properties: Contextual data specific to this action
  3. User Properties: Attributes describing the user
  4. Timestamp: When the event occurred
  5. User ID / Device ID: Who performed the action
amplitude.track('product_viewed', {
  // Event properties
  product_id: 'SKU-12345',
  product_name: 'Wireless Headphones',
  category: 'Electronics',
  price: 79.99,
  currency: 'USD',
  view_source: 'search_results'
});

Event Design Principles

  1. Specificity: Events should represent distinct actions (checkout_started vs. generic button_clicked)
  2. Consistency: Use the same event for the same action across all contexts
  3. Clarity: Event names should be self-documenting (user_logged_in not event1)
  4. Avoid overloading: Don't use one event for multiple unrelated actions

Event vs. Property Decision

Use Event Name When Use Event Property When
Action is fundamentally different Action is the same, context varies
You need separate funnel steps You want to filter/segment same action
Business logic differs Reporting grouping needed

Example:

// Good: Same action, context as property
amplitude.track('video_played', {
  video_id: 'VID-123',
  video_category: 'tutorial' // Property
});

amplitude.track('video_played', {
  video_id: 'VID-456',
  video_category: 'marketing' // Property
});

// Bad: Separate events for same action
amplitude.track('tutorial_video_played', { video_id: 'VID-123' });
amplitude.track('marketing_video_played', { video_id: 'VID-456' });

Core Event Catalog

Page View

amplitude.track('page_viewed', {
  page_path: window.location.pathname,
  page_title: document.title,
  page_url: window.location.href,
  page_type: 'product', // home, category, product, cart, checkout
  referrer: document.referrer
});

Route View (SPAs)

// For single-page applications
router.afterEach((to, from) => {
  amplitude.track('route_viewed', {
    route_path: to.path,
    route_name: to.name,
    page_title: to.meta.title,
    previous_route: from.path
  });
});
amplitude.track('navigation_clicked', {
  link_text: 'Products',
  link_url: '/products',
  link_position: 'header',
  navigation_type: 'main_menu'
});

Ecommerce Events

Product Viewed

amplitude.track('product_viewed', {
  product_id: 'SKU-12345',
  product_name: 'Wireless Headphones',
  sku: 'SKU-12345',
  category: 'Electronics',
  subcategory: 'Audio',
  brand: 'AudioTech',
  price: 79.99,
  currency: 'USD',
  availability: 'in_stock',
  view_source: 'search_results' // search, recommendation, direct, category
});

Product List Viewed

amplitude.track('product_list_viewed', {
  list_name: 'Search Results',
  list_id: 'search_electronics_headphones',
  category: 'Electronics',
  item_count: 24,
  sort_order: 'price_low_to_high',
  filters_applied: ['brand:AudioTech', 'price:50-100']
});

Add to Cart

amplitude.track('product_added_to_cart', {
  product_id: 'SKU-12345',
  product_name: 'Wireless Headphones',
  price: 79.99,
  quantity: 1,
  currency: 'USD',
  cart_total: 79.99,
  cart_item_count: 1,
  add_method: 'quick_add' // quick_add, pdp_button, recommendation
});

Remove from Cart

amplitude.track('product_removed_from_cart', {
  product_id: 'SKU-12345',
  product_name: 'Wireless Headphones',
  price: 79.99,
  quantity: 1,
  cart_total: 0,
  cart_item_count: 0,
  removal_reason: 'user_action' // user_action, out_of_stock
});

Cart Viewed

amplitude.track('cart_viewed', {
  cart_total: 142.97,
  cart_item_count: 3,
  currency: 'USD',
  cart_items: ['SKU-12345', 'SKU-67890', 'SKU-11111']
});

Checkout Started

amplitude.track('checkout_started', {
  cart_total: 142.97,
  cart_item_count: 3,
  currency: 'USD',
  coupon_code: 'SUMMER10',
  discount_amount: 14.30,
  checkout_type: 'standard' // standard, express, guest
});

Shipping Info Added

amplitude.track('shipping_info_added', {
  shipping_method: 'ground',
  shipping_cost: 5.99,
  shipping_tier: 'standard',
  estimated_delivery_days: 5
});

Payment Info Added

amplitude.track('payment_info_added', {
  payment_method: 'credit_card',
  payment_type: 'Visa',
  saved_payment_used: false
});

Purchase Completed

// Revenue API (recommended for revenue tracking)
const revenue = new amplitude.Revenue()
  .setProductId('SKU-12345')
  .setPrice(79.99)
  .setQuantity(1)
  .setRevenueType('purchase');

amplitude.logRevenueV2(revenue);

// Purchase event
amplitude.track('purchase_completed', {
  order_id: 'ORD-98765',
  total_amount: 156.96,
  subtotal: 142.97,
  tax: 8.00,
  shipping: 5.99,
  discount: 14.30,
  currency: 'USD',
  coupon_code: 'SUMMER10',
  item_count: 3,
  payment_method: 'credit_card',
  is_first_purchase: false,
  items: [
    {
      product_id: 'SKU-12345',
      product_name: 'Wireless Headphones',
      category: 'Electronics',
      price: 79.99,
      quantity: 1
    }
  ]
});

Feature Engagement Events

Feature Used

amplitude.track('feature_used', {
  feature_name: 'advanced_search',
  feature_category: 'search',
  feature_context: 'product_catalog',
  usage_count: 1 // Increment with user property
});

Search Performed

amplitude.track('search_performed', {
  search_query: 'wireless headphones',
  search_type: 'product', // product, help, content
  result_count: 24,
  has_filters: true,
  filters_applied: ['category:Electronics'],
  search_location: 'header'
});

Filter Applied

amplitude.track('filter_applied', {
  filter_type: 'category',
  filter_value: 'Electronics',
  filter_group: 'product_filters',
  result_count_before: 100,
  result_count_after: 24
});

Sort Applied

amplitude.track('sort_applied', {
  sort_option: 'price_low_to_high',
  sort_location: 'product_list',
  result_count: 24
});

User Lifecycle Events

User Signed Up

amplitude.track('user_signed_up', {
  signup_method: 'email', // email, google, facebook, apple
  referral_source: 'organic',
  signup_location: 'homepage_hero',
  account_type: 'individual'
});

User Logged In

amplitude.track('user_logged_in', {
  login_method: 'email_password',
  login_location: 'header',
  session_count: 5
});

User Logged Out

amplitude.track('user_logged_out', {
  logout_location: 'account_menu',
  session_duration_seconds: 1200
});

Account Created

amplitude.track('account_created', {
  account_type: 'business',
  plan: 'free',
  signup_method: 'email',
  referral_code: 'FRIEND123',
  team_size: 5
});

Subscription Events

Subscription Started

amplitude.track('subscription_started', {
  plan: 'premium',
  billing_cycle: 'monthly',
  price: 29.99,
  currency: 'USD',
  trial_included: true,
  trial_days: 14
});

Subscription Upgraded

amplitude.track('subscription_upgraded', {
  previous_plan: 'basic',
  new_plan: 'premium',
  price_difference: 15.00,
  upgrade_reason: 'user_initiated'
});

Subscription Downgraded

amplitude.track('subscription_downgraded', {
  previous_plan: 'premium',
  new_plan: 'basic',
  downgrade_reason: 'cost',
  feedback_provided: true
});

Subscription Canceled

amplitude.track('subscription_canceled', {
  plan: 'premium',
  cancellation_reason: 'too_expensive',
  days_subscribed: 90,
  total_value_paid: 89.97,
  retention_offer_shown: true,
  retention_offer_accepted: false
});

Engagement Events

Form Started

amplitude.track('form_started', {
  form_id: 'contact_form',
  form_name: 'Contact Us',
  form_location: 'footer'
});

Form Submitted

amplitude.track('form_submitted', {
  form_id: 'contact_form',
  form_name: 'Contact Us',
  form_location: 'footer',
  time_to_complete_seconds: 45,
  fields_completed: 5,
  fields_total: 5
});

Form Error

amplitude.track('form_error', {
  form_id: 'contact_form',
  error_type: 'validation',
  error_field: 'email',
  error_message: 'Invalid email format'
});

Video Started

amplitude.track('video_started', {
  video_id: 'VID-123',
  video_title: 'Product Demo',
  video_category: 'tutorial',
  video_duration_seconds: 120,
  video_location: 'product_page',
  autoplay: false
});

Video Progress

amplitude.track('video_progress', {
  video_id: 'VID-123',
  percent_watched: 50,
  seconds_watched: 60,
  milestone: '50%' // 25%, 50%, 75%, 100%
});

Video Completed

amplitude.track('video_completed', {
  video_id: 'VID-123',
  video_title: 'Product Demo',
  total_watch_time_seconds: 120,
  completion_rate: 100
});

Share Action

amplitude.track('content_shared', {
  content_type: 'product',
  content_id: 'SKU-12345',
  share_method: 'email', // email, facebook, twitter, link
  share_location: 'product_page'
});

Event Property Standards

Required Properties by Event Type

  • event_timestamp: ISO 8601 format
  • schema_version: Track implementation version

Ecommerce Events

  • currency: ISO 4217 code (USD, EUR, GBP)
  • product_id: Unique product identifier
  • price: Numeric value
  • page_path or route_path
  • page_title

User Action Events

  • Context about where action occurred
  • Outcome or result of action

Property Value Guidelines

Property Type Format Example
IDs String "SKU-12345", "ORD-98765"
Prices Number 79.99 (not "$79.99")
Currencies String (ISO 4217) "USD"
Timestamps String (ISO 8601) "2024-01-15T10:30:00Z"
Booleans Boolean true (not "true")
Enums Lowercase snake_case "credit_card", "product_page"

Naming Conventions

Event Naming

Use past tense + snake_case:

  • product_viewed, cart_updated, purchase_completed
  • productView, Cart Update, Purchase Complete

Object-Action Pattern

Structure: [object]_[action]

  • product_viewed
  • cart_updated
  • subscription_canceled
  • filter_applied

Property Naming

Use snake_case and be descriptive:

  • product_id, total_amount, payment_method
  • prodId, amt, pm

Consistent Terminology

Maintain consistency across events:

Use Consistently Avoid Mixing
product_id prod_id, productId, sku
user_id userId, uid, customer_id
total_amount total, amount, price

Deduplication with insert_id

Prevent duplicate events during retries:

const insertId = `${userId}_${eventType}_${Date.now()}_${Math.random()}`;

amplitude.track('purchase_completed', {
  order_id: 'ORD-98765',
  total_amount: 156.96
}, {
  insert_id: insertId
});

For deterministic deduplication:

// Use order_id as insert_id for purchase events
amplitude.track('purchase_completed', {
  order_id: 'ORD-98765',
  total_amount: 156.96
}, {
  insert_id: `purchase_${orderId}` // Dedupe based on order
});

User ID and Device ID Management

Pre-Authentication (Anonymous)

// Amplitude auto-generates device_id
amplitude.init('YOUR_API_KEY');

// All events tracked with device_id only
amplitude.track('product_viewed', { product_id: 'SKU-12345' });

Post-Authentication

// Set user_id after login
amplitude.setUserId('user_12345');

// Events now have both user_id and device_id
amplitude.track('product_viewed', { product_id: 'SKU-67890' });

Logout Handling

// On logout
amplitude.track('user_logged_out');

// Clear user_id (keep device_id for continuity)
amplitude.setUserId(null);
amplitude.regenerateDeviceId(); // Optional: start fresh device tracking

Tracking Plan Governance

Define Expected Events

Create a tracking plan document:

Event Name Description Required Properties Optional Properties Owner
product_viewed User viewed product detail product_id, product_name, price category, brand Product Team
purchase_completed Purchase confirmed order_id, total_amount, currency coupon_code, discount Commerce Team

Use Amplitude Govern

  1. Define taxonomy: Mark events as "Expected" or "Planned"
  2. Set properties: Define required vs. optional properties
  3. Enforce schema: Block unexpected events or properties
  4. Monitor violations: Track when unexpected events occur

Schema Versioning

Include version in events:

amplitude.track('purchase_completed', {
  schema_version: '2.1.0',
  order_id: 'ORD-98765',
  total_amount: 156.96
});

Change management:

  • v2.0.0 → v2.1.0: Added optional property (backward compatible)
  • v2.1.0 → v3.0.0: Renamed/removed property (breaking change)

QA and Validation

Pre-Launch Checklist

Check Validation Method
Event fires correctly Browser console + debugger
Properties have correct types Event Stream inspection
Required properties present Govern schema validation
No PII in properties Manual review
User ID set after login Event Stream user_id field
Revenue events track correctly Revenue LTV chart
Funnels work end-to-end Funnel chart test
Session continuity maintained User Sessions view

Testing in Event Stream

  1. Navigate to Data > Events in Amplitude
  2. Enable Event Stream (real-time view)
  3. Trigger events in your application
  4. Verify in Event Stream:
    • Event name appears
    • Properties present with correct values
    • User ID/Device ID correct
    • Timestamps accurate

Funnel Testing

Test critical funnels end-to-end:

// Test checkout funnel
amplitude.track('product_viewed', { product_id: 'TEST-SKU' });
amplitude.track('product_added_to_cart', { product_id: 'TEST-SKU' });
amplitude.track('checkout_started', { cart_total: 99.99 });
amplitude.track('payment_info_added', { payment_method: 'test_card' });
amplitude.track('purchase_completed', { order_id: 'TEST-ORDER' });

Then in Amplitude:

  1. Create funnel: Product Viewed → Added to Cart → Checkout Started → Purchase Completed
  2. Filter by order_id = "TEST-ORDER"
  3. Verify 100% conversion through all steps

Revenue Validation

Confirm revenue tracking works:

// Track test revenue
const revenue = new amplitude.Revenue()
  .setProductId('TEST-SKU')
  .setPrice(99.99)
  .setQuantity(1)
  .setRevenueType('test_purchase');

amplitude.logRevenueV2(revenue);

Validate:

  1. Go to Analytics > Revenue LTV
  2. Filter by recent test events
  3. Confirm revenue appears correctly

Attribution Testing

Verify attribution persists through funnels:

  1. Land with UTM: ?utm_source=test&utm_campaign=qa
  2. Trigger multiple events
  3. Complete conversion
  4. In Amplitude, verify all events attributed to utm_source=test

Troubleshooting

Symptom Likely Cause Solution
Events not appearing API key incorrect Verify API key in init
Properties missing Not passed in track call Check track() parameters
Wrong data types String instead of number Enforce types: parseFloat(), parseInt()
Duplicate events No insert_id Add unique insert_id to events
User ID not persisting Not set after login Call setUserId() after authentication
Revenue not in LTV chart Using track() instead of Revenue API Use logRevenueV2() for revenue
Funnel steps missing Event name typo Verify exact event names in Event Stream
Session fragmentation Session timeout too short Increase sessionTimeout in init
Attribution lost UTM not captured Capture UTM on first page load

Advanced Patterns

Conditional Event Tracking

function trackConditionalEvent(eventName, properties, condition) {
  if (condition) {
    amplitude.track(eventName, properties);
  }
}

// Only track for premium users
trackConditionalEvent('premium_feature_used', {
  feature_name: 'advanced_analytics'
}, user.plan === 'premium');

Event Queuing for Offline

class OfflineEventQueue {
  constructor() {
    this.queue = JSON.parse(localStorage.getItem('eventQueue') || '[]');
  }

  add(eventName, properties) {
    this.queue.push({ eventName, properties, timestamp: Date.now() });
    localStorage.setItem('eventQueue', JSON.stringify(this.queue));
  }

  flush() {
    this.queue.forEach(({ eventName, properties }) => {
      amplitude.track(eventName, properties);
    });
    this.queue = [];
    localStorage.setItem('eventQueue', '[]');
  }
}

const eventQueue = new OfflineEventQueue();

// When offline
if (!navigator.onLine) {
  eventQueue.add('product_viewed', { product_id: 'SKU-12345' });
}

// When back online
window.addEventListener('online', () => {
  eventQueue.flush();
});

Batch Event Tracking

function trackBatchEvents(events) {
  events.forEach(({ eventName, properties }) => {
    amplitude.track(eventName, properties);
  });
}

// Track multiple events at once
trackBatchEvents([
  { eventName: 'page_viewed', properties: { page_path: '/products' } },
  { eventName: 'feature_impression', properties: { feature: 'banner' } },
  { eventName: 'recommendation_shown', properties: { item_count: 4 } }
]);

Best Practices

  1. Start simple: Begin with 10-20 core events, expand as needed
  2. Document everything: Maintain a tracking plan with ownership
  3. Test thoroughly: Validate in Event Stream before production
  4. Use consistent naming: Enforce snake_case and past tense
  5. Include context: Add properties that explain "why" and "where"
  6. Deduplicate carefully: Use insert_id for idempotent events
  7. Track user lifecycle: Capture signup, login, subscription changes
  8. Measure outcomes: Focus on events that indicate success/failure
  9. Version your schema: Communicate breaking changes
  10. Review regularly: Audit events quarterly and deprecate unused ones