Documentation Index
Fetch the complete documentation index at: https://dataglue.io/llms.txt
Use this file to discover all available pages before exploring further.
Journey Tracking
DataGlue now tracks complete user journeys - every session, every event, every step from first visit to conversion. All data is stored in localStorage as a backup, and optionally synced to your server.
Features
- ✅ Complete history - Every session and event from first visit
- ✅ Offline-first - Works without server, data stored in localStorage
- ✅ JSON format - Two simple collections:
users and events
- ✅ Auto-identification - Detects email from forms or URL params
- ✅ First & last touch - Attribution tracking built-in
- ✅ Form-friendly - Works with native forms, Fillout, Typeform, etc.
Quick Start
Step 1: Add Profile Endpoint
<script
src="https://api.dataglue.io/glue.min.js"
profile-endpoint="https://your-api.com/glue"
auto-identify="true"
track-events="pageview,form_submit,click"
></script>
Step 2: That’s It!
DataGlue automatically:
- Tracks page views, clicks, form submits
- Stores everything in
localStorage (3 keys):
glue_user_profile - User info
glue_sessions - All sessions
glue_events - All events
- Auto-identifies users when they fill email fields
- Sends data to your API endpoint
How It Works
User Journey Flow
Day 1:
→ User lands from Google (utm_source=google)
→ DataGlue generates glue_user_id: "abc-123"
→ Tracks: page_view (/landing)
→ User leaves
Day 3:
→ User returns directly
→ Same glue_user_id: "abc-123" (from localStorage)
→ Tracks: page_view (/blog)
→ User leaves again
Day 5:
→ User clicks email link (utm_source=email)
→ Lands on /promo
→ Fills form with email: john@example.com
→ DataGlue auto-identifies: links "abc-123" → "john@example.com"
→ Form redirects to /thank-you?email=john@example.com&glue_user_id=abc-123
→ Your server receives identify() call
→ Journey complete!
Data Structure
localStorage Keys
glue_user_profile
{
"glue_user_id": "abc-123-uuid",
"email": "john@example.com",
"traits": {
"fname": "John",
"lname": "Doe"
},
"first_seen": "2024-01-15T10:30:00Z",
"last_seen": "2024-01-20T16:20:00Z",
"identified": true
}
glue_sessions
[
{
"session_id": "session-001",
"session_start": "2024-01-15T10:30:00Z",
"landing_page": "/landing",
"referrer": "https://google.com",
"utm": {
"utm_source": "google",
"utm_campaign": "summer"
},
"pages_viewed": 3,
"events_count": 5,
"converted": false
},
{
"session_id": "session-002",
"session_start": "2024-01-20T16:00:00Z",
"landing_page": "/promo",
"utm": {
"utm_source": "email"
},
"pages_viewed": 4,
"converted": true
}
]
glue_events
[
{
"event_id": "evt-001",
"session_id": "session-001",
"event": "page_view",
"timestamp": "2024-01-15T10:30:00Z",
"properties": {
"url": "/landing",
"title": "Landing Page"
}
},
{
"event_id": "evt-002",
"session_id": "session-001",
"event": "button_click",
"timestamp": "2024-01-15T10:32:15Z",
"properties": {
"button_text": "Learn More"
}
},
{
"event_id": "evt-003",
"session_id": "session-002",
"event": "form_submit",
"timestamp": "2024-01-20T16:05:00Z",
"properties": {
"form_id": "signup"
}
}
]
JavaScript API
View Journey Data
// Get complete user journey
const data = glue.journey.getCompleteData();
console.log(data);
/* Returns:
{
profile: {...},
sessions: [...],
events: [...],
summary: {
total_sessions: 3,
total_events: 16,
converted_sessions: 1,
attribution: {
first_touch: { utm_source: "google" },
last_touch: { utm_source: "email" }
}
}
}
*/
// Get just the profile
const profile = glue.journey.getProfile();
// Get all sessions
const sessions = glue.journey.getSessions();
// Get all events
const events = glue.journey.getEvents();
// Export as JSON (for download/debugging)
const json = glue.journey.exportData();
console.log(json); // Pretty-printed JSON string
Manual Tracking
// Track custom event
glue.journey.track('video_played', {
video_id: '123',
duration: 45
});
// Manually identify user
glue.journey.identify('user@example.com', {
fname: 'John',
lname: 'Doe',
plan: 'premium'
});
// Clear all journey data
glue.journey.clear();
Server Sync
// Manually send identify to server
await glue.sync.identify('user@example.com', {
fname: 'John',
subscription: 'pro'
});
// Manually track event to server
await glue.sync.track('purchase', {
product_id: '123',
amount: 99.00
});
// Sync all local data to server
await glue.sync.syncAll();
// Configure sync settings
glue.sync.config({
autoSync: true,
syncInterval: 30000 // 30 seconds
});
Server-Side API
Your server needs to handle these endpoints:
POST /glue/identify
Links glue_user_id to email address.
Request:
{
"glue_user_id": "abc-123-uuid",
"email": "user@example.com",
"traits": {
"fname": "John",
"lname": "Doe"
},
"context": {
"session_id": "session-456",
"utm_source": "google",
"utm_campaign": "summer",
"page": {
"url": "https://example.com/landing",
"title": "Landing Page"
},
"visitor": {
"user_agent": "Mozilla/5.0...",
"geolocation": "US | New York",
"timezone": "America/New_York"
}
}
}
Example Handler (Node.js):
app.post('/glue/identify', async (req, res) => {
const { glue_user_id, email, traits, context } = req.body;
// Save to database (MongoDB example)
await db.users.updateOne(
{ glue_user_id },
{
$set: {
email,
traits,
last_seen: new Date(),
'attribution.last_touch': {
utm_source: context.utm_source,
utm_campaign: context.utm_campaign,
timestamp: new Date()
}
},
$setOnInsert: {
glue_user_id,
first_seen: new Date(),
'attribution.first_touch': {
utm_source: context.utm_source,
utm_campaign: context.utm_campaign,
timestamp: new Date()
}
}
},
{ upsert: true }
);
res.json({ success: true });
});
POST /glue/track
Records individual events.
Request:
{
"glue_user_id": "abc-123-uuid",
"session_id": "session-456",
"email": "user@example.com",
"event": "button_click",
"properties": {
"button_text": "Get Started",
"page": "/landing"
},
"timestamp": 1704376800000,
"context": {
"utm_source": "google"
}
}
Example Handler:
app.post('/glue/track', async (req, res) => {
const { glue_user_id, event, properties, timestamp } = req.body;
// Insert event
await db.events.insert({
glue_user_id,
event,
properties,
timestamp: new Date(timestamp)
});
// Update user last_seen
await db.users.updateOne(
{ glue_user_id },
{ $set: { last_seen: new Date() } }
);
res.json({ success: true });
});
POST /glue/sync
Receives complete user data (all sessions + events).
Request:
{
"glue_user_id": "abc-123-uuid",
"data": {
"profile": {...},
"sessions": [...],
"events": [...],
"summary": {...}
},
"synced_at": "2024-01-20T16:30:00Z"
}
Example Handler:
app.post('/glue/sync', async (req, res) => {
const { glue_user_id, data } = req.body;
// Save complete user data
await db.users.updateOne(
{ glue_user_id },
{ $set: { ...data.profile, last_synced: new Date() } },
{ upsert: true }
);
// Bulk insert events
if (data.events.length > 0) {
await db.events.insertMany(data.events, { ordered: false });
}
res.json({ success: true });
});
Configuration Options
Script Tag Attributes
<script
src="https://api.dataglue.io/glue.min.js"
profile-endpoint="https://api.example.com/glue"
auto-identify="true"
auto-sync="false"
track-events="pageview,click,form_submit"
></script>
| Attribute | Default | Description |
profile-endpoint | - | Your API base URL |
auto-identify | true | Auto-identify from email fields/URL params |
auto-sync | false | Automatically sync data to server |
track-events | pageview,form_submit | Events to auto-track (comma-separated) |
Event Types
Auto-trackable events:
pageview - Page views
click - Link and button clicks
form_submit - Form submissions
Query Examples
Get User Journey
// Client-side
const journey = glue.journey.getCompleteData();
// Server-side (MongoDB)
const user = await db.users.findOne({ email: "john@example.com" });
const events = await db.events.find({ glue_user_id: user.glue_user_id }).sort({ timestamp: 1 });
Conversion Funnel
// Server-side
const landed = await db.events.countDocuments({
event: "page_view",
"properties.path": "/landing"
});
const submitted = await db.events.countDocuments({
event: "form_submit"
});
const converted = await db.events.countDocuments({
event: "page_view",
"properties.path": "/thank-you"
});
console.log(`Funnel: ${landed} → ${submitted} → ${converted}`);
Attribution Report
// Server-side
const sources = await db.users.aggregate([
{
$group: {
_id: "$attribution.first_touch.utm_source",
count: { $sum: 1 }
}
}
]);
Best Practices
- Let forms redirect with query params - Easiest way to pass data between pages
- Use meaningful event names -
video_played not event_1
- Keep properties simple - JSON-serializable values only
- Implement server endpoints - Don’t rely solely on localStorage
- Test with different form providers - Fillout, Typeform, native HTML
Troubleshooting
Check localStorage Data
// Open browser console
console.log(JSON.parse(localStorage.getItem('glue_user_profile')));
console.log(JSON.parse(localStorage.getItem('glue_sessions')));
console.log(JSON.parse(localStorage.getItem('glue_events')));
Debug Mode
// Enable detailed logging
localStorage.setItem('glue_dev_mode', 'true');
location.reload();
Export Journey
// Download complete journey as JSON file
const data = glue.journey.exportData();
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'user-journey.json';
a.click();
FAQ
Q: Does this work with iframes?
A: Yes! For embedded forms, they redirect with query params. DataGlue on the next page auto-identifies from URL.
Q: What if my server is down?
A: All data stays in localStorage. When server comes back up, call glue.sync.syncAll().
Q: How much localStorage space does this use?
A: ~10-50KB per user. DataGlue limits to 1000 events and 100 sessions automatically.
Q: Can I use this with Segment/Mixpanel?
A: Yes! Use glue.journey.track() and send to both DataGlue and your analytics tool.
Q: How do I handle multiple devices?
A: Each device has its own glue_user_id. When email is captured, your server links all IDs to one user.