Amplitude is a product analytics platform built around behavioral event data, cohort analysis, and user journey mapping. Unlike page-view-centric platforms like Google Analytics, Amplitude's value depends entirely on the quality of your event taxonomy and identity resolution. A sloppy implementation with inconsistent event names, missing user properties, and broken identity merges does not just produce bad reports -- it permanently corrupts cohort definitions, funnel analyses, and retention curves that product teams rely on for feature decisions.
Why Proper Implementation Matters
Event Quality Determines Product Insight Quality
Amplitude's core analyses are all event-driven:
- Funnels: Measure conversion between sequential events. If "Add to Cart" is sometimes spelled "add_to_cart" and sometimes "AddToCart", your funnel has a gap.
- Retention: Tracks whether users return to perform specific events. Inconsistent event names create phantom user drop-off.
- Cohorts: Groups of users defined by behavior. A missing user property on 30% of events means 30% of users are excluded from targeting cohorts.
- Pathfinder: Maps user journeys through event sequences. Duplicate or missing events create false paths.
Identity Resolution is Permanent
Amplitude's identity system has strict rules:
device_idis automatically assigned per device/browseruser_idis your application's login identifier- Once a
user_idis associated with adevice_id, the merge is permanent - Calling
identify()with the wrong user at the wrong time (e.g., on a shared computer) permanently associates all anonymous events with the wrong user - There is no way to un-merge users in Amplitude without contacting support and losing data
Event Volume = Cost
Amplitude pricing is based on event volume (Monthly Tracked Users or event count):
- Every
track()call costs money - Autocaptured events (page views, sessions) count toward your quota
- Server-side events count toward your quota
- Testing events in production count toward your quota
A poorly configured implementation that fires excessive events can burn through your event quota quickly.
Pre-Implementation Planning
Access and Permissions
Amplitude Organization:
- Sign in at
analytics.amplitude.com - Verify your organization and project
- Request Admin access for implementation work
Required Access:
| Platform | Role | Purpose |
|---|---|---|
| Amplitude | Admin | Project settings, API keys, Govern rules |
| Amplitude | Manager | Event management, cohort creation |
| Your application | Developer | SDK integration, data layer |
| Server environment | Developer | HTTP API integration for backend events |
API Keys (per environment):
| Environment | Key Type | Where to Find |
|---|---|---|
| Development | API Key | Settings > Projects > Dev Project > API Key |
| Staging | API Key | Settings > Projects > Staging Project > API Key |
| Production | API Key | Settings > Projects > Prod Project > API Key |
| Server-side | Secret Key | Settings > Projects > API Key (Secret Key tab) |
Data Residency:
- US data center: Default (
api.amplitude.com) - EU data center: Must be selected at project creation (
api.eu.amplitude.com) - Cannot be changed after project creation
- EU residency is required for GDPR compliance for EU-based companies
Event Taxonomy Design
Design your taxonomy before writing any code. Use a tracking plan spreadsheet:
| Event Name | Category | Description | Properties | Required | Type |
|---|---|---|---|---|---|
| Page Viewed | Navigation | User views any page | page_name, page_url, referrer | Yes | string |
| Button Clicked | Interaction | User clicks a CTA | button_name, button_location | Yes | string |
| Signup Started | Onboarding | User begins registration | signup_method | Yes | string |
| Signup Completed | Onboarding | User completes registration | signup_method, plan_type | Yes | string |
| Feature Used | Product | User engages a feature | feature_name, feature_context | Yes | string |
| Purchase Completed | Commerce | User completes purchase | revenue, currency, product_id, plan_type | Yes | number/string |
| Subscription Started | Commerce | User starts subscription | plan_type, billing_period, revenue | Yes | string/number |
Naming Conventions:
- Use
Title Casefor event names: "Page Viewed", "Signup Completed" - Use
snake_casefor property names:page_name,plan_type - Prefix platform-specific events: "iOS: App Opened", "Web: Page Viewed"
- Never use abbreviations in event names
Identity Strategy
Define when and how to identify users:
Anonymous Visit (device_id only)
└─> Page Viewed, Button Clicked (anonymous events)
└─> Signup Started
└─> setUserId('USER-123') ← Identity merge happens here
└─> Signup Completed, Feature Used (identified events)
└─> All previous anonymous events retroactively attributed
Rules:
- Set
user_idonly after confirmed authentication (not on form input, not on email entry) - Never set
user_idto an email address (use internal user ID) - Clear
user_idon logout:amplitude.reset()to generate a newdevice_id - For shared devices (kiosks, shared computers): Always call
reset()between users
Implementation Walkthrough
Step 1: Install Amplitude Browser SDK
npm Installation (recommended for SPAs):
npm install @amplitude/analytics-browser
import * as amplitude from '@amplitude/analytics-browser';
// Initialize with configuration
amplitude.init('YOUR_API_KEY', {
// Recommended settings
defaultTracking: {
sessions: true, // Track session start/end
pageViews: true, // Track page views automatically
formInteractions: false, // Disable unless you need form analytics
fileDownloads: false // Disable unless you need download tracking
},
minIdLength: 1, // Minimum user_id length
flushIntervalMillis: 1000, // Send events every 1 second
flushQueueSize: 30, // Or when 30 events are queued
logLevel: amplitude.Types.LogLevel.Warn // Set to Debug during development
});
Script Tag Installation (for traditional sites):
<script type="text/javascript">
!function(){"use strict";!function(e,t){var r=e.amplitude||{_q:[],_iq:{}};if(r.invoked)
e.console&&console.error&&console.error("Amplitude snippet has been loaded.");else{
var n=function(e,t){e.prototype[t]=function(){return this._q.push({name:t,
args:Array.prototype.slice.call(arguments,0)}),this}},s=function(e,t,r){return function(n){
e._q.push({name:t,args:Array.prototype.slice.call(arguments,0),resolve:r})}},
o=["add","append","clearAll","prepend","set","setOnce","unset","preInsert","postInsert","remove",
"getUserProperties"];/* truncated for brevity - use full snippet from Amplitude docs */
r.invoked=!0;var a=t.createElement("script");a.type="text/javascript",a.integrity=
"sha384-AMPLITUDE_HASH",a.crossOrigin="anonymous",a.async=!0,
a.src="https://cdn.amplitude.com/libs/analytics-browser-2.x.min.js",
a.onload=function(){e.amplitude.runQueuedFunctions||console.log("[Amplitude] Error: could not load SDK")};
var c=t.getElementsByTagName("script")[0];c.parentNode.insertBefore(a,c)}}(window,document)}();
amplitude.init('YOUR_API_KEY', {
defaultTracking: { sessions: true, pageViews: true }
});
</script>
Step 2: Implement User Identity
// After successful login
function onUserLogin(user) {
// Set the user ID (permanent merge with device_id)
amplitude.setUserId(user.id); // e.g., 'USR-12345'
// Set user properties
const identifyEvent = new amplitude.Identify();
identifyEvent.set('email_domain', user.email.split('@')[1]);
identifyEvent.set('plan_type', user.plan); // 'free', 'pro', 'enterprise'
identifyEvent.set('account_created', user.createdAt); // ISO date string
identifyEvent.set('company_name', user.company);
identifyEvent.setOnce('first_login_date', new Date().toISOString());
identifyEvent.append('login_methods', user.authMethod); // 'google', 'email', 'sso'
amplitude.identify(identifyEvent);
}
// On logout
function onUserLogout() {
amplitude.track('Logged Out');
amplitude.reset(); // Clears user_id and generates new device_id
}
Step 3: Track Events with Properties
// Page view with context
amplitude.track('Page Viewed', {
page_name: document.title,
page_url: window.location.href,
page_path: window.location.pathname,
referrer: document.referrer,
page_category: getPageCategory() // 'product', 'blog', 'pricing', etc.
});
// Feature usage
amplitude.track('Feature Used', {
feature_name: 'Export Dashboard',
feature_context: 'analytics_page',
export_format: 'csv',
row_count: 1500
});
// Purchase with revenue
amplitude.track('Purchase Completed', {
revenue: 99.00,
currency: 'USD',
product_id: 'PRO_ANNUAL',
plan_type: 'pro',
billing_period: 'annual',
discount_applied: true,
discount_code: 'SAVE20'
});
// Or use the Revenue class for Amplitude's built-in revenue tracking
const revenue = new amplitude.Revenue();
revenue.setProductId('PRO_ANNUAL');
revenue.setPrice(99.00);
revenue.setQuantity(1);
revenue.setRevenueType('subscription');
revenue.setEventProperties({
plan_type: 'pro',
billing_period: 'annual'
});
amplitude.revenue(revenue);
Step 4: Implement Server-Side Tracking
For backend events that cannot be captured client-side (subscription renewals, webhook events, backend processes):
const https = require('https');
const AMPLITUDE_API_KEY = process.env.AMPLITUDE_API_KEY;
const AMPLITUDE_ENDPOINT = 'https://api2.amplitude.com/2/httpapi';
// For EU: 'https://api.eu.amplitude.com/2/httpapi'
async function trackServerEvent(userId, eventName, eventProperties) {
const payload = {
api_key: AMPLITUDE_API_KEY,
events: [{
user_id: userId,
event_type: eventName,
event_properties: eventProperties,
time: Date.now(),
platform: 'server',
ip: '$remote' // Let Amplitude resolve from request IP
}]
};
const response = await fetch(AMPLITUDE_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
return response.json();
}
// Example: Track subscription renewal from Stripe webhook
async function handleStripeRenewal(subscription) {
await trackServerEvent(subscription.metadata.amplitude_user_id, 'Subscription Renewed', {
plan_type: subscription.plan.id,
revenue: subscription.plan.amount / 100,
currency: subscription.currency.toUpperCase(),
billing_period: subscription.plan.interval,
renewal_count: subscription.metadata.renewal_count
});
}
// Example: Track backend process completion
async function handleReportGenerated(userId, reportId) {
await trackServerEvent(userId, 'Report Generated', {
report_id: reportId,
report_type: 'monthly_analytics',
generation_time_ms: 3400
});
}
Step 5: Configure Amplitude Govern
Govern enforces your taxonomy by blocking or transforming non-conforming events:
In Amplitude, go to Data > Events
For each expected event:
- Click the event name
- Set Status to "Expected" (planned) or "Live" (in production)
- Add a Description explaining what the event represents
- Mark required properties
- Set property types (string, number, boolean)
Configure Blocking Rules:
- Go to Data > Govern > Block/Filter
- Block events with unexpected names (catches typos)
- Block events with invalid property types
- Drop sensitive properties (email, phone, SSN) before storage
Configure Transformations:
- Merge duplicate event names (e.g., "add_to_cart" -> "Add to Cart")
- Rename properties for consistency
- Apply across all incoming data
Step 6: Set Up Group Analytics (Optional)
For B2B products where you need account-level analytics:
// Set the user's group (company/account)
amplitude.setGroup('company', 'ACME Corp');
// Set group properties
const groupIdentify = new amplitude.Identify();
groupIdentify.set('plan', 'enterprise');
groupIdentify.set('employee_count', 500);
groupIdentify.set('industry', 'technology');
amplitude.groupIdentify('company', 'ACME Corp', groupIdentify);
This enables account-level funnels, retention, and revenue analysis in Amplitude.
Verification and QA
Event Stream (Real-Time Debugging)
- In Amplitude, go to User Lookup
- Search by your test user ID or device ID
- View the Event Stream to see events in real-time
- For each event, verify:
- Event name matches taxonomy exactly (case-sensitive)
- All required properties are present
- Property values have correct types
user_idanddevice_idare correct
Instrumentation Alerts
- Go to Data > Events
- Check for:
- Unexpected events: Events not in your taxonomy (typos, test events)
- Volume anomalies: Sudden spikes or drops in event counts
- Property issues: Missing required properties or wrong types
Identity Validation
- Create a test user account
- Browse anonymously, then log in
- In User Lookup, verify:
- Anonymous events are merged with the identified user
device_idanduser_idare both present after login- User properties are correctly set
- Log out and verify
reset()generates a newdevice_id - Log in as a different user and verify no cross-contamination
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Events not appearing | Wrong API key or network block | Check API key, test with logLevel: Debug |
| Duplicate events | SDK initialized twice | Ensure init() is called only once |
| Identity merge wrong | setUserId called before auth confirmation |
Only set after successful login |
| Missing properties | Property passed as undefined | Check for null/undefined before tracking |
| EU compliance failure | Using US endpoint for EU project | Verify data center in project settings |
| Event quota exceeded | Autocapture or excessive tracking | Disable unnecessary defaultTracking options |
| Properties show wrong type | String passed instead of number | Validate types before track() call |
Deployment Artifacts
- Tracking plan: Event names, properties, types, and descriptions (sync with Govern)
- API keys and secrets: Per environment, stored securely
- Identity strategy document: When to set/clear
user_id, merge rules, shared device handling - SDK configuration:
init()parameters per environment - Server-side integration: HTTP API endpoint, authentication, and payload format
- Govern rules: Blocking rules, transformations, and expected event statuses
- Data residency: US or EU endpoint configuration
Linked Runbooks
- Install or Embed the Tag or SDK -- SDK installation methods
- Event Tracking -- Detailed event and property configuration
- Data Layer Setup -- Data layer structure for Amplitude events
- Cross-Domain Tracking -- Identity management across domains
- Server-Side vs Client-Side -- Browser SDK vs. HTTP API decisions
Change Log and Owners
- Document who owns Govern approvals and taxonomy changes
- Track open questions about default tracking settings and their quota impact
- Maintain API key rotation schedule
- Record identity strategy changes with rationale
- Review quarterly: event volume trends, Govern violations, data quality metrics