> ## 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

> Track complete user journeys from first visit to conversion

# 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

```html theme={null}
<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`

```json theme={null}
{
  "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`

```json theme={null}
[
  {
    "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`

```json theme={null}
[
  {
    "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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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:**

```json theme={null}
{
  "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):**

```javascript theme={null}
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:**

```json theme={null}
{
  "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:**

```javascript theme={null}
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:**

```json theme={null}
{
  "glue_user_id": "abc-123-uuid",
  "data": {
    "profile": {...},
    "sessions": [...],
    "events": [...],
    "summary": {...}
  },
  "synced_at": "2024-01-20T16:30:00Z"
}
```

**Example Handler:**

```javascript theme={null}
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

```html theme={null}
<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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// Server-side
const sources = await db.users.aggregate([
  {
    $group: {
      _id: "$attribution.first_touch.utm_source",
      count: { $sum: 1 }
    }
  }
]);
```

## Best Practices

1. **Let forms redirect with query params** - Easiest way to pass data between pages
2. **Use meaningful event names** - `video_played` not `event_1`
3. **Keep properties simple** - JSON-serializable values only
4. **Implement server endpoints** - Don't rely solely on localStorage
5. **Test with different form providers** - Fillout, Typeform, native HTML

## Troubleshooting

### Check localStorage Data

```javascript theme={null}
// 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

```javascript theme={null}
// Enable detailed logging
localStorage.setItem('glue_dev_mode', 'true');
location.reload();
```

### Export Journey

```javascript theme={null}
// 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.
