How FullStory Works
FullStory captures user sessions by recording a full DOM snapshot on page load, then tracking every subsequent DOM mutation, network request, console event, and user interaction as a structured event stream. The recording agent does not produce video; it serializes the DOM tree and replays mutations against it to reconstruct the visual experience.
The FullStory agent (fs.js) loads asynchronously and instruments the page with:
- MutationObserver on the document root to capture all DOM changes (node insertions, removals, attribute modifications, text content changes)
- Event listeners for mouse events, touch events, scroll, resize, keyboard input, and focus/blur
- XMLHttpRequest and Fetch API monkey-patches to log network request/response metadata (URL, method, status, timing; bodies are not captured by default)
- Console interception to record
console.log,console.warn,console.error, and unhandled exceptions
Each recorded session is assigned a unique session URL (app.fullstory.com/ui/{org}/session/{session_id}). Sessions are indexed by FullStory's backend for full-text search across page content, CSS selectors, and captured event properties.
FullStory uses a proprietary binary wire format to transmit data to rs.fullstory.com. Data is compressed client-side before transmission. The agent maintains a local buffer and flushes events periodically or on page unload via sendBeacon.
Installing the Tracking Script
Add the FullStory snippet inside <head>. Replace YOUR_ORG_ID with the organization ID from FullStory Settings > General:
<script>
window['_fs_host'] = 'fullstory.com';
window['_fs_script'] = 'edge.fullstory.com/s/fs.js';
window['_fs_org'] = 'YOUR_ORG_ID';
window['_fs_namespace'] = 'FS';
(function(m,n,e,t,l,o,g,y){
if (e in m) {if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');} return;}
g=m[e]=function(a,b,s){g.q?g.q.push([a,b,s]):g._api(a,b,s);};g.q=[];
o=n.createElement(t);o.async=1;o.crossOrigin='anonymous';o.src='https://'+_fs_script;
y=n.getElementsByTagName(t)[0];y.parentNode.insertBefore(o,y);
g.identify=function(i,v,s){g(l,{uid:i},s);if(v)g(l,v,s)};g.setUserVars=function(v,s){g(l,v,s)};g.event=function(i,v,s){g('event',{n:i,p:v},s)};
g.anonymize=function(){g.identify(!!0)};
g.shutdown=function(){g("rec",!1)};g.restart=function(){g("rec",!0)};
g.log = function(a,b){g("log",[a,b])};
g.consent=function(a){g("consent",!arguments.length||a)};
g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)};
g.clearUserCookie=function(){};
g.setVars=function(n,p){g('setVars',[n,p]);};
g._hierarchyRecordingEnabled=true;
g._hierarchyDefaultRegion='main';
})(window,document,window['_fs_namespace'],'script','user');
</script>
For npm/module-based installations:
npm install @fullstory/browser
import { FullStory, init } from '@fullstory/browser';
init({ orgId: 'YOUR_ORG_ID' });
Single-page applications are handled automatically. FullStory detects pushState/replaceState calls and logs virtual page transitions. No manual intervention is required for React Router, Vue Router, or Angular Router.
Content Security Policy requirements:
script-src: https://edge.fullstory.com https://fullstory.com
connect-src: https://rs.fullstory.com
img-src: https://fullstory.com
Event Tracking and Data Collection
Custom Events
Track business-specific events with FS.event(). Events accept a name and a properties object:
FS.event('Order Completed', {
orderId_str: 'ORD-98765',
revenue_real: 149.99,
itemCount_int: 3,
couponApplied_bool: true
});
Property naming uses type suffixes to enforce schema:
_strfor strings_realfor floats_intfor integers_boolfor booleans_datefor ISO 8601 date strings
Without suffixes, FullStory infers the type, but explicit suffixes prevent schema conflicts.
// Track feature usage
FS.event('Feature Used', {
featureName_str: 'bulk_export',
duration_int: 4500,
success_bool: true
});
// Track errors
FS.event('Checkout Error', {
errorCode_str: 'PAYMENT_DECLINED',
paymentMethod_str: 'credit_card',
amount_real: 89.99
});
Page Properties
Set properties on the current page for segmentation:
FS.setVars('page', {
pageName_str: 'Pricing',
experiment_str: 'pricing_v2',
category_str: 'marketing'
});
Identity and User Tracking
User Identification
Call FS.identify() after authentication to associate sessions with a known user:
FS.identify('user_12345', {
displayName: 'Jane Smith',
email: 'jane@example.com',
pricingPlan_str: 'enterprise',
accountAge_int: 365,
isAdmin_bool: true
});
The first argument is your internal user ID (string). The second argument is a properties object. displayName and email are reserved keys used by FullStory's UI. All other properties use type suffixes.
Account-Level Identification
For B2B applications, associate users with accounts:
FS.identify('user_12345', {
displayName: 'Jane Smith',
email: 'jane@example.com'
});
FS.setVars('account', {
id_str: 'acct_789',
name_str: 'Acme Corp',
plan_str: 'enterprise',
seats_int: 50
});
Anonymization
Clear user identity and start a new anonymous session:
FS.anonymize();
Consent Management
Defer recording until user consent is obtained:
// On page load, FullStory buffers but does not transmit
FS.consent(false);
// After user grants consent
FS.consent(true);
// Revoke consent and stop recording
FS.shutdown();
// Re-enable recording
FS.restart();
API and Data Export
Data Export API
FullStory provides a REST API for exporting raw event data. Authenticate with a server-side API key from Settings > Integrations > API Keys.
List sessions for a user:
curl -H "Authorization: Basic YOUR_API_KEY" \
"https://api.fullstory.com/v2/sessions?uid=user_12345"
Export events (bulk):
curl -X POST \
-H "Authorization: Basic YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"start": "2025-11-01T00:00:00Z",
"end": "2025-11-02T00:00:00Z",
"type": "event"
}' \
"https://api.fullstory.com/v2/data/export"
The export endpoint returns a download URL for a gzipped JSON Lines file containing all events in the time range.
Get session replay URL:
curl -H "Authorization: Basic YOUR_API_KEY" \
"https://api.fullstory.com/v2/sessions/5678901234:12345"
Response:
{
"sessionId": "5678901234:12345",
"fsUrl": "https://app.fullstory.com/ui/YOUR_ORG/session/5678901234:12345",
"createdTime": "2025-11-15T10:30:00Z",
"userId": "user_12345"
}
Webhooks
Configure webhooks in Settings > Integrations > Webhooks to receive real-time notifications:
{
"event": "session.completed",
"data": {
"sessionId": "5678901234:12345",
"userId": "user_12345",
"duration": 342,
"pagesVisited": 7,
"errorsCount": 1,
"frustratedEvents": 3
},
"timestamp": "2025-11-15T10:36:00Z"
}
Supported webhook events: session.completed, note.created, segment.matched.
Segment Integration
FullStory works as both a Segment source and destination:
// As a Segment destination, identify calls are forwarded to FullStory
analytics.identify('user_12345', {
name: 'Jane Smith',
email: 'jane@example.com',
plan: 'enterprise'
});
// Track calls become FS.event()
analytics.track('Feature Used', {
featureName: 'bulk_export'
});
Common Issues
Session replay shows a blank white page: FullStory could not capture the initial DOM snapshot. This occurs when the snippet loads after the DOM is already fully rendered and the initial snapshot is missed. Ensure the FullStory snippet is in <head> before any deferred content rendering. Also verify that CSP headers allow connections to rs.fullstory.com.
Custom events not appearing in search: Event property names with incorrect type suffixes cause silent schema mismatches. A property sent as revenue_str: "149.99" and later as revenue_real: 149.99 creates two separate properties. Audit your event payloads for consistent suffix usage.
User identity not linking sessions: FS.identify() must be called on every page load or SPA navigation where the user is authenticated. If called only on the login page, subsequent sessions on other pages remain anonymous. Place the identify call in your app's authentication state handler.
Privacy selectors not masking content: FullStory processes exclusion classes during the DOM snapshot. If content is dynamically inserted after the snapshot, the exclusion class must be present on the element before it enters the DOM. Apply fs-exclude classes in your component rendering logic, not via post-render DOM manipulation.
High network overhead: FullStory transmits more data on pages with frequent DOM mutations (real-time dashboards, chat interfaces, animation-heavy pages). Use FS.shutdown() on pages where recording is unnecessary, and FS.restart() when the user navigates to a relevant section.
Data Export API returns 429: The export API enforces rate limits of approximately 10 requests per minute per organization. Implement exponential backoff in your export scripts.
Platform-Specific Considerations
Privacy Selectors
FullStory provides CSS class-based privacy controls that are evaluated during DOM serialization:
<!-- Exclude element entirely from recording (element is invisible in replay) -->
<div class="fs-exclude">
<p>This content will not appear in session replay</p>
</div>
<!-- Mask text content (shows placeholder text but preserves layout) -->
<div class="fs-mask">
<p>This text is replaced with masked characters</p>
</div>
<!-- Block element recording (replaced with a gray placeholder box) -->
<div class="fs-block">
<img src="sensitive-document.png" />
</div>
For React components, apply classes at the component level:
function SensitivePanel({ children }) {
return <div className="fs-exclude">{children}</div>;
}
Rage Click and Frustration Detection
FullStory automatically detects three frustration signal types:
- Rage clicks: 3+ rapid clicks on the same element within 700ms
- Dead clicks: Clicks on elements that produce no DOM change, navigation, or network request within 1 second
- Error clicks: Clicks that immediately precede a JavaScript error
These signals are indexed and searchable via Omnisearch. Filter sessions by frustration score to prioritize high-impact UX issues.
Recording Sampling
FullStory records all sessions by default, subject to your plan's session quota. To control sampling programmatically:
// Record only 10% of anonymous users
if (Math.random() > 0.1 && !isAuthenticated) {
FS.shutdown();
}
For authenticated users in B2B applications, recording all sessions is typically preferred since session volumes are lower and individual user behavior is more valuable.
Data Retention
FullStory retains session data based on your plan tier (typically 3-13 months). The Data Export API allows you to extract events into your own data warehouse (Snowflake, BigQuery, Redshift) for long-term analysis beyond FullStory's retention window.