How Pendo Works
Pendo operates through a JavaScript agent (the "Pendo Agent") that loads asynchronously into the host application. The agent performs three functions: behavioral data collection, in-app guide rendering, and feedback widget injection.
Data collection works through auto-capture. The agent attaches event listeners at the document level and records:
- Page/route events: URL changes detected via
pushState/replaceStateinterception andpopstatelisteners - Click events: Every
clickevent on the page, resolved to a CSS selector path and the element's computed properties (text, href, id, class, data attributes) - Feature events: Clicks on elements that match tagged feature definitions (CSS selectors configured in the Pendo UI)
The agent does not record DOM snapshots or session replays by default (session replay is a separate add-on module). Instead, it captures structured event data: which features were used, on which pages, by which visitors, at what time.
Visitor and Account model: Pendo organizes data around two entities:
- Visitor: An individual user, identified by a unique
visitorId - Account: A company or organization, identified by an
accountId
Every event is attributed to a visitor-account pair. This B2B-oriented data model enables account-level analytics (feature adoption by company, NPS by account tier, etc.).
Guide rendering: Pendo injects guide HTML/CSS into the host application's DOM. Guides are fetched from Pendo's CDN based on targeting rules evaluated client-side (visitor metadata, account metadata, URL, feature usage history, segment membership). Guide types include tooltips (anchored to a CSS selector), lightboxes (modal overlays), banners, and multi-step walkthroughs.
Data transmits to https://app.pendo.io/data/ via XHR POST. The agent compresses events into batched payloads sent every 5 minutes or on page unload.
Installing the Tracking Script
The Pendo agent snippet goes inside <head> or at the top of your application shell. Replace API_KEY with your Pendo subscription API key from Settings > Subscription Settings:
<script>
(function(apiKey){
(function(p,e,n,d,o){var v,w,x,y,z;o=p[d]=p[d]||{};o._q=o._q||[];
v=['initialize','identify','updateOptions','pageLoad','track'];for(w=0,x=v.length;w<x;++w)(function(m){
o[m]=o[m]||function(){o._q[m===v[0]?'unshift':'push']([m].concat([].slice.call(arguments,0)));};
})(v[w]);
y=e.createElement(n);y.async=!0;y.src='https://cdn.pendo.io/agent/static/'+apiKey+'/pendo.js';
z=e.getElementsByTagName(n)[0];z.parentNode.insertBefore(y,z);
})(window,document,'script','pendo');
})('API_KEY');
</script>
After the snippet loads, initialize with visitor and account metadata:
pendo.initialize({
visitor: {
id: 'USER_123',
email: 'jane@example.com',
role: 'admin',
plan: 'enterprise',
creationDate: '2024-03-15T00:00:00Z'
},
account: {
id: 'ACCT_456',
name: 'Acme Corp',
planLevel: 'enterprise',
industry: 'SaaS',
employeeCount: 250,
arr: 120000
}
});
For single-page applications, call pendo.initialize() once on app load. Pendo detects route changes automatically. If you need to force a page load event after a route change:
// After route transition in React/Vue/Angular
pendo.pageLoad();
For npm installations:
npm install @pendo/agent
import pendo from '@pendo/agent';
pendo.initialize({
visitor: { id: currentUser.id, email: currentUser.email },
account: { id: currentUser.accountId }
});
Content Security Policy:
script-src: https://cdn.pendo.io https://app.pendo.io
connect-src: https://app.pendo.io
img-src: https://cdn.pendo.io https://app.pendo.io https://pendo-static-*.storage.googleapis.com
style-src: 'unsafe-inline'
frame-src: https://app.pendo.io
Event Tracking and Data Collection
Auto-Captured Events
Pendo auto-captures two event types without any code:
Page events: Recorded on every URL change. Properties include the full URL, page title, and load time. In SPAs, triggered by pushState/replaceState.
Feature events: Recorded when a visitor clicks on an element matching a tagged feature's CSS selector. Features are defined in the Pendo UI using the visual tagging tool (point-and-click in your live application to select elements and assign feature names).
Custom Track Events
Send business logic events that do not correspond to page views or clickable features:
pendo.track('Upgrade Initiated', {
currentPlan: 'starter',
targetPlan: 'enterprise',
source: 'settings_page'
});
pendo.track('Report Exported', {
format: 'pdf',
pageCount: 24,
duration_ms: 3200
});
Track event properties can be strings, numbers, or booleans. Pendo does not support nested objects in track properties.
Feature Tagging
Feature tags are CSS selector rules defined in the Pendo UI. The agent evaluates click events against all active feature tag selectors. When a match occurs, the click is recorded as a feature event with the tag name.
For reliable tagging in applications with dynamic CSS class names, add stable identifiers:
<button data-pendo="upgrade-button" class="css-dynamic-hash">
Upgrade Plan
</button>
Configure the feature tag in Pendo to target [data-pendo="upgrade-button"].
Updating Visitor/Account Metadata
Update metadata at any point during the session:
pendo.updateOptions({
visitor: {
id: 'USER_123',
plan: 'enterprise',
lastLoginDate: new Date().toISOString()
},
account: {
id: 'ACCT_456',
arr: 150000
}
});
Metadata updates apply to all subsequent events in the session and update the visitor/account record in Pendo.
Identity and User Tracking
Visitor Identification
The visitor.id passed to pendo.initialize() is the primary identity key. Pendo creates or updates a visitor record keyed by this ID. All sessions, page events, feature events, guide interactions, and NPS responses are attributed to this visitor.
For anonymous visitors (pre-login), omit the visitor.id or pass an empty string. Pendo generates an anonymous ID stored in a cookie. After login, calling pendo.initialize() with the real visitor.id merges the anonymous session data.
// Before login (anonymous)
pendo.initialize({
visitor: { id: '' },
account: { id: '' }
});
// After login
pendo.initialize({
visitor: { id: 'USER_123', email: 'jane@example.com' },
account: { id: 'ACCT_456', name: 'Acme Corp' }
});
Account-Level Analytics
Every visitor belongs to an account. Pendo aggregates metrics at the account level:
- Feature adoption by account (which accounts use which features)
- NPS by account tier
- Guide completion rates by account segment
Pass account.id consistently across all visitors in the same organization to enable account-level roll-ups.
Multi-Application Tracking
For organizations with multiple products, Pendo supports multiple subscription keys. Each application gets its own Pendo agent instance with a separate API key. Cross-application visitor matching is done by visitor.id if the same ID is used across applications.
API and Data Export
Aggregation API
Query visitor, account, feature, and page metrics:
# Get feature usage for a specific feature
curl -X GET \
-H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
-H "Content-Type: application/json" \
"https://app.pendo.io/api/v1/feature/FEATURE_ID"
Data Explorer API (Aggregation Queries)
Run aggregation queries against Pendo data:
curl -X POST \
-H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
-H "Content-Type: application/json" \
-d '{
"response": {
"mimeType": "application/json"
},
"request": {
"pipeline": [
{
"source": {
"featureEvents": {
"featureId": "FEATURE_ID"
}
}
},
{
"identified": "visitorId"
},
{
"dateRange": {
"first": "2025-10-01",
"last": "2025-11-01"
}
},
{
"group": {
"field": "visitorId"
}
},
{
"count": {}
}
]
}
}' \
"https://app.pendo.io/api/v1/aggregation"
This returns the number of times each visitor used the specified feature in the date range.
Visitor and Account APIs
# Get visitor details
curl -H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
"https://app.pendo.io/api/v1/visitor/USER_123"
# List accounts with metadata
curl -H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
"https://app.pendo.io/api/v1/account?limit=50&offset=0"
# Update account metadata via API
curl -X PUT \
-H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
-H "Content-Type: application/json" \
-d '{"metadata": {"auto": {"arr": 200000}}}' \
"https://app.pendo.io/api/v1/account/ACCT_456"
Guide API
# List all guides
curl -H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
"https://app.pendo.io/api/v1/guide"
# Get guide analytics (views, completions, dismissals)
curl -H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
"https://app.pendo.io/api/v1/guide/GUIDE_ID/stats"
Webhook Events
Pendo can push events to external endpoints:
{
"event": "guideActivity",
"data": {
"guideId": "GUIDE_ID",
"visitorId": "USER_123",
"accountId": "ACCT_456",
"type": "guideSeen",
"stepId": "step_1",
"timestamp": 1700000000000
}
}
Common Issues
Feature tags not capturing clicks: The CSS selector in the feature tag does not match the rendered DOM element. Inspect the element in browser DevTools and compare the selector. Dynamic class names (CSS-in-JS) change on each build, breaking selector-based tags. Use data-pendo attributes for stable targeting.
pendo.initialize() called but no data appears: Verify the API key matches your subscription. Check the browser console for Pendo-related errors. Confirm that visitor.id is a non-empty string; passing undefined or null causes silent failures.
Guides not displaying: Guide targeting rules are evaluated client-side. Verify that the current visitor's metadata matches the guide's segment targeting. Check that the guide is published (not draft) and that the CSS selector for the anchor element exists in the current DOM. Open the Pendo Debugger (pendo.debugging.enableDebugMode()) to see why a guide was not shown.
Metadata not updating in reports: Pendo processes metadata asynchronously. Updates made via pendo.updateOptions() or the API may take up to 1 hour to reflect in reports and segments. Real-time guide targeting uses the most recent metadata immediately.
Duplicate visitors: If pendo.initialize() is called with different visitor.id values in the same session (e.g., identity changes without a page reload), Pendo creates separate visitor records. Call pendo.clearSession() before re-initializing with a different identity.
Agent size and load performance: The Pendo agent is approximately 100KB compressed. It loads asynchronously and does not block page rendering. Guide assets (images, custom CSS) are loaded on-demand only when a guide is triggered. For performance-sensitive pages, defer pendo.initialize() until after critical content loads.
Platform-Specific Considerations
Retroactive Analytics
Pendo's retroactive analytics capability works at the feature and page level. When you tag a new feature, Pendo applies the tag definition to all historical click data that matches the selector. This means you can define features after launch and immediately see adoption metrics going back to when the agent was first installed.
Retroactive analysis does not apply to custom pendo.track() events. Track events only produce data from the moment the code is deployed.
Guide Architecture
Guides inject HTML/CSS directly into the host application's DOM. They operate in the same CSS context as your application, which means:
- Your application's CSS can affect guide styling (use specific Pendo CSS selectors to override)
- Guides can be affected by z-index stacking contexts
- Shadow DOM boundaries block guide rendering; Pendo cannot anchor tooltips inside Shadow DOM elements
Guide targeting evaluates on every page load and URL change. The agent checks: Does the current URL match? Does the visitor meet the segment criteria? Has the visitor already seen this guide (frequency rules)? If all conditions pass, the guide renders.
NPS Survey Implementation
Pendo NPS surveys are a specialized guide type. Configure in the Pendo UI with:
- Targeting: which visitors/accounts see the survey
- Frequency: how often the same visitor can be surveyed (e.g., once every 90 days)
- Timing: delay after page load, trigger on specific URL, or trigger via API
Trigger an NPS survey programmatically:
pendo.showGuideById('NPS_GUIDE_ID');
NPS responses are stored in Pendo and accessible via the API:
curl -H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
"https://app.pendo.io/api/v1/guide/NPS_GUIDE_ID/polls"
Data Residency and Privacy
Pendo offers US and EU data center options. Configure data residency during onboarding. The agent respects Do Not Track headers if enabled in Pendo settings. For GDPR compliance, use pendo.stopSendingEvents() to halt data collection and pendo.startSendingEvents() to resume after consent:
// Check consent status
if (!hasUserConsent()) {
pendo.stopSendingEvents();
}
// After consent granted
pendo.startSendingEvents();
To delete a visitor's data for right-to-erasure requests, use the Visitor Deletion API:
curl -X DELETE \
-H "X-Pendo-Integration-Key: YOUR_INTEGRATION_KEY" \
"https://app.pendo.io/api/v1/visitor/USER_123"