# Visitor tracking

Beyond the conversation widget, the SDK ships a separate **tracker** that resolves a stable visitor identity, then auto-tracks page views, clicks, and scroll depth. Conversations carry the same visitor ID, so your CRM sees one continuous timeline whether the visitor talks to the agent or just browses.

## Standalone tracker (script tag)

Drop the tracker on every page where you want visitor analytics:

```html title="head.html"
<script
  src="https://livelayer.app/v1.js"
  data-agent-id="agt_abc123"
  data-tracker="auto">
</script>
```

When `data-tracker="auto"` is set, the SDK boots the tracker automatically on script load.

## What gets tracked automatically

- **Page views** — initial load + SPA navigation (`pushState` / `popstate`)
- **Clicks** on interactive elements: links, buttons, `[role="button"]`, `[onclick]`
- **Scroll depth** (percentage) on page exit
- **Page exit** — duration, final scroll depth

Events are batched (every 5 seconds or 50 events) and POSTed to `/api/visitors/{visitorId}/events`. Failed posts retry with exponential backoff.

## Visitor identity

The tracker resolves a stable identity using a layered approach:

1. **FingerprintJS (best effort)**

   The tracker dynamically imports `@fingerprintjs/fingerprintjs` and computes a device-level fingerprint. Survives across cookie clears.

2. **localStorage fallback**

   If FingerprintJS is blocked (adblocker, privacy mode), the tracker falls back to a UUID stored in `localStorage`. Per-browser only.

3. **Server resolution**

   Fingerprint + localStorage ID POST to `/api/visitors/identify`. The server returns `{ visitorId, isReturning, sessionCount }`.

## Identifying known visitors

When you know who the visitor is (signed-in user), call `identify()` to attach attributes:

```ts title="identify.ts"
LiveLayer.identify({
  email: "jordan@acme.com",
  name: "Jordan Park",
  phone: "+1...",
  company: "Acme Inc",
  // Free-form custom attributes
  plan: "pro",
  signupAt: "2026-01-12",
});
```

Subsequent events (and the next conversation) carry these attributes.

## Tracking custom events

```ts title="custom-events.ts"
LiveLayer.track("clicked_pricing_cta", {
  plan: "pro",
  source: "homepage_hero",
});

LiveLayer.track("watched_demo_video", {
  videoId: "intro-2026",
  durationSeconds: 47,
});
```

Custom events show up alongside auto-tracked events in the visitor timeline.

## Reading visitor state

```ts title="visitor-state.ts"
console.log(LiveLayer.visitor);
// { id: "vis_abc123", isReturning: true, sessionCount: 4 }
```

Useful for personalizing UI based on whether the visitor has been here before.

## Configuration

```html title="config.html"
<script
  src="https://livelayer.app/v1.js"
  data-agent-id="agt_abc123"
  data-tracker="auto"
  data-track-page-views="true"
  data-track-clicks="true"
  data-api-base="https://livelayer.app">
</script>
```

<!-- omitted: ParamsTable -->

## Privacy and consent

- The tracker respects `Do Not Track` headers — when `navigator.doNotTrack === "1"`, automatic events are suppressed.
- `LiveLayer.optOut()` clears localStorage and stops sending events for the rest of the page session.
- Mark sensitive elements with `data-ll-private="true"` to exclude them from auto-tracked clicks.

## Where this data appears

In the dashboard:

- **People** page — visitor timeline with both events and conversations interleaved
- **Inbox** — every conversation links back to its visitor's full event history
- **Analytics** — funnel and retention reports built from event data

## Read next

- [Visitor identity resolution (concept)](/docs/introduction/concepts) — how visitor IDs work
- [NPM tracker programmatic API](/docs/develop/npm/sdk/tracker) — fine-grained control from React
