> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zixflow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SaaS Product Analytics

> Tracking patterns for B2B/B2C SaaS — trials, features, teams, and billing.

Imagine you're running a B2B SaaS product — think a project management or analytics tool where companies sign up, invite their team, and pay by subscription. This guide walks through tracking both individual user actions and account-level activity: from signup and activation through feature usage, team collaboration, plan upgrades, and early churn signals.

***

## The SaaS Event Model

### Two-Level Tracking: User + Account

**Unlike B2C**, SaaS requires tracking both:

1. **Individual users** (seats, roles, activity)
2. **Accounts/Organizations** (subscriptions, usage limits, billing)

**Implementation:**

```javascript theme={null}
// Track at user level
track('Feature Used', {
  feature_name: 'custom_reports',
  user_role: 'admin',
  account_id: 'acct_123',  // Link to account
  workspace_id: 'ws_456'
});

// Track at account level (server-side)
track('Account Upgraded', {
  account_id: 'acct_123',
  from_plan: 'professional',
  to_plan: 'enterprise',
  seat_count: 25,
  mrr_change: 500,
  contract_value: 6000  // Annual contract
});
```

**Profile attributes:**

```javascript theme={null}
// User-level
setProfileAttributes({
  account_id: 'acct_123',
  role: 'admin',
  department: 'marketing',
  invited_by: 'user_owner_789',
  activated: true,
  activation_date: '2026-05-01T10:00:00Z'
});

// Account-level (stored separately)
setAccountAttributes('acct_123', {
  plan_type: 'enterprise',
  mrr: 1200,
  seat_count: 25,
  seats_occupied: 18,
  trial_ends_at: null,  // No longer on trial
  billing_status: 'active',
  created_at: '2025-01-15T10:00:00Z',
  industry: 'E-commerce',
  company_size: '100-500'
});
```

***

## Activation Events (PLG Focus)

### Signup Completed

**When:** User creates account (self-serve signup)

```javascript theme={null}
// Client-side
identify({
  userId: 'user_12345',
  traits: {
    email: 'john@acme.com',
    name: 'John Smith',
    company: 'Acme Corp',
    role: 'Marketing Manager',
    signup_source: 'organic_search',
    signup_flow: 'free_trial',  // vs 'demo_request', 'sales_led'
    signup_page: '/pricing'
  }
});

track('Signup Completed', {
  account_id: 'acct_123',  // Auto-generated or from invite link
  is_account_creator: true,
  trial_plan: 'professional',
  trial_duration_days: 14,
  signup_time_seconds: 45,  // Time from form start to completion
  utm_source: 'google',
  utm_campaign: 'spring_2026_search'
});
```

**Trigger:** Welcome email + onboarding sequence

***

### Activation Milestone Events

**Why activation matters:** Activated users = 3x higher retention, 5x higher conversion to paid.

**Define your activation criteria** (example for project management tool):

* Created first project
* Invited team member
* Created first task
* Assigned task to someone
* Completed at least one task

**Track each milestone:**

```javascript theme={null}
// Milestone 1: First project created
track('First Project Created', {
  project_name: 'Q2 Marketing Campaign',
  project_type: 'marketing',
  days_since_signup: 0,  // Happened immediately
  time_to_milestone_minutes: 5
});

setProfileAttributes({
  activation_step: 1,
  activation_milestone_1_completed: true,
  activation_milestone_1_at: '2026-05-04T10:05:00Z'
});

// Milestone 2: First team member invited
track('First Team Member Invited', {
  invitee_email: 'sarah@acme.com',
  invitee_role: 'member',
  days_since_signup: 0,
  time_to_milestone_minutes: 12,
  invite_method: 'email'
});

setProfileAttributes({
  activation_step: 2,
  activation_milestone_2_completed: true,
  activation_milestone_2_at: '2026-05-04T10:12:00Z'
});

// ... continue for all milestones

// Final activation
track('User Activated', {
  days_to_activation: 0,  // Activated same day as signup
  time_to_activation_minutes: 45,
  activation_path: 'power_user',  // vs 'slow_adopter', 'invited_user'
  milestones_completed: 5
});

setProfileAttributes({
  activated: true,
  activation_date: '2026-05-04T10:45:00Z',
  activation_cohort: '2026-05-W18'  // Week 18 of 2026
});
```

**Analytics: Activation funnel**

