Tracking Snippet
The Agent Analytics tracking snippet is a lightweight, privacy-first JavaScript library that automatically captures pageviews, engagement metrics, and custom events. At under 2KB minified, it requires zero dependencies and works seamlessly with modern frameworks and single-page applications.
Installation
Add the tracking snippet to your website by including the script tag in your HTML. The snippet can be installed in various ways depending on your tech stack.
Basic HTML Installation
Add this script tag to your HTML, ideally in the <head> section or just before </body>:
<script
async
defer
data-site-id="your-site-id-here"
src="https://api.statscontext.com/tracker.js"
></script>The async or defer attribute ensures the tracking script loads without blocking page rendering. The data-site-id attribute is required and must match the site ID from your Agent Analytics dashboard.
React / Next.js Installation
For React applications, use the Next.js Script component to load the tracker with optimal performance:
import Script from "next/script";export default function RootLayout({ children }) { return ( <html lang="en"> <body> {children} <Script src="https://api.statscontext.com/tracker.js" data-site-id="your-site-id-here" strategy="afterInteractive" /> </body> </html> );}The strategy="afterInteractive" setting loads the tracker after the page becomes interactive, ensuring optimal performance without delaying user interactions.
WordPress Installation
For WordPress sites, add the tracking snippet to your theme's footer or use a plugin like "Insert Headers and Footers":
<!-- Add before </body> tag -->
<script
async
defer
data-site-id="your-site-id-here"
src="https://api.statscontext.com/tracker.js"
></script>
<?php wp_footer(); ?>Static HTML Sites
For static HTML websites, add the script tag before the closing </body> tag on every page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Website</title>
</head>
<body>
<!-- Your content -->
<script
async
defer
data-site-id="your-site-id-here"
src="https://api.statscontext.com/tracker.js"
></script>
</body>
</html>Getting Your Site ID
To find your unique site ID:
- Log in to your Agent Analytics dashboard
- Navigate to the Sites page
- Copy the site ID for your domain
- Replace
your-site-id-herein the tracking snippet
How It Works
The tracking snippet follows a simple initialization and event flow designed for maximum reliability and minimal performance impact.
Initialization Flow
When the tracking script loads, it automatically performs these steps:
- Extract Site ID: Reads the
data-site-idattribute from the script tag - Check Do Not Track: Respects browser DNT settings by checking
navigator.doNotTrack - Configure API Endpoint: Derives the API base URL from the script source
- Initialize State: Sets up tracking state including page view ID and heartbeat timer
- Register Event Listeners: Hooks into browser navigation and visibility events
- Track Initial Pageview: Automatically sends the first pageview event
If the site ID is missing or Do Not Track is enabled, the tracker exits silently without any network activity or errors. This ensures tracking never breaks your website.
Automatic Pageview Tracking
The tracker automatically captures pageviews on initial page load without requiring any manual code. Each pageview captures:
- URL: Full URL from
window.location.href - Referrer: Previous page URL from
document.referrer - Page Title: Document title from
document.title - UTM Parameters: Campaign tracking parameters (source, medium, campaign, term, content)
The server responds with a page_view_id that the tracker uses for subsequent heartbeat events and SPA navigation tracking.
Heartbeat Mechanism
After each pageview, the tracker starts a heartbeat timer that sends engagement data every 5 seconds while the page is active:
{
"type": "heartbeat",
"site_id": "your-site-id",
"page_view_id": "pv_abc123",
"duration_ms": 15000
}Heartbeats serve multiple purposes:
- Accurate Time Tracking: Measures actual time spent on page, not just page load
- Engagement Metrics: Distinguishes between quick bounces and engaged visits
- Session Duration: Updates session duration in real-time
The heartbeat timer stops on page navigation or when the tab becomes hidden, and a final heartbeat is sent on visibilitychange events.
SPA Navigation Support
The tracker automatically detects single-page application (SPA) navigation by intercepting:
- history.pushState(): Programmatic navigation (e.g., React Router)
- history.replaceState(): URL replacement without navigation
- popstate event: Browser back/forward button clicks
When a navigation is detected, the tracker automatically:
- Sends a final heartbeat for the current page
- Stops the current heartbeat timer
- Sends a navigation event linking to the previous page
- Starts a new heartbeat timer for the new page
This ensures accurate page tracking in React, Vue, Angular, and other SPA frameworks without requiring manual tracking code.
Custom Events
Beyond automatic pageview tracking, the tracker exposes a global API for tracking custom application events through window.aa.track().
API Reference
window.aa.track( name: string, // Event name (required, max 255 chars) properties?: Record<string, any> // Optional metadata object): voidBasic Usage
Track a simple event without properties:
// Track newsletter signupwindow.aa.track('newsletter_signup');// Track downloadwindow.aa.track('whitepaper_downloaded');Events with Properties
Add contextual data to events using the properties parameter:
// Track button click with contextwindow.aa.track('button_clicked', { button_id: 'hero_cta', button_text: 'Get Started', page: '/pricing'});// Track feature usagewindow.aa.track('feature_used', { feature: 'export_data', format: 'csv', record_count: 150});E-commerce Tracking Example
Track shopping and checkout events with product details:
// Add to cartwindow.aa.track('add_to_cart', { product_id: 'prod_123', product_name: 'Pro Plan', price: 29.99, quantity: 1, currency: 'USD'});// Purchase completionwindow.aa.track('purchase', { order_id: 'order_456', total: 59.98, items: 2, payment_method: 'stripe', currency: 'USD'});// Checkout stepwindow.aa.track('checkout_step', { step: 'payment_info', cart_total: 59.98});Form Tracking Example
Track form interactions to understand conversion funnels:
// Track form start (first field interaction)document.querySelector('#contact-form').addEventListener('focus', () => { window.aa.track('form_started', { form_id: 'contact', form_type: 'sales_inquiry' });}, { once: true });// Track form submissiondocument.querySelector('#contact-form').addEventListener('submit', (e) => { window.aa.track('form_submitted', { form_id: 'contact', form_type: 'sales_inquiry', field_count: 5 });});// Track form errorsfunction validateForm() { const errors = getFormErrors(); if (errors.length > 0) { window.aa.track('form_error', { form_id: 'contact', error_count: errors.length, error_fields: errors.map(e => e.field).join(',') }); }}Best Practices
Event Naming
Use consistent, descriptive event names across your application:
// Good: Consistent naming with properties for variantswindow.aa.track('cta_clicked', { location: 'hero' });window.aa.track('cta_clicked', { location: 'footer' });// Bad: Inconsistent naming makes analysis difficultwindow.aa.track('hero_cta_click');window.aa.track('clicked_footer_cta');Meaningful Properties
Include context that helps you analyze and segment events:
// Good: Rich context for analysiswindow.aa.track('video_played', { video_id: 'intro_2024', duration: 120, autoplay: false, position: 'hero'});// Bad: Missing contextwindow.aa.track('video_played');Privacy Considerations
Never track personally identifiable information (PII):
// Bad: Contains PIIwindow.aa.track('form_submitted', { email: 'user@example.com', phone: '555-1234', name: 'John Doe'});// Good: No PIIwindow.aa.track('form_submitted', { form_type: 'contact', field_count: 5, has_message: true});Property Guidelines
- Use simple, serializable values: Strings, numbers, booleans work best
- Keep property names consistent: Use snake_case or camelCase throughout
- Avoid large objects: Keep properties under 1KB total
- Don't include sensitive data: No passwords, tokens, credit cards, or PII
- Avoid non-serializable values: No functions, undefined, or circular references
Checking Availability
The window.aa object is available immediately after the tracking script loads. To ensure it's available before calling:
// Option 1: Check before callingif (typeof window.aa !== 'undefined') { window.aa.track('event_name');}// Option 2: Wrap in try-catchtry { window.aa.track('event_name', { prop: 'value' });} catch (e) { console.warn('Analytics not loaded');}// Option 3: Wait for DOMContentLoadeddocument.addEventListener('DOMContentLoaded', () => { if (window.aa) { window.aa.track('page_interactive'); }});Configuration
Required Attributes
The tracking script requires one attribute:
- data-site-id: Your unique site identifier from the Agent Analytics dashboard (required)
Optional Attributes
For optimal performance, consider these attributes:
- async: Load the script asynchronously without blocking page rendering
- defer: Load the script after the document has been parsed
Use either async or defer, but not both. For analytics, async is typically preferred.
API URL Derivation
The tracker automatically determines the API endpoint from the script source URL:
// If script src is: https://api.statscontext.com/tracker.js// API endpoint becomes: https://api.statscontext.com/api/eventvar script = document.currentScript;var apiBase = script.src.replace(/\/tracker\.js.*$/, "");// Result: https://api.statscontext.comThis means you don't need to configure the API endpoint separately - it's automatically derived from the script URL.
SPA Support
The tracker includes built-in support for single-page applications (SPAs) without requiring any additional configuration.
How It Works
The tracker patches the browser's History API to detect navigation:
// Intercept pushState (used by React Router, etc.)var origPushState = history.pushState;history.pushState = function() { origPushState.apply(this, arguments); detectNavigationAndTrack();};// Intercept replaceStatevar origReplaceState = history.replaceState;history.replaceState = function() { origReplaceState.apply(this, arguments); detectNavigationAndTrack();};// Listen for back/forward button clickswindow.addEventListener("popstate", detectNavigationAndTrack);Supported Frameworks
The SPA support works automatically with:
- React Router: Both v5 and v6
- Next.js: App Router and Pages Router
- Vue Router: All versions
- Angular Router: All versions
- Any framework using History API: Automatic detection
Navigation Event Flow
When a user navigates in an SPA:
- User clicks a link or uses browser back/forward
- Framework updates the URL using
pushStateorreplaceState - Tracker detects the URL change
- Tracker sends final heartbeat for previous page
- Tracker sends navigation event with
prev_page_view_id - Server marks previous page as non-bounce
- Server creates new page view and continues session
- Tracker starts new heartbeat timer
Session Continuity
SPA navigation maintains session continuity by:
- Linking pages together via
prev_page_view_id - Continuing the same session (no new session created)
- Incrementing the session's page count
- Updating the session's exit page
- Correctly calculating bounce rates (navigation marks previous page as non-bounce)
Privacy
Agent Analytics is built privacy-first from the ground up. The tracking system respects user privacy while providing actionable analytics.
No Cookies
The tracker does not use cookies, which means:
- No cookie consent banners required
- No client-side data storage
- Automatic compliance with cookie laws
- Works in browsers with third-party cookies blocked
No Local Storage
The tracker does not use localStorage or any other client-side storage:
- No persistent tracking identifiers
- No cross-session fingerprinting
- Works in private browsing mode
- Respects browser privacy settings
Daily-Rotating Hashes
Visitor identification uses daily-rotating hashes to prevent long-term tracking:
visitor_hash = SHA-256(
daily_salt + ":" +
site_id + ":" +
ip_address + ":" +
user_agent
)How daily rotation works:
- New salt each day: A unique salt is generated for each UTC day
- Same-day continuity: Same visitor gets same hash within a day
- Cross-day privacy: Hash changes daily, preventing multi-day tracking
- Site isolation: Each site gets different hashes for the same visitor
- One-way hashing: Cannot reverse hash to get IP or user agent
Do Not Track Compliance
The tracker fully respects the Do Not Track (DNT) browser setting:
// Check DNT on initializationvar dnt = navigator.doNotTrack === "1" || window.doNotTrack === "1";if (dnt) return; // Exit silently, no trackingWhen DNT is enabled:
- Tracker exits immediately on initialization
- No network requests are sent
- No errors are thrown
- Website functionality is unaffected
Data Retention
Privacy is enforced through automatic data expiration:
- Daily salts: Automatically deleted after 2 days
- IP addresses: Never stored in the database
- User agents: Only parsed metadata stored (browser, OS, device type)
- Analytics data: Configurable retention period (typically 90 days)
No Personal Data
The tracker never collects personally identifiable information:
- No email addresses
- No names
- No IP addresses stored
- No unique device identifiers
- No cross-site tracking
Technical Reference
Script Size
- Unminified: ~2.5 KB
- Minified: ~1.8 KB
- Gzipped: ~0.9 KB
The tiny footprint ensures minimal impact on page load performance.
Browser Compatibility
The tracker is written in ES5-compatible JavaScript for maximum compatibility:
- Modern browsers: Chrome, Firefox, Safari, Edge (full support)
- Older browsers: IE11+ (full support)
- Mobile browsers: iOS Safari, Chrome Mobile (full support)
- Feature detection: Gracefully degrades if APIs are unavailable
Network Protocol
Events are sent via navigator.sendBeacon when available, with XMLHttpRequest fallback:
function send(data) { var payload = JSON.stringify(data); if (navigator.sendBeacon) { // Preferred: Guaranteed delivery, non-blocking navigator.sendBeacon(apiBase + "/api/event", payload); } else { // Fallback: Standard AJAX request var xhr = new XMLHttpRequest(); xhr.open("POST", apiBase + "/api/event", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(payload); }}Benefits of sendBeacon:
- Guaranteed to send even on page unload
- Non-blocking (doesn't delay navigation)
- Browser-optimized for analytics use cases
Error Handling
The tracker is designed to fail silently to prevent tracking errors from breaking your website:
- Network failures: No retries, fails silently
- Invalid responses: Caught and ignored
- Missing APIs: Feature detection prevents errors
- Invalid configuration: Silent exit if site ID missing
This ensures tracking is a zero-risk addition to your website.
Performance Characteristics
- Zero dependencies: No external libraries required
- Minimal DOM access: Only reads necessary properties
- Non-blocking loads: async/defer script loading
- Efficient heartbeats: 5-second interval balances accuracy and performance
- No render blocking: Never delays page rendering
Debugging
Verify that tracking is working correctly using browser developer tools.
Network Tab Inspection
Check that events are being sent to the API:
- Open your website in a browser
- Open Developer Tools (F12 or Cmd+Option+I)
- Go to the Network tab
- Filter by "event" or look for POST requests
- You should see requests to
/api/event - Click on a request to inspect the payload and response
Console Monitoring
Add debug logging to monitor custom events:
// Paste this in the browser console to debug trackingif (window.aa) { const originalTrack = window.aa.track; window.aa.track = function(name, properties) { console.log('📊 Custom Event:', name, properties); return originalTrack.apply(this, arguments); }; console.log('✅ Analytics debug mode enabled');} else { console.error('❌ window.aa not found - tracker may not be loaded');}Verifying Installation
Quick checks to verify the tracker is properly installed:
// Check if tracker script loadedconsole.log('Tracker loaded:', typeof window.aa !== 'undefined');// Check site IDconst script = document.querySelector('script[data-site-id]');console.log('Site ID:', script?.getAttribute('data-site-id'));// Test custom eventif (window.aa) { window.aa.track('test_event', { debug: true }); console.log('✅ Test event sent');}Common Issues
No Network Requests
If you don't see any /api/event requests:
- Check that the
data-site-idattribute is present - Verify Do Not Track is not enabled in browser settings
- Check browser console for JavaScript errors
- Ensure the script URL is correct and accessible
Events Not Appearing in Dashboard
If network requests succeed but events don't appear:
- Check that the site ID matches your dashboard site
- Verify the origin matches the configured site domain
- Wait a few seconds - there may be processing delay
- Check for 403 responses indicating origin mismatch
SPA Navigation Not Tracking
If page views aren't tracked on SPA navigation:
- Ensure the tracker loads before your framework initializes
- Check that your framework uses the History API (not hash routing)
- Verify that initial pageview succeeds (navigation requires
page_view_id)
Test Custom Events
Send a test event from the browser console:
// Open browser console and run:window.aa.track('debug_test', { timestamp: new Date().toISOString(), test: true});// Check Network tab for the POST request// Check dashboard for the custom event