Event Tracking
The event tracking system captures user interactions and page analytics, sent to the Experience service via protobuf for maximum throughput and minimal payload size.
What's Tracked Automatically
| Event | Trigger | Data Captured |
|---|---|---|
page_view | Navigation (SPA-aware via History API) | URL, title, referrer, load time, viewport |
click | Click on any element with data-t attribute | Element TID, tag, text, x/y position |
scroll | Scroll depth milestones (25%, 50%, 75%, 100%) | Scroll depth percentage, page URL |
hover | Sustained hover (>500ms) on data-t elements | Element TID, hover duration |
heatmap | Mouse movement (throttled, every 100ms) | x/y coordinates, viewport size |
session_start | New session detected | Session ID, timestamp |
session_end | Page unload / timeout | Session duration, pages visited |
Manual Tracking
Builders can fire custom events:
exp.track('checkout_started', {
cart_value: '99.99',
item_count: '3',
currency: 'GBP'
});See the JS SDK and React SDK for full API details.
The data-t System
The data-t attribute (short for "tracking ID") is the bridge between the UI and the analytics system. Any HTML element with a data-t attribute is automatically tracked for clicks, hovers, and visibility.
<!-- Automatically tracked -->
<button data-t="checkout-btn">Complete Purchase</button>
<a data-t="nav-pricing" href="/pricing">Pricing</a>
<div data-t="hero-banner">...</div>How It Works
- The SDK's
ExperienceProvider(React) orExperience.init()(vanilla) sets up aMutationObserveron the document - All elements with
data-tare registered in an internal map - Event delegation on
documentcaptures clicks — if the target (or ancestor) hasdata-t, an event fires - An
IntersectionObservertracks visibility ofdata-telements (for "was this seen?" analytics) - Hover tracking uses
mouseenter/mouseleavewith a 500ms threshold
Naming Conventions
{section}-{element}-{qualifier}
Examples:
nav-link-pricing
hero-cta-signup
checkout-btn-submit
sidebar-toggle-theme
card-product-123Design System Integration
All interactive @shellapps/react-ui components accept a trackingId prop that maps to data-t:
<Button trackingId="checkout-submit" variant="primary">
Complete Purchase
</Button>
// Renders: <button data-t="checkout-submit" ...>Complete Purchase</button>Event Batching & Delivery
Events are NOT sent individually. The SDK collects events in memory and sends them in batches:
Events generated ──► In-memory queue ──► Batch (protobuf encode) ──► POST /api/v1/events/ingest
│ │
│ Every 2 seconds
│ OR queue reaches 50 events
│ OR page unload (sendBeacon)
│
IndexedDB backup
(offline resilience)- Batch interval: 2 seconds (configurable)
- Max batch size: 50 events (configurable)
- Page unload: Uses
navigator.sendBeacon()for reliable delivery on close - Offline: Events queue in IndexedDB, flush when connection restored
- Sampling: Configurable
sampleRate(0.0–1.0) for high-traffic apps
Heatmap Data
Heatmap events are separate from regular tracking events because of their volume. Mouse position is captured every 100ms and batched separately.
The dashboard renders heatmaps by overlaying aggregated position data onto a wireframe/screenshot of the page. Positions are normalized to viewport width so different screen sizes aggregate correctly.
Heatmap data has a shorter retention period (30 days vs 90 days for regular events).
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /api/v1/events/ingest | API Key | Ingest protobuf EventBatch |
POST | /api/v1/events/ingest/json | API Key | Ingest JSON events (debug) |
GET | /api/v1/events/query | Bearer | Query events with filters |
GET | /api/v1/events/aggregate | Bearer | Aggregated analytics |
GET | /api/v1/events/sessions/:sessionId | Bearer | Full session event stream |
GET | /api/v1/events/heatmap | Bearer | Heatmap data for a page |
See the full API Reference and Data Models for protobuf schemas and MongoDB collection structures.