<Accordion title="Analyst query example">
  ```sql theme={null}
  SELECT 
    COUNT(DISTINCT CASE WHEN event_name = 'Signup Completed' THEN user_id END) AS signups,
    COUNT(DISTINCT CASE WHEN event_name = 'First Project Created' THEN user_id END) AS milestone_1,
    COUNT(DISTINCT CASE WHEN event_name = 'First Team Member Invited' THEN user_id END) AS milestone_2,
    COUNT(DISTINCT CASE WHEN event_name = 'First Task Created' THEN user_id END) AS milestone_3,
    COUNT(DISTINCT CASE WHEN event_name = 'User Activated' THEN user_id END) AS activated
  FROM profile_events
  WHERE workspace_id = 'ws_123'
  AND timestamp >= DATE_TRUNC('month', NOW());
  ```
</Accordion>

***

## Feature Usage Events

### Core Feature Used

**When:** User engages with key product feature

```javascript theme={null}
track('Report Generated', {
  report_type: 'custom',  // vs 'template', 'scheduled'
  report_name: 'Monthly Sales Performance',
  data_range: 'last_30_days',
  filters_applied: ['region: US', 'product_category: Electronics'],
  visualization_types: ['line_chart', 'bar_chart', 'data_table'],
  export_format: 'pdf',  // vs 'csv', 'excel', null (viewed only)
  generation_time_seconds: 2.3,
  row_count: 15420,
  is_first_use: false
});
```

**Use cases:**

* **Usage-based pricing:** Track API calls, reports generated, emails sent
* **Feature adoption:** % of users who've used Feature X
* **Power user identification:** Users who generate 10+ reports/month

***

### Premium Feature Gated

**When:** Free/trial user hits paywall

```javascript theme={null}
track('Premium Feature Gated', {
  feature_name: 'advanced_analytics',
  user_plan: 'free',
  required_plan: 'professional',
  gate_type: 'soft',  // Showed upgrade CTA vs 'hard' (feature disabled)
  interaction: 'clicked_upgrade',  // vs 'dismissed', 'closed'
  days_since_signup: 7,
  usage_this_month: 145  // Free tier allows 100
});
```

**Trigger:** Upgrade prompt email or in-app message

***

### Collaboration Events

**Team collaboration = higher retention + account expansion**

#### Team Member Invited

```javascript theme={null}
track('Team Member Invited', {
  invitee_email: 'sarah@acme.com',
  invitee_role: 'member',  // vs 'admin', 'viewer'
  invited_by: 'user_12345',
  invite_method: 'email',  // vs 'link', 'bulk_csv'
  account_seat_count: 5,
  seats_occupied_before: 2,
  seats_occupied_after: 3
});
```

***

#### Team Member Joined

```javascript theme={null}
// When invitee accepts invitation
identify({
  userId: 'user_67890',
  traits: {
    email: 'sarah@acme.com',
    account_id: 'acct_123',
    role: 'member',
    invited_by: 'user_12345',
    joined_via: 'email_invite'
  }
});

track('Team Member Joined', {
  account_id: 'acct_123',
  user_role: 'member',
  invited_by: 'user_12345',
  days_to_accept: 1,  // Time from invite to acceptance
  seats_occupied: 3,
  seat_limit: 5
});
```

***

#### Mentioned User

```javascript theme={null}
track('User Mentioned', {
  mentioned_user_id: 'user_67890',
  mention_context: 'task_comment',  // vs 'document', 'chat'
  entity_id: 'task_456',
  entity_type: 'task'
});
```

**Trigger:** Notification to mentioned user

***

#### Shared Content

```javascript theme={null}
track('Content Shared', {
  content_type: 'dashboard',
  content_id: 'dash_789',
  shared_with: ['user_67890', 'user_11111'],
  permission_level: 'view',  // vs 'edit', 'admin'
  share_method: 'internal',  // vs 'public_link', 'email'
});
```

**Analytics:** Collaboration index = (mentions + shares + comments) / user count

***

## Usage-Based Pricing Events

### Quota Tracked

**For usage-based billing (API calls, storage, seats, etc.)**

```javascript theme={null}
// Server-side event (triggered by API gateway)
track('API Call Made', {
  account_id: 'acct_123',
  endpoint: '/api/v1/analytics/reports',
  method: 'POST',
  user_id: 'user_12345',
  response_time_ms: 145,
  status_code: 200,
  quota_consumed: 1,  // 1 API call
  quota_remaining: 4850,  // 150 calls left this month
  quota_limit: 5000,
  billing_cycle: '2026-05'
});
```

***

### Quota Warning Triggered

**When:** User approaching usage limit

```javascript theme={null}
// Server-side (quota monitoring service)
if (accountUsage.apiCalls >= accountUsage.quota * 0.8) {
  track('Quota Warning Triggered', {
    account_id: 'acct_123',
    quota_type: 'api_calls',
    quota_used: 4000,
    quota_limit: 5000,
    quota_percentage: 80,
    warning_level: '80_percent',  // vs '90_percent', '100_percent'
    billing_cycle: '2026-05',
    days_left_in_cycle: 10
  });
  
  // Trigger email to account admin
}
```

**Trigger:** Email to admins + in-app banner

***

### Quota Exceeded

**When:** User hits hard limit

```javascript theme={null}
track('Quota Exceeded', {
  account_id: 'acct_123',
  quota_type: 'api_calls',
  quota_limit: 5000,
  attempted_action: 'generate_report',
  days_left_in_cycle: 8,
  overage_by: 1,
  current_plan: 'professional',
  upgrade_offer_shown: true
});
```

**Trigger:** Upgrade prompt + sales notification (high-value account)

***

## Account Expansion Events

### Seat Added

**When:** Account increases seat count

```javascript theme={null}
track('Seat Added', {
  account_id: 'acct_123',
  seats_before: 5,
  seats_after: 10,
  added_by: 'user_12345',  // Account admin
  reason: 'new_hires',  // vs 'seasonal_growth', 'expansion'
  mrr_change: 200,  // $40/seat/month * 5 seats
  prorated_charge: 150  // Mid-cycle addition
});
```

***

### Plan Upgraded

**When:** Account moves to higher tier

```javascript theme={null}
track('Plan Upgraded', {
  account_id: 'acct_123',
  from_plan: 'professional',
  to_plan: 'enterprise',
  upgrade_trigger: 'quota_exceeded',  // vs 'sales_call', 'self_serve'
  seat_count: 10,
  mrr_before: 400,
  mrr_after: 1200,
  contract_length_months: 12,
  contract_value: 14400,
  discount_applied: 10,  // 10% annual discount
  upgraded_by: 'user_12345',
  sales_rep: 'rep_sarah_jones'  // If sales-assisted
});

// Update account attributes
setAccountAttributes('acct_123', {
  plan_type: 'enterprise',
  mrr: 1200,
  contract_value: 14400,
  billing_cycle: 'annual',
  upgraded_at: '2026-05-04T10:00:00Z'
});
```

**Trigger:** Thank-you email + account manager introduction (enterprise)

***

### Feature Tier Unlocked

**When:** Upgrade unlocks new features

```javascript theme={null}
track('Feature Tier Unlocked', {
  account_id: 'acct_123',
  plan: 'enterprise',
  features_unlocked: [
    'sso_authentication',
    'custom_reports',
    'api_access',
    'dedicated_support'
  ],
  previous_limitations: {
    'reports_per_month': 50,
    'api_calls_per_month': 5000
  },
  new_limits: {
    'reports_per_month': 'unlimited',
    'api_calls_per_month': 50000
  }
});
```

**Trigger:** Feature announcement email + onboarding guide

***

## Churn Signals

### Negative Engagement Events

**Track behaviors indicating churn risk:**

#### Usage Dropped

```javascript theme={null}
// Server-side (daily cron job)
const lastWeekActivity = getActivityCount(accountId, 'last_7_days');
const previousWeekActivity = getActivityCount(accountId, '8_to_14_days_ago');

if (lastWeekActivity < previousWeekActivity * 0.5) {
  track('Usage Dropped Significantly', {
    account_id: accountId,
    last_week_activity: lastWeekActivity,
    previous_week_activity: previousWeekActivity,
    drop_percentage: 60,
    primary_users_inactive: ['user_12345', 'user_67890'],
    plan_type: 'professional',
    mrr: 400
  });
}
```

**Trigger:** Outreach email from customer success

***

#### Cancellation Flow Started

```javascript theme={null}
track('Cancellation Flow Started', {
  account_id: 'acct_123',
  initiated_by: 'user_12345',
  user_role: 'admin',
  plan_type: 'professional',
  mrr: 400,
  account_age_days: 180,
  last_active_days_ago: 14,
  retention_offer_shown: true,
  retention_offer_type: '20_percent_discount'
});
```

**Trigger:** Save workflow (discount offer, feature unlock, account review call)

***

#### Cancellation Reason Selected

```javascript theme={null}
track('Cancellation Reason Selected', {
  account_id: 'acct_123',
  reason: 'too_expensive',  // vs 'missing_features', 'switching_to_competitor', 'no_longer_needed'
  reason_details: 'Budget cuts this quarter',
  competitor_name: null,  // Or 'Competitor X' if switching
  requested_export: true,
  requested_refund: false,
  feedback_provided: true
});
```

**Analytics:** Top churn reasons by plan type, MRR cohort

***

#### Account Downgraded

```javascript theme={null}
track('Plan Downgraded', {
  account_id: 'acct_123',
  from_plan: 'professional',
  to_plan: 'starter',
  downgrade_reason: 'budget_constraints',
  seat_count_before: 10,
  seat_count_after: 5,
  mrr_before: 400,
  mrr_after: 150,
  mrr_change: -250,
  downgraded_by: 'user_12345',
  features_lost: ['custom_reports', 'api_access']
});
```

