The churn radar for B2B SaaS·Book a call·Setup in 10 minutes·Trusted by CS teams·SOC 2 · GDPR · AES-256·
The churn radar for B2B SaaS·Book a call·Setup in 10 minutes·Trusted by CS teams·SOC 2 · GDPR · AES-256·

Event tracking guide

Track product usage events to power Customer Insights health scores and customer health monitoring.

Last Updated: January 17, 2026

Overview

Event tracking allows you to monitor how customers use your product. Events are automatically batched (20 per batch) and sent to FirstDistro every 5 seconds or on page unload.

Prerequisites: Before tracking events, ensure you've completed the Getting Started guide - specifically installing the SDK (see Installation Guide) and enabling event tracking.

Basic tracking

Track an event

javascript
FirstDistro.track('event_name', {
  property1: 'value1',
  property2: 'value2'
});

Example events

javascript
// User actions
FirstDistro.track('user_signed_up', { plan: 'pro', source: 'website' });
FirstDistro.track('user_logged_in', { method: 'email' });
FirstDistro.track('user_logged_out');

// Feature usage
FirstDistro.track('feature_used', { 
  feature: 'export', 
  format: 'pdf',
  page_count: 10
});

FirstDistro.track('feature_used', { 
  feature: 'import', 
  file_type: 'csv',
  row_count: 1000
});

// Page views
FirstDistro.track('page_viewed', { 
  page: '/dashboard',
  referrer: '/login'
});

// Milestones
FirstDistro.track('milestone_reached', {
  milestone: '100_users',
  user_count: 100
});

FirstDistro.track('milestone_reached', {
  milestone: 'first_payment',
  amount: 99.00
});

Use setup() to set both user and account context in one call. This is the recommended approach as it handles the correct ordering automatically.

javascript
FirstDistro.setup({
  user: { 
    id: 'user-123', 
    name: 'John Doe', 
    email: 'john@example.com',
    role: 'admin'
  },
  account: { 
    id: 'account-456', 
    name: 'Acme Corp', 
    plan: 'growth',
    industry: 'SaaS'
  }
});

When to call:

  • After user logs in
  • When user/account information loads
  • When switching accounts (just pass the new account)

Benefits of setup():

  • One method instead of two
  • Impossible to get the ordering wrong
  • Cleaner, more readable code

Updating context:

javascript
// Update only user traits
FirstDistro.setup({
  user: { id: 'user-123', role: 'superadmin' }
});

// Switch to a different account
FirstDistro.setup({
  account: { id: 'new-account', name: 'New Team', plan: 'pro' }
});

User identification (legacy)

Note: We recommend using setup() instead. See Setting user and account context above.

For granular control, you can identify users separately:

javascript
FirstDistro.identify('user-123', {
  name: 'John Doe',
  email: 'john@example.com',
  role: 'admin',
  plan: 'pro'
});

⚠️ Important: If using legacy methods, always call group() BEFORE identify() to ensure the user is linked to the account context.

Account grouping (legacy)

Note: We recommend using setup() instead. See Setting user and account context above.

For granular control, you can group users into accounts separately:

javascript
FirstDistro.group('account-456', {
  name: 'Acme Corp',
  industry: 'SaaS',
  plan: 'growth',
  user_count: 25
});

⚠️ Important:

  • Always call group() FIRST, then identify()
  • Do NOT put user data (user_id, user_email, etc.) in group() traits

Account grouping required for customer insights

Important: Customer Insights requires account grouping to function. Without FirstDistro.group():

  • ✅ Events are stored in the database
  • ❌ Events are not processed for health scoring
  • ❌ Customers won't appear in Customer Insights dashboard
  • ❌ Health scores won't be calculated
  • ❌ Alerts won't be generated

With account grouping:

  • ✅ Events are stored with account_id
  • ✅ Events are aggregated into customer_accounts table
  • ✅ Health scores are calculated hourly
  • ✅ Customers appear in Customer Insights dashboard
  • ✅ Alerts are generated for at-risk customers

Setup: See the Getting Started guide for account grouping setup instructions.

Recommended pattern: setup()track()

javascript
// Set user and account context in one call
FirstDistro.setup({
  user: { id: user.id, name: user.name, email: user.email },
  account: { id: account.id, name: account.name, plan: account.plan }
});

// Track events (user_id and account_id auto-attached)
FirstDistro.track('feature_used', { feature: 'export' });
// This event will be processed for health scoring ✅

Legacy pattern (still supported): group()identify()track()

javascript
// Step 1: Group to the ACCOUNT FIRST (sets account context)
FirstDistro.group(account.id, {
  name: account.name,
  plan: account.plan
});

// Step 2: Identify the USER SECOND (now linked to account)
FirstDistro.identify(user.id, {
  name: user.name,
  email: user.email
});

// Step 3: Track events
FirstDistro.track('feature_used', { feature: 'export' });

Advanced patterns: For complex account grouping scenarios (multiple accounts, account switching), use setup() with the new account context when switching.

Handling logout

When a user logs out of your application, you must call FirstDistro.reset() to clear the current user session. This ensures that subsequent events are not incorrectly attributed to the previous user.

javascript
// Example: Call this in your logout handler
function handleLogout() {
  // Clear FirstDistro user context BEFORE signing out
  if (window.FirstDistro) {
    FirstDistro.reset();
  }
  
  // ... your existing logout logic ...
}

