fullstory-saas
Industry-specific guide for implementing Fullstory in B2B SaaS applications. Covers feature adoption tracking, onboarding optimization, churn prediction signals, multi-tenant privacy considerations, role-based experiences, and enterprise customer requirements. Includes detailed examples for dashboards, workflows, settings, and collaboration features.
$ Instalar
git clone https://github.com/fullstorydev/fs-skills /tmp/fs-skills && cp -r /tmp/fs-skills/industry/fullstory-saas ~/.claude/skills/fs-skills// tip: Run this command in your terminal to install the skill
name: fullstory-saas version: v2 description: Industry-specific guide for implementing Fullstory in B2B SaaS applications. Covers feature adoption tracking, onboarding optimization, churn prediction signals, multi-tenant privacy considerations, role-based experiences, and enterprise customer requirements. Includes detailed examples for dashboards, workflows, settings, and collaboration features. related_skills:
- fullstory-privacy-controls
- fullstory-privacy-strategy
- fullstory-user-properties
- fullstory-analytics-events
- fullstory-page-properties
- fullstory-element-properties
Fullstory for B2B SaaS
โ ๏ธ LEGAL DISCLAIMER: This guidance is for educational purposes only and does not constitute legal, compliance, or regulatory advice. SaaS applications may be subject to various regulations (GDPR, CCPA, SOC 2, industry-specific requirements) depending on your customers and data processed. Always consult with your legal and compliance teams before implementing any data capture solution. Your organization is responsible for ensuring compliance with all applicable regulations and customer contracts.
Industry Overview
B2B SaaS applications have unique characteristics for session analytics:
- Feature adoption focus: Understanding which features users adopt (or don't)
- Onboarding optimization: Critical first-use experience
- Multi-tenant concerns: Data separation between customers
- Enterprise requirements: SOC 2, single-tenant options, data residency
- Complex user hierarchies: Organizations, teams, roles
- Workflow optimization: Multi-step processes, collaboration
Key Goals for SaaS Implementations
- Improve onboarding completion and time-to-value
- Understand feature adoption and usage patterns
- Identify friction points in core workflows
- Reduce churn by identifying at-risk signals
- Optimize pricing page and upgrade flows
- Support enterprise customer requirements
Recommended: Private by Default for Enterprise SaaS
For SaaS applications handling customer data, Private by Default mode is recommended:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SaaS: Consider Private by Default โ
โ โ
โ โข Multi-tenant = high data sensitivity โ
โ โข Customer data displayed in your UI โ mask by default โ
โ โข Selectively unmask your UI (buttons, nav, feature names) โ
โ โข Contact Fullstory Support to enable โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| SaaS Type | Private by Default? | Reason |
|---|---|---|
| CRM/Sales tools | โ ๏ธ Recommended | Displays customer contact info |
| Project management | โ ๏ธ Consider | User-generated content |
| Analytics dashboards | โ ๏ธ Consider | Customer business data |
| Developer tools | โ Highly recommended | Code, credentials, secrets |
| HR/Payroll | โ Required | Employee PII |
| Collaboration tools | โ ๏ธ Consider | Messages, documents |
Reference: Fullstory Private by Default
SaaS Privacy Considerations
What Can Typically Be Captured
| Data Type | Capture? | Notes |
|---|---|---|
| Feature usage | โ Yes | Core SaaS analytics |
| User role/permissions | โ Yes | For segmentation |
| Account/plan info | โ Yes | For segmentation |
| Product interactions | โ Yes | Core value |
| Workflow steps | โ Yes | Optimization |
| User names | โ ๏ธ Consider | Mask or use IDs |
| Email addresses | โ ๏ธ Consider | Hash or mask |
| User-generated content | โ ๏ธ Consider | Depends on product |
| Customer data in product | โ ๏ธ Careful | May need exclusion |
| File contents | โ Usually not | Customer's IP |
| API keys/tokens | โ Never | Security risk |
| Passwords | โ Never | Security risk |
Multi-Tenant Privacy
Important: In SaaS, you're capturing data about YOUR users,
but they may be working with THEIR customers' data.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ YOUR SaaS Application โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Customer A's Workspace (their data shown in your UI) โโ
โ โ - Their customer lists โโ
โ โ - Their financial data โโ
โ โ - Their proprietary content โโ
โ โ โ May need to exclude/mask depending on product โโ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Customer B's Workspace โโ
โ โ โ Same considerations โโ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Implementation Architecture
Privacy Zones for SaaS
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ B2B SAAS APPLICATION โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ VISIBLE (fs-unmask) โ
โ โข Navigation and menus โ
โ โข Feature names and labels โ
โ โข Action buttons โ
โ โข Empty states and onboarding UI โ
โ โข Error messages (generic) โ
โ โข Settings labels (not values) โ
โ โข Dashboard structure (not data) โ
โ โข Pricing and plans โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ MASKED (fs-mask) โ
โ โข User names โ
โ โข Email addresses (consider hash for ID) โ
โ โข Company names (unless your user) โ
โ โข User-generated titles/names โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ EXCLUDED (fs-exclude) โ
โ โข Customer's customer data (CRM contacts, etc.) โ
โ โข Customer's financial data โ
โ โข File contents and documents โ
โ โข API keys and tokens โ
โ โข Passwords and credentials โ
โ โข Proprietary/sensitive business data โ
โ โข Healthcare/financial SaaS: customer's regulated data โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
User Identification Pattern
// SaaS: Rich identification for product analytics
function onUserLogin(user, organization) {
FS('setIdentity', {
uid: user.id, // Your internal user ID
displayName: user.firstName // Just first name
});
FS('setProperties', {
type: 'user',
properties: {
// User context
user_role: user.role, // "admin", "member", "viewer"
user_created_at: user.createdAt,
user_title: user.jobTitle, // If available
is_account_owner: user.isOwner,
// Organization context (your customer)
org_id: organization.id,
org_name: organization.name, // Usually OK for your customer
org_industry: organization.industry,
org_size: getOrgSizeRange(organization.employeeCount),
org_plan: organization.plan, // "free", "pro", "enterprise"
org_created_at: organization.createdAt,
// Feature access
has_feature_x: organization.features.includes('feature_x'),
has_feature_y: organization.features.includes('feature_y'),
// Engagement signals
days_since_signup: daysSince(user.createdAt),
login_count_30d: user.loginCount30d,
last_active_feature: user.lastFeature,
// Lifecycle stage
is_trial: organization.isTrial,
trial_days_remaining: organization.trialDaysRemaining,
is_paying: organization.isPaying,
mrr: organization.mrr, // Your revenue from this customer
// Onboarding
onboarding_completed: user.onboardingCompleted,
onboarding_step: user.onboardingStep,
// Integrations (what they've connected)
has_slack_integration: organization.integrations.includes('slack'),
has_salesforce_integration: organization.integrations.includes('salesforce'),
// Joinable keys
crm_account_id: organization.salesforceAccountId,
support_org_id: organization.zendeskOrgId
}
});
}
AI/ML Feature Tracking
Modern SaaS applications increasingly use AI/ML features. Track these carefully:
What to Track for AI Features
| AI Feature Type | Track | Don't Track |
|---|---|---|
| AI assistants/copilots | Feature activated, suggestions accepted/rejected | Actual suggestions or prompts containing user data |
| Smart recommendations | Recommendation shown, clicked, dismissed | The recommended content itself (may contain customer data) |
| Automated workflows | Automation triggered, completed, failed | Customer data processed by automation |
| Predictive analytics | Feature viewed, exported | The predictions themselves |
| AI-generated content | Generation requested, accepted, edited | The generated content |
// Track AI feature usage without capturing customer data
FS('trackEvent', {
name: 'ai_feature_interaction',
properties: {
feature_name: 'smart_compose',
interaction_type: 'suggestion_accepted',
context: 'email_compose',
suggestion_count: 3,
accepted_index: 1,
response_time_ms: 850,
// NEVER: The actual suggestion text, user prompt, or generated content
}
});
// Track AI feature adoption over time
FS('trackEvent', {
name: 'ai_feature_first_use',
properties: {
feature_name: 'report_generator',
days_since_signup: 14,
user_role: 'analyst',
trial_or_paid: 'trial',
// Helps understand AI feature discovery patterns
}
});
AI Ethics Considerations
| Consideration | Guidance |
|---|---|
| Bias auditing | Track AI feature usage across segments to identify disparities |
| Transparency | Log when AI made a decision vs user |
| Opt-out tracking | Track users who disable AI features |
| Error rates | Track AI failures without capturing the data that caused them |
Page-Specific Implementations
Onboarding Flow
<!-- Onboarding - Critical to capture well -->
<div class="onboarding-flow">
<!-- Progress indicator - visible -->
<div class="onboarding-progress fs-unmask">
<div class="step active">1. Welcome</div>
<div class="step">2. Team</div>
<div class="step">3. Integration</div>
<div class="step">4. First Project</div>
</div>
<!-- Welcome step - visible -->
<section class="step-content step-welcome fs-unmask">
<h1>Welcome to ProductName!</h1>
<p>Let's get you set up in just a few minutes.</p>
<!-- Role selection - visible (product decision) -->
<div class="role-selection">
<h2>What best describes your role?</h2>
<button data-role="engineer">Engineer</button>
<button data-role="designer">Designer</button>
<button data-role="pm">Product Manager</button>
<button data-role="other">Other</button>
</div>
</section>
<!-- Team invite step - mix -->
<section class="step-content step-team">
<h1 class="fs-unmask">Invite Your Team</h1>
<p class="fs-unmask">ProductName works better with your team.</p>
<!-- Email input - mask emails -->
<div class="invite-form fs-mask">
<input type="email" placeholder="teammate@company.com" />
<button>Add</button>
</div>
<!-- Invited list - mask emails -->
<div class="invited-list fs-mask">
<div class="invite">
<span class="email">john@company.com</span>
<button>Remove</button>
</div>
</div>
<!-- Actions - visible -->
<div class="step-actions fs-unmask">
<button>Skip for now</button>
<button>Continue</button>
</div>
</section>
<!-- Integration step - visible (shows what they connect) -->
<section class="step-content step-integrations fs-unmask">
<h1>Connect Your Tools</h1>
<p>Connect the tools you already use.</p>
<div class="integration-options">
<button class="integration" data-integration="slack">
<img src="slack-icon.svg" alt="" />
Connect Slack
</button>
<button class="integration" data-integration="github">
<img src="github-icon.svg" alt="" />
Connect GitHub
</button>
<button class="integration" data-integration="jira">
<img src="jira-icon.svg" alt="" />
Connect Jira
</button>
</div>
</section>
</div>
// Comprehensive onboarding tracking
function trackOnboardingStep(step, data) {
FS('trackEvent', {
name: 'onboarding_step_completed',
properties: {
step_number: step,
step_name: data.stepName,
time_on_step_seconds: data.timeOnStep,
// Step-specific data
...getStepSpecificData(step, data)
}
});
}
function getStepSpecificData(step, data) {
switch(step) {
case 'welcome':
return {
selected_role: data.role,
selected_use_case: data.useCase
};
case 'team':
return {
teammates_invited: data.inviteCount,
skipped: data.skipped
};
case 'integrations':
return {
integrations_connected: data.connectedIntegrations,
skipped: data.skipped
};
default:
return {};
}
}
// Track onboarding completion
FS('trackEvent', {
name: 'onboarding_completed',
properties: {
total_time_minutes: totalOnboardingTime,
steps_skipped: skippedSteps,
teammates_invited: inviteCount,
integrations_connected: connectedIntegrations,
first_project_created: hasFirstProject
}
});
// Track onboarding abandonment
window.addEventListener('beforeunload', () => {
if (!onboardingCompleted && currentOnboardingStep) {
FS('trackEvent', {
name: 'onboarding_abandoned',
properties: {
abandoned_at_step: currentOnboardingStep,
time_in_onboarding_minutes: getOnboardingTime(),
steps_completed: completedSteps
}
});
}
});
Main Dashboard
<!-- SaaS Dashboard -->
<div class="dashboard">
<!-- Navigation - visible -->
<nav class="main-nav fs-unmask">
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/analytics">Analytics</a>
<a href="/team">Team</a>
<a href="/settings">Settings</a>
</nav>
<!-- Dashboard header - mix -->
<header class="dashboard-header">
<h1 class="fs-unmask">Dashboard</h1>
<!-- Workspace selector - mask customer data -->
<div class="workspace-selector fs-mask">
<button>Acme Corp โผ</button>
<div class="workspace-menu">
<a href="#">Acme Corp</a>
<a href="#">Widgets Inc</a>
</div>
</div>
</header>
<!-- Metrics cards - depends on product -->
<div class="metrics-grid fs-unmask">
<!-- These are YOUR metrics about their usage - usually OK -->
<div class="metric-card"
data-fs-element="Metric Card"
data-fs-properties-schema='{"metric_name":"string","metric_type":"string"}'>
<h3>Active Users</h3>
<span class="value">127</span>
<span class="trend">+12%</span>
</div>
<div class="metric-card">
<h3>Projects</h3>
<span class="value">24</span>
</div>
<div class="metric-card">
<h3>API Calls</h3>
<span class="value">1.2M</span>
</div>
</div>
<!-- Activity feed - may contain customer data -->
<div class="activity-feed">
<h2 class="fs-unmask">Recent Activity</h2>
<!-- Activity items - mask names, possibly exclude details -->
<div class="activity-list fs-mask">
<!-- User names and content may need masking -->
<div class="activity-item">
<span class="user">Sarah J.</span>
<span class="action">created a new project</span>
<span class="target">"Q4 Marketing Campaign"</span>
<span class="time">2 hours ago</span>
</div>
</div>
</div>
<!-- Quick actions - visible -->
<div class="quick-actions fs-unmask">
<button>New Project</button>
<button>Invite Team</button>
<button>View Reports</button>
</div>
</div>
// Dashboard page properties
FS('setProperties', {
type: 'page',
properties: {
page_type: 'dashboard',
// Usage metrics (your analytics about them)
active_users_count: metrics.activeUsers,
project_count: metrics.projects,
// Feature usage indicators
has_active_project: metrics.hasActiveProject,
has_recent_activity: metrics.hasRecentActivity,
// Dashboard customization
widgets_visible: getVisibleWidgets(),
date_range_selected: dateRange
}
});
Feature Usage Tracking (Core SaaS Value)
<!-- Feature Page (e.g., Report Builder) -->
<div class="report-builder">
<!-- Feature header - visible -->
<header class="feature-header fs-unmask">
<h1>Report Builder</h1>
<div class="actions">
<button>Save</button>
<button>Export</button>
<button>Share</button>
</div>
</header>
<!-- Tool palette - visible (what tools they use) -->
<aside class="tool-palette fs-unmask"
data-fs-element="Tool Palette">
<h2>Add Chart</h2>
<button data-chart="bar">Bar Chart</button>
<button data-chart="line">Line Chart</button>
<button data-chart="pie">Pie Chart</button>
<button data-chart="table">Table</button>
<button data-chart="metric">Single Metric</button>
</aside>
<!-- Canvas - may contain customer data -->
<main class="report-canvas">
<!-- The actual data in the report may be customer's data -->
<div class="chart-container fs-mask">
<!-- Chart showing customer's data -->
</div>
</main>
<!-- Configuration panel - mix -->
<aside class="config-panel">
<h2 class="fs-unmask">Chart Settings</h2>
<!-- Data source selection - may reveal customer's data structure -->
<div class="data-source fs-mask">
<label>Data Source</label>
<select>
<option>Sales Pipeline</option>
<option>Customer Database</option>
</select>
</div>
<!-- Chart configuration - visible (product behavior) -->
<div class="chart-config fs-unmask">
<label>Chart Type</label>
<select>
<option>Bar</option>
<option>Line</option>
</select>
</div>
</aside>
</div>
// Feature usage tracking
FS('setProperties', {
type: 'page',
properties: {
page_type: 'report_builder',
feature_name: 'report_builder',
// Feature state
report_id: report.id,
is_new_report: report.isNew,
chart_count: report.charts.length,
// Feature engagement
editing_mode: 'visual', // or "code"
has_unsaved_changes: report.isDirty
}
});
// Track feature interactions
function onChartAdded(chartType) {
FS('trackEvent', {
name: 'feature_used',
properties: {
feature: 'report_builder',
action: 'chart_added',
chart_type: chartType,
total_charts_after: report.charts.length
}
});
}
function onReportSaved(report) {
FS('trackEvent', {
name: 'feature_used',
properties: {
feature: 'report_builder',
action: 'report_saved',
chart_count: report.charts.length,
is_first_report: user.reportCount === 1,
time_to_create_minutes: report.creationTime
}
});
}
// Track feature discovery
function onFeatureFirstUse(featureName) {
FS('trackEvent', {
name: 'feature_first_use',
properties: {
feature_name: featureName,
days_since_signup: user.daysSinceSignup,
discovery_method: getDiscoveryMethod() // "navigation", "search", "help"
}
});
}
Settings and Configuration
<!-- Settings Page -->
<div class="settings-page">
<!-- Settings nav - visible -->
<nav class="settings-nav fs-unmask">
<a href="#profile">Profile</a>
<a href="#organization">Organization</a>
<a href="#billing">Billing</a>
<a href="#integrations">Integrations</a>
<a href="#security">Security</a>
<a href="#api">API</a>
</nav>
<!-- Profile settings - mask PII -->
<section id="profile" class="settings-section">
<h2 class="fs-unmask">Profile Settings</h2>
<div class="profile-form fs-mask">
<div class="form-group">
<label>Full Name</label>
<input type="text" value="John Smith" />
</div>
<div class="form-group">
<label>Email</label>
<input type="email" value="john@acme.com" />
</div>
<div class="form-group">
<label>Job Title</label>
<input type="text" value="Product Manager" />
</div>
</div>
<!-- Action button - visible -->
<button class="fs-unmask">Save Changes</button>
</section>
<!-- Organization settings - mix -->
<section id="organization" class="settings-section">
<h2 class="fs-unmask">Organization Settings</h2>
<div class="org-form">
<div class="form-group fs-mask">
<label>Organization Name</label>
<input type="text" value="Acme Corporation" />
</div>
<!-- Plan info - visible (your product data) -->
<div class="plan-info fs-unmask">
<label>Current Plan</label>
<span class="plan-badge">Professional</span>
<a href="/upgrade">Upgrade</a>
</div>
</div>
</section>
<!-- API settings - EXCLUDE sensitive -->
<section id="api" class="settings-section">
<h2 class="fs-unmask">API Access</h2>
<!-- API keys - EXCLUDE -->
<div class="api-keys fs-exclude">
<div class="key-row">
<label>API Key</label>
<code>sk_live_abc123xyz789...</code>
<button>Regenerate</button>
</div>
<div class="key-row">
<label>Webhook Secret</label>
<code>whsec_...</code>
<button>Regenerate</button>
</div>
</div>
<!-- API usage stats - visible -->
<div class="api-usage fs-unmask">
<h3>API Usage</h3>
<p>This month: 45,000 / 100,000 requests</p>
</div>
</section>
</div>
// Settings page tracking
FS('setProperties', {
type: 'page',
properties: {
page_type: 'settings',
settings_section: currentSection // "profile", "billing", "api"
}
});
// Track settings changes
function onSettingsChanged(section, changes) {
FS('trackEvent', {
name: 'settings_changed',
properties: {
section: section,
fields_changed: Object.keys(changes),
// Don't include actual values (could be PII)
}
});
}
Billing and Upgrade
<!-- Upgrade / Pricing Page - Important for conversion -->
<div class="pricing-page">
<!-- Pricing header - visible -->
<header class="pricing-header fs-unmask">
<h1>Choose Your Plan</h1>
<p>Scale as you grow</p>
<!-- Billing toggle - visible -->
<div class="billing-toggle">
<button class="active">Monthly</button>
<button>Annual (Save 20%)</button>
</div>
</header>
<!-- Plan cards - visible (your pricing) -->
<div class="plan-cards fs-unmask"
data-fs-element="Pricing Card"
data-fs-properties-schema='{"plan_name":"string","plan_price":"real","billing_cycle":"string"}'>
<div class="plan-card" data-plan="starter">
<h2>Starter</h2>
<p class="price">$29<span>/month</span></p>
<ul class="features">
<li>5 team members</li>
<li>10 projects</li>
<li>Basic analytics</li>
</ul>
<button>Choose Starter</button>
</div>
<div class="plan-card featured" data-plan="professional">
<span class="badge">Most Popular</span>
<h2>Professional</h2>
<p class="price">$99<span>/month</span></p>
<ul class="features">
<li>Unlimited team members</li>
<li>Unlimited projects</li>
<li>Advanced analytics</li>
<li>Priority support</li>
</ul>
<button>Choose Professional</button>
</div>
<div class="plan-card" data-plan="enterprise">
<h2>Enterprise</h2>
<p class="price">Custom</p>
<ul class="features">
<li>Everything in Professional</li>
<li>SSO & SAML</li>
<li>Dedicated support</li>
<li>Custom contracts</li>
</ul>
<button>Contact Sales</button>
</div>
</div>
<!-- FAQ - visible -->
<div class="pricing-faq fs-unmask">
<h2>Frequently Asked Questions</h2>
<!-- FAQ items -->
</div>
</div>
// Pricing page tracking
FS('setProperties', {
type: 'page',
properties: {
page_type: 'pricing',
current_plan: organization.plan,
is_trial: organization.isTrial,
trial_days_remaining: organization.trialDaysRemaining,
billing_cycle_viewed: selectedBillingCycle
}
});
// Track plan interest
function onPlanHover(plan) {
FS('trackEvent', {
name: 'pricing_plan_interest',
properties: {
plan_name: plan,
interaction_type: 'hover',
current_plan: organization.plan
}
});
}
function onPlanSelected(plan) {
FS('trackEvent', {
name: 'pricing_plan_selected',
properties: {
plan_name: plan,
billing_cycle: selectedBillingCycle,
current_plan: organization.plan,
is_upgrade: isPlanUpgrade(organization.plan, plan)
}
});
}
// Track upgrade completion
function onUpgradeComplete(upgrade) {
FS('trackEvent', {
name: 'subscription_upgraded',
properties: {
from_plan: upgrade.previousPlan,
to_plan: upgrade.newPlan,
billing_cycle: upgrade.billingCycle,
mrr_change: upgrade.mrrChange,
upgrade_trigger: upgrade.trigger // "trial_end", "feature_limit", "self_serve"
}
});
}
Collaboration Features
<!-- Comments / Collaboration - Common in SaaS -->
<div class="collaboration-panel">
<h3 class="fs-unmask">Comments</h3>
<!-- Comment thread - mask user content -->
<div class="comment-thread fs-mask">
<div class="comment">
<span class="author">Sarah J.</span>
<span class="time">2h ago</span>
<p class="content">Can we adjust the timeline for Q4?</p>
<div class="replies">
<div class="comment reply">
<span class="author">Mike T.</span>
<span class="time">1h ago</span>
<p class="content">Sure, I'll update it now.</p>
</div>
</div>
</div>
</div>
<!-- Comment input - mask -->
<div class="comment-input fs-mask">
<textarea placeholder="Add a comment..."></textarea>
<button>Post</button>
</div>
<!-- Comment actions - visible -->
<div class="comment-actions fs-unmask">
<button>Resolve All</button>
<button>Filter</button>
</div>
</div>
// Collaboration tracking
FS('trackEvent', {
name: 'collaboration_action',
properties: {
action_type: 'comment_added',
context: 'project',
context_id: project.id,
is_reply: isReply,
mentions_count: mentionsCount
// Never: comment content
}
});
Churn Prediction Signals
Track signals that may indicate churn risk:
// User engagement signals
FS('setProperties', {
type: 'user',
properties: {
// Activity metrics
login_frequency: getLoginFrequency(), // "daily", "weekly", "monthly", "rare"
days_since_last_login: user.daysSinceLastLogin,
// Feature engagement
features_used_30d: user.featuresUsed30d.length,
core_feature_usage: getCoreFeatureUsage(), // "high", "medium", "low"
// Team engagement
team_size: organization.teamSize,
active_team_members: organization.activeMembers,
// Value realization
projects_created: user.projectCount,
reports_exported: user.exportCount,
integrations_active: organization.activeIntegrations.length,
// Support/friction
support_tickets_30d: organization.ticketCount30d,
errors_encountered_30d: user.errorCount30d
}
});
// Track engagement drop signals
function trackEngagementDrop(signal) {
FS('trackEvent', {
name: 'engagement_signal',
properties: {
signal_type: signal.type, // "login_frequency_drop", "feature_usage_drop"
severity: signal.severity, // "warning", "critical"
days_since_baseline: signal.daysSince
}
});
}
Enterprise Considerations
For Enterprise Customers
// Enterprise-specific tracking
if (organization.plan === 'enterprise') {
FS('setProperties', {
type: 'user',
properties: {
// Enterprise features
sso_enabled: organization.ssoEnabled,
scim_enabled: organization.scimEnabled,
audit_log_enabled: organization.auditLogEnabled,
// Compliance
data_residency_region: organization.dataRegion,
// Scale metrics
seat_count: organization.seatCount,
workspace_count: organization.workspaceCount
}
});
}
Respecting Enterprise Privacy Requirements
Some enterprise customers may require special handling:
// Check for customer-specific privacy requirements
function initializeFullstory(organization) {
// Some enterprises may opt out of session replay
if (organization.settings.disableSessionReplay) {
// Don't initialize Fullstory at all
return;
}
// Some may have stricter masking requirements
if (organization.settings.strictPrivacyMode) {
// Additional element exclusions
document.body.classList.add('fs-mask');
}
// Initialize with org-specific settings
FS('setIdentity', {
uid: user.id,
consent: organization.settings.analyticsConsent
});
}
Common SaaS Patterns
Feature Adoption Tracking
// Track feature discovery and adoption
const FEATURE_LIST = [
'report_builder',
'integrations',
'team_collaboration',
'api_access',
'custom_dashboards',
'automation'
];
function trackFeatureAdoption(user) {
const adopted = FEATURE_LIST.filter(f => user.hasUsed(f));
const notAdopted = FEATURE_LIST.filter(f => !user.hasUsed(f));
FS('setProperties', {
type: 'user',
properties: {
features_adopted_count: adopted.length,
features_adopted: adopted.join(','),
features_not_adopted: notAdopted.join(','),
adoption_rate: adopted.length / FEATURE_LIST.length
}
});
}
Organization Size Ranges
function getOrgSizeRange(employeeCount) {
if (employeeCount <= 10) return '1-10';
if (employeeCount <= 50) return '11-50';
if (employeeCount <= 200) return '51-200';
if (employeeCount <= 1000) return '201-1000';
return '1000+';
}
KEY TAKEAWAYS FOR AGENT
When helping SaaS clients with Fullstory:
- Feature adoption is core: Track which features users discover and use
- Onboarding is critical: Comprehensive tracking of first-use experience
- Churn signals matter: Track engagement patterns that predict churn
- Multi-tenant privacy: Customer's customer data may need exclusion
- Enterprise requirements: Be prepared for stricter privacy requirements
- API keys/credentials: Always exclude
Questions to Ask SaaS Clients
- "What are your core activation metrics?"
- "How do you define a 'healthy' user engagement pattern?"
- "Does your product display your customer's customer data?"
- "Do you have enterprise customers with special privacy requirements?"
- "What features are most important to track for conversion?"
Key Events to Track in SaaS
- Onboarding completion
- Feature first use
- Feature repeated use
- Upgrade/downgrade
- Team member invited
- Integration connected
- Export/share actions
- Settings changes
- Error encounters
REFERENCE LINKS
- Fullstory for Product Teams: https://www.fullstory.com/product-analytics/
- Element Properties: ../core/fullstory-element-properties/SKILL.md
- Analytics Events: ../core/fullstory-analytics-events/SKILL.md
- User Properties: ../core/fullstory-user-properties/SKILL.md
This skill document is specific to B2B SaaS implementations. Adjust based on your specific product's data sensitivity requirements.
Repository