**Trigger:** Win-back campaign (show ROI of lost features)

***

## Analytics Queries

### Activation Rate by Signup Source

<Accordion title="Analyst query example">
  ```sql theme={null}
  SELECT 
    properties->>'signup_source' AS source,
    COUNT(DISTINCT CASE WHEN event_name = 'Signup Completed' THEN user_id END) AS signups,
    COUNT(DISTINCT CASE WHEN event_name = 'User Activated' THEN user_id END) AS activated,
    ROUND(100.0 * COUNT(DISTINCT CASE WHEN event_name = 'User Activated' THEN user_id END) /
          COUNT(DISTINCT CASE WHEN event_name = 'Signup Completed' THEN user_id END), 2) AS activation_rate
  FROM profile_events
  WHERE workspace_id = 'ws_123'
  AND timestamp >= DATE_TRUNC('month', NOW())
  GROUP BY source
  ORDER BY activation_rate DESC;
  ```
</Accordion>

***

### Feature Adoption by Plan Type

<Accordion title="Analyst query example">
  ```sql theme={null}
  SELECT 
    u.traits->>'plan_type' AS plan,
    COUNT(DISTINCT e.user_id) AS total_users,
    COUNT(DISTINCT CASE WHEN e.event_name = 'Report Generated' THEN e.user_id END) AS used_reports,
    COUNT(DISTINCT CASE WHEN e.event_name = 'API Call Made' THEN e.user_id END) AS used_api,
    COUNT(DISTINCT CASE WHEN e.event_name = 'Dashboard Created' THEN e.user_id END) AS used_dashboards,
    ROUND(100.0 * COUNT(DISTINCT CASE WHEN e.event_name = 'Report Generated' THEN e.user_id END) / COUNT(DISTINCT e.user_id), 2) AS reports_adoption_pct
  FROM profiles u
  LEFT JOIN profile_events e ON u.user_id = e.user_id
  WHERE u.workspace_id = 'ws_123'
  AND e.timestamp >= NOW() - INTERVAL 30 DAY
  GROUP BY plan
  ORDER BY plan;
  ```
</Accordion>

***

### Revenue Expansion by Cohort

<Accordion title="Analyst query example">
  ```sql theme={null}
  WITH cohorts AS (
    SELECT 
      user_id,
      DATE_TRUNC('month', traits->>'created_at'::TIMESTAMP) AS cohort_month
    FROM profiles
    WHERE workspace_id = 'ws_123'
  ),
  upgrades AS (
    SELECT 
      user_id,
      (properties->>'mrr_change')::DECIMAL AS mrr_change,
      timestamp
    FROM profile_events
    WHERE event_name = 'Plan Upgraded'
    AND workspace_id = 'ws_123'
  )
  SELECT 
    c.cohort_month,
    COUNT(DISTINCT c.user_id) AS cohort_size,
    SUM(COALESCE(u.mrr_change, 0)) AS total_expansion_mrr,
    AVG(COALESCE(u.mrr_change, 0)) AS avg_expansion_per_user
  FROM cohorts c
  LEFT JOIN upgrades u ON c.user_id = u.user_id
  GROUP BY c.cohort_month
  ORDER BY c.cohort_month DESC;
  ```
</Accordion>

***

## Implementation Checklist

### Phase 1: Signup & Activation (Week 1)

* [ ] Signup Completed event
* [ ] Define activation milestones (5-7 steps)
* [ ] Track each milestone event
* [ ] User Activated event
* [ ] Onboarding email sequence
* [ ] Profile attributes: activated, activation\_date, activation\_cohort

### Phase 2: Feature Usage (Week 2)

* [ ] Top 5 feature usage events
* [ ] Premium Feature Gated events
* [ ] Collaboration events (invited, joined, mentioned)
* [ ] Usage-based quota tracking
* [ ] Quota warning/exceeded triggers

### Phase 3: Expansion & Retention (Week 3)

* [ ] Plan Upgraded/Downgraded events
* [ ] Seat Added event
* [ ] Usage Dropped detection (cron job)
* [ ] Cancellation Flow Started
* [ ] At-risk account segment + outreach campaign

### Phase 4: Analytics & Optimization (Week 4)

* [ ] Activation rate dashboard (by source, plan)
* [ ] Feature adoption analysis
* [ ] Cohort retention + expansion
* [ ] Churn reason analysis
* [ ] Expansion-ready account identification

***

**Next:** Learn about communication channels and messaging → [Communication Channels](/documentation/events/channels-overview)