Why this matters:

  • The SDK persists user_id and account_id in localStorage
  • Without reset(), a new user logging in may inherit the previous user's context
  • This can cause events to be incorrectly attributed, polluting your analytics data

When to call reset():

  • User clicks "Log out" or "Sign out"
  • User session expires
  • Before switching between accounts (if your app supports multiple accounts)

Track these events for best Customer Insights:

1. authentication events

  • user_signed_up - New user registration
  • user_logged_in - User login
  • user_logged_out - User logout

2. feature usage events

  • feature_used - Any feature interaction
  • data_imported - Data import completed
  • data_exported - Data export completed
  • report_generated - Report created

3. milestone events

  • milestone_reached - Important milestones
  • first_feature_used - First feature interaction
  • milestone_100_users - User count milestones
  • milestone_1000_users - User count milestones

4. engagement events

  • page_viewed - Page navigation
  • session_started - Session begins
  • session_ended - Session ends

Event properties

Include relevant properties with each event:

javascript
FirstDistro.track('feature_used', {
  // Feature details
  feature: 'export',
  feature_category: 'data',
  
  // Usage context
  format: 'pdf',
  page_count: 10,
  file_size_kb: 250,
  
  // User context (auto-added if identified)
  user_id: 'user-123', // Auto-added
  account_id: 'account-456', // Auto-added after group() call
  
  // Custom properties
  source: 'dashboard',
  workflow: 'monthly_report'
});

Event batching

Events are automatically batched:

  • Batch size: 20 events per batch
  • Flush interval: 5 seconds
  • Unload flush: Events sent on page close using sendBeacon()

You don't need to manage batching - it's handled automatically!

Best practices

1. track key user actions

Focus on actions that indicate product value:

  • Feature usage
  • Data operations
  • User milestones
  • Engagement patterns

2. use consistent event names

Use snake_case and be descriptive:

  • user_signed_up
  • feature_used
  • milestone_reached
  • signup (too vague)
  • click (not descriptive)

3. include relevant properties

Add properties that provide context:

  • Feature names
  • User/account identifiers
  • Quantities (counts, sizes, etc.)
  • Timestamps (auto-added)

4. use setup() for context

Use setup() to set user and account context - it handles the ordering automatically:

javascript
// Set user and account context in one call
FirstDistro.setup({
  user: { id: user.id, name: user.name, email: user.email },
  account: { id: account.id, name: account.name, plan: account.plan }
});

// Track events (both user_id and account_id auto-attached)
FirstDistro.track('feature_used', { feature: 'export' });

Why use setup():

  • Handles correct ordering internally (account before user)
  • Both user_id and account_id are automatically included in tracked events
  • This ensures user data appears correctly in Customer Insights

Examples

React integration

Step 1: Add the script tag to your public/index.html:

html
<!-- Add before closing </body> tag -->
<script src="https://firstdistro.com/sdk/install/fd_your-token-here.js"></script>

Step 2: Use the SDK in your React components:

tsx
function App() {
  useEffect(() => {
    // SDK is automatically initialized after script loads
    if (user && account) {
      // Set user and account context in one call
      FirstDistro.setup({
        user: { id: user.id, name: user.name, email: user.email },
        account: { id: account.id, name: account.name, plan: account.plan }
      });
    }
  }, [user, account]);
  
  const handleFeatureUse = () => {
    FirstDistro.track('feature_used', {
      feature: 'export',
      format: 'pdf'
    });
  };
  
  return <button onClick={handleFeatureUse}>Export</button>;
}

Get your token: Dashboard → Settings → SDK Configuration

Next.js integration

tsx
// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Script
          src="https://firstdistro.com/sdk/install/fd_your-token-here.js"
          strategy="afterInteractive"
        />
        {children}
      </body>
    </html>
  );
}

Then use the SDK in your pages:

tsx
// app/page.tsx
'use client';

import { useEffect } from 'react';

export default function HomePage() {
  useEffect(() => {
    if (typeof window === 'undefined' || !window.FirstDistro) return;

    // SDK is automatically initialized
    if (user && account) {
      // Set user and account context in one call
      window.FirstDistro.setup({
        user: { id: user.id, name: user.name, email: user.email },
        account: { id: account.id, name: account.name, plan: account.plan }
      });
    }
    
    window.FirstDistro.track('page_viewed', { page: '/' });
  }, []);

  return <div>Welcome</div>;
}

See the Installation Guide for complete setup instructions.

Troubleshooting

Events not sending?

  • Check tracking is enabled in dashboard
  • Verify API key is correct
  • Check browser console for errors
  • Verify network requests in Network tab

Events delayed?

  • Events are batched (up to 5 seconds delay)
  • Events flush on page unload
  • This is normal behavior

Too many events?

  • Use sample rate in dashboard config
  • Filter events client-side before tracking
  • Batch related events together

Customers not appearing in Customer Insights?

  • Ensure you've called FirstDistro.setup() with account context
  • Verify events include account_id (check browser console)
  • See Getting Started guide for setup

"No users found" in Customer Detail Modal?

  • Ensure you've called FirstDistro.setup() with both user and account context
  • Verify user traits include name and email: setup({ user: { id, name, email }, account: { id, name } })
  • Check that events include user_id (check browser console)

For more help, email us at jide@firstdistro.com.