> For the complete documentation index, see [llms.txt](https://docs.fullsession.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fullsession.io/3.-installing-the-tracker.md).

# 3. Installing the Tracker

Chapter 2 covered the express install. This chapter is the **complete, accurate reference** for installing the FullSession tracker. It documents the exact snippet FullSession generates for you, what every line of it does, the alternative install paths (Google Tag Manager, the `fullsession` npm package, and the native mobile SDKs), and how FullSession verifies that recording has actually started.

#### One Customer ID, many sites

Your account has a **single Customer ID** that is shared across **all** the domains you track. You don't get a different ID per website — instead, you install the *same* snippet (carrying that one Customer ID) on every domain you want to record.

FullSession then sorts the incoming data for you: when a session is recorded, FullSession matches the **origin domain** the session came from against the URLs of the **sites** you've saved in your account, and files the session under the matching site automatically. This is what lets a single Customer ID power a marketing site, a web app, and a help center — each one's sessions land in its own site, even though they all share the same tracking code.

Wherever you see `YOUR_CUSTOMER_ID` below, the Installation page will have already substituted your real Customer ID.

<div data-with-frame="true"><figure><img src="/files/4iGam9jANZdZWIQCS5Rk" alt="FullSession Installation page showing install method tabs for Script, GTM, NPM, Android, and iOS with a pre-filled Customer ID."><figcaption></figcaption></figure></div>

> **Add your domains first.** Because sessions are routed by matching their origin domain to your saved site URLs, make sure each domain you intend to track is listed under **Settings → Sites** before (or shortly after) you install. A domain that isn't saved has no site to be filed under.

***

### 3.1 JavaScript Snippet Installation

The direct `<script>` snippet is the most universal install method. It works on any site where you can edit the page HTML — a custom site, WordPress, Shopify, Webflow, and so on.

#### The snippet

This is the exact snippet FullSession generates. Copy it from **Settings → Installation → Script** (don't retype it — the app embeds your Customer ID and the correct CDN URL for your environment automatically):

```html
<!-- Fullsession Recording Code for http://fullsession.io/ -->
<script type="text/javascript">
    window.FUS_IS_IN_IFRAME = (window.top != window.self);
    (function (m, n, t, l, x, p, o) {
        window["_fus_host"] = l;
        window["_site_id"] = p;
        window["_fus_id"] = x;
        o = n.createElement(t);
        o.type = "text/javascript";
        o.async = true;
        o.src = "https://emitter.fullsession.io/RTSessions.js";
        y = n.getElementsByTagName(t)[0];
        y.parentNode.insertBefore(o, y);
    })(window, document, "script", "fullsession.io", 'YOUR_CUSTOMER_ID');
</script>
```

The **same snippet goes on every domain** in your account. You do not customize it per site — the Customer ID is identical everywhere, and FullSession handles the per-site routing on its end.

<div data-with-frame="true"><figure><img src="/files/21enDn9Px5e0TDpWB0LJ" alt="FullSession Installation page showing the Script tab with a copy button for the tracking snippet used across domains."><figcaption></figcaption></figure></div>

#### Anatomy of the snippet

You don't need to understand the internals to install it, but knowing what each part does helps when troubleshooting:

| Line                                                      | What it does                                                                                                                |
| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `window.FUS_IS_IN_IFRAME = (window.top != window.self);`  | Detects whether the page is running inside an iframe. The tracker uses this to behave correctly when your site is embedded. |
| `(function (m, n, t, l, x, p, o) { … })(...)`             | A self-executing function (IIFE) that injects the recorder script without blocking your page.                               |
| `window["_fus_host"] = l;`                                | Stores the FullSession host (`"fullsession.io"`).                                                                           |
| `window["_fus_id"] = x;`                                  | Stores **your Customer ID** — the value passed in as `'YOUR_CUSTOMER_ID'`. This is what ties recordings to your account.    |
| `o = n.createElement("script"); o.async = true;`          | Creates a new `<script>` element and marks it **async** so it loads in the background.                                      |
| `o.src = "https://emitter.fullsession.io/RTSessions.js";` | The recorder library that actually captures sessions.                                                                       |
| `y.parentNode.insertBefore(o, y);`                        | Inserts the recorder ahead of the first existing script on the page.                                                        |

The key takeaways:

* The tracker is loaded **asynchronously** (`async = true`), so it never blocks your page from rendering.
* Your **Customer ID** is the last argument to the function — `'YOUR_CUSTOMER_ID'`. This single value is what every domain in your account shares.
* The recorder itself is served from FullSession's emitter CDN, so you never host or update any tracking code yourself — improvements ship automatically.
* The **origin domain** of each recorded page is what FullSession later uses to route the session to the correct site — you don't encode the site anywhere in the snippet.

#### CDN URLs by environment

The `src` in your snippet depends on which FullSession environment your account uses:

| Environment               | Recorder URL                                           |
| ------------------------- | ------------------------------------------------------ |
| **Production**            | `https://emitter.fullsession.io/RTSessions.js`         |
| **Development / staging** | `https://dev-emitter.fullsession.io/RTSessions-dev.js` |

The Installation page always shows the correct one for your account — you normally never need to change this by hand.

#### Where to paste it

Paste the snippet inside the `<head>` of every page you want to record, as **high as possible** — ideally right after the opening `<head>` tag. Placing it early means the recorder initializes before the rest of your page loads, so it captures the full session from the very first moment.

<div data-with-frame="true"><figure><img src="/files/eCflEOR5DcgvrWIfaaLB" alt="FullSession tracking snippet placement recommendation showing the script added near the top of the HTML head section."><figcaption></figcaption></figure></div>

If your site uses a shared layout, theme header, or master template, paste the snippet there **once** so it's automatically present on every page of that domain. Repeat for each separate domain you want to track.

#### Common platforms

| Platform        | Where to paste                                                      |
| --------------- | ------------------------------------------------------------------- |
| **WordPress**   | Theme's `header.php`, or a "Header & Footer Scripts" plugin         |
| **Shopify**     | Online Store → Themes → Edit code → `theme.liquid`, inside `<head>` |
| **Webflow**     | Project Settings → Custom Code → Head Code                          |
| **Squarespace** | Settings → Advanced → Code Injection → Header                       |
| **Custom site** | The shared `<head>` include / master layout                         |

#### Tracking multiple domains

Because all your domains share one Customer ID, adding another site to FullSession is straightforward:

1. Add the new domain under **Settings → Sites** so FullSession has a URL to match sessions against.
2. Install the **same snippet** on that domain.
3. Sessions from the new domain are automatically filed under its site, separate from your other sites' data.

<div data-with-frame="true"><figure><img src="/files/Ko6JZSgB2GH8bq0lISiL" alt="FullSession setup showing one Customer ID connected to multiple domains with sessions routed by the origin URL."><figcaption></figcaption></figure></div>

#### Publish and check

After pasting, **save and publish** your site, then open it in an incognito window and browse for \~30 seconds. The Installation page should change from **Waiting for data** to **Verified** (covered in detail in section 3.5).

***

### 3.2 Google Tag Manager Installation

If your site runs **Google Tag Manager (GTM)**, FullSession can install the tracker for you — no copy-pasting into your site's code. This is the path most marketing-managed sites use.

#### Automated GTM setup (recommended)

From the **GTM** tab on the Installation page, you can connect your Google account and let FullSession create and publish the tag for you:

1. On the Installation page, open the **GTM** tab.
2. Click **Connect Google Tag Manager** and authorize access to your GTM account.
3. Select the **GTM container** for your site.
4. Confirm — FullSession will then automatically:
   * Create a **page-view trigger** named **"Fullsession Trigger"**.
   * Create a **Custom HTML tag** named **"Fullsession Tracking Script"** containing your snippet (with your Customer ID).
   * Wire the tag to fire on the trigger (i.e. on every page view).
   * Create and **publish** a new container version named **"Fullsession tracking code added"**.<br>

     <div data-with-frame="true"><figure><img src="/files/NDtWQBNd9siBBiImUJgB" alt="Automated GTM setup flow showing FullSession creating the tracking script tag, trigger, and publishing the container after authorization." width="563"><figcaption></figcaption></figure></div>

When the flow finishes, the tracker is live on every page covered by the container — no manual GTM work required. If that container serves more than one domain, all of them report under your single Customer ID and are routed to their respective sites automatically.

#### Manual GTM setup

If you'd rather not connect your Google account, you can add the tag yourself:

1. In Google Tag Manager, open your container and go to **Tags → New**.
2. Name the tag **Fullsession Tracking Script**.
3. Choose **Tag Configuration → Custom HTML**.
4. Paste the FullSession **Script** snippet (from section 3.1) into the HTML field.
5. Under **Triggering**, create or select a trigger that fires on **All Pages** (a page-view trigger).
6. Click **Save**.
7. Click **Submit → Publish** to push a new container version live.

<figure><img src="/files/RZ84siO8U2oI4p993QHi" alt="Manual GTM setup showing a Custom HTML tag named FullSession Tracking Script with a page-view trigger."><figcaption></figcaption></figure>

> **Don't forget to publish.** In GTM, saving a tag is not enough — the tracker only goes live after you **Submit** and publish a new container version. The automated flow does this for you; the manual flow does not.

#### A note on the HTML tag settings

When FullSession creates the GTM tag automatically, it configures it as a **Custom HTML** tag with `convertJsValuesToExpressions` enabled and `usePostscribe` disabled. If you build the tag manually, the GTM defaults are fine — you don't need to change these unless your container has special requirements.

***

### 3.3 NPM Package Installation

For applications built with a bundler (React, Vue, Angular, Svelte, Next.js, etc.), FullSession publishes an official npm package. This is often cleaner than a raw `<script>` tag because the tracker becomes part of your app's normal dependency and build process.

#### Install

```bash
npm i fullsession
```

#### Initialize

Import the tracker and initialize it **once**, as early as possible in your app's startup (for example, in your root entry file — `main`, `index`, `App`, or equivalent):

```javascript
import { fullSessionTracker } from 'fullsession'

// initialize fullsession tracker
fullSessionTracker.initialize('YOUR_CUSTOMER_ID');
```

<div data-with-frame="true"><figure><img src="/files/pcY84mlOOh980UaEh7XU" alt="FullSession Installation page showing the NPM setup with the install command and tracker initialization code pre-filled with the Customer ID."><figcaption></figcaption></figure></div>

#### Where to put `initialize`

Call `initialize` **exactly once**, at app boot — not inside a component that re-renders or a route that re-mounts. Initializing repeatedly can start the recorder more than once.

| Framework            | Recommended location                                                         |
| -------------------- | ---------------------------------------------------------------------------- |
| **React (Vite/CRA)** | `src/main.tsx` / `src/index.tsx`, before rendering the root                  |
| **Next.js**          | A top-level client component / `app/layout.tsx` (client side), or `_app.tsx` |
| **Vue**              | `src/main.ts`, before `app.mount()`                                          |
| **Angular**          | `main.ts`, or an `APP_INITIALIZER`                                           |

#### Single-page apps capture navigation automatically

Once initialized, the tracker detects client-side route changes on its own — each route change is recorded as a separate page within the same session, even though there's no full page reload. You do **not** need to re-initialize on navigation. This applies to both the npm package and the `<script>` snippet.<br>

<figure><img src="/files/sMyuf1XSM8XVK28Doe7Z" alt=""><figcaption></figcaption></figure>

***

### 3.4 Mobile SDK Installation (Android & iOS)

FullSession also records native mobile apps through dedicated SDKs. The Installation page has **Android** and **iOS** tabs that generate the exact dependency and initialization code, pre-filled with your Customer ID and site URL. *(React Native and Flutter are listed as coming soon.)*

#### Android

Add the dependency in your module's `build.gradle`:

```gradle
implementation("io.fullsession:fullsession-android:0.1.2")
```

Initialize the SDK when your application starts (typically in your `Application` class):

```kotlin
import io.fullsession.FullSession
import io.fullsession.config.FullSessionConfig

FullSession.init(application, FullSessionConfig(
    customerId = "YOUR_CUSTOMER_ID",
    siteUrl = "https://yoursite.com"
  )
)
```

<div data-with-frame="true"><figure><img src="/files/eD0joCf8Gi8xxPk8DjDy" alt="FullSession Android installation tab showing the Gradle dependency and initialization code with customer ID and site URL configured."><figcaption></figcaption></figure></div>

#### iOS

Add the package via Swift Package Manager in your `Package.swift` (or through Xcode → Add Packages):

```swift
dependencies: [
    .package(url: "https://github.com/fullsession/ios-sdk-releases", from: "0.1.2")
],
targets: [
    .target(
        name: "<YOUR_TARGET_NAME>",
        dependencies: [
            .product(name: "FullSessionIos", package: "FullSessionIos")
        ]
    )
]
```

Initialize recording at app launch:

```swift
import FullSessionIos

let config = FullSessionConfig(
    customerId: "YOUR_CUSTOMER_ID",
    siteUrl: "https://yoursite.com"
)

FullSession.shared.initRecording(config: config)
```

<figure><img src="/files/CT5mpFH650A9y9GfQAEF" alt="FullSession iOS installation tab showing the Swift Package Manager setup and FullSession recording initialization code."><figcaption></figcaption></figure>

#### Mobile configuration

Both mobile SDKs take the same two configuration values:

| Option       | Required | Purpose                                                                     |
| ------------ | -------- | --------------------------------------------------------------------------- |
| `customerId` | Yes      | Your Customer ID — ties recordings to your account                          |
| `siteUrl`    | Yes      | The app's site URL, used for context and to route data to the matching site |

***

### 3.5 Identifying Users & Sending Events (the `FUS` API)

Once the tracker is installed via the `<script>` snippet or GTM, it exposes a small global object called **`FUS`** on `window`. You'll use this to attach identities and custom events to recordings. (This section is a quick reference; \[Chapter 4 — Identifying Users] and \[Chapter 7 — Recording Rules & Element Tracking] cover the concepts in depth.)

The `FUS` object exposes two primary methods:

#### `FUS.identify(...)` — attach a user identity

```javascript
window.FUS.identify('YOUR_USER_ID', {
  name: 'Jane Doe',
  email: 'jane@example.com'
});
```

* **First argument** — a stable, unique identifier for the user (your internal user ID or their email).
* **Second argument (optional)** — a properties object. The recognized fields are **`name`** and **`email`**.

Call this right after a user logs in, so their sessions become searchable by identity.<br>

<div data-with-frame="true"><figure><img src="/files/zeRmAlY2FwM9w5RFwSft" alt=""><figcaption></figcaption></figure></div>

#### `FUS.event(...)` — record a custom event

```javascript
window.FUS.event('funnel_access', {
  funnel_id: '1024',
  funnel_name: 'Checkout'
});
```

* **First argument** — the event name.
* **Second argument (optional)** — a properties object of any key/value details you want attached.

Custom events become the building blocks of funnels, segments, and insight cards.

<div align="left" data-with-frame="true"><figure><img src="/files/T79Fdq1YwxHvIcRzBrEy" alt="FullSession session timeline showing a custom event with its name and captured properties alongside the session replay." width="359"><figcaption></figcaption></figure></div>

> **Guard against early calls.** Because the tracker loads asynchronously, `window.FUS` may not exist for the first few milliseconds after page load. If you call `FUS.identify` or `FUS.event` very early, check that `window.FUS` is defined first, or call it after the user has interacted (e.g. right after login).

***

### 3.6 Site Verification & Troubleshooting

After you install the tracker, FullSession verifies that it's actually loading and reaching your account. Verification is a real handshake between FullSession and your live site — not just a check that the snippet text is present.

#### How verification works

When you open the Installation page (or click **Verify**), FullSession loads your site in a hidden iframe and listens for messages the tracker posts back as it initializes:

| Signal                         | Meaning                                                                                                                        |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| **`FUS_LOADED`**               | The tracker initialized successfully. It includes a `customer_id`, which FullSession checks against your expected Customer ID. |
| **`FUS_FAILED_TO_REACH_SITE`** | The tracker could not initialize. It includes an `error` with details.                                                         |

If no signal arrives within about **8 seconds**, verification times out and falls back to checking whether any sessions have been recorded for the site yet.

<div data-with-frame="true"><figure><img src="/files/tkNnXCq1UBaBQVUE0V7d" alt="FullSession verification process showing the site loading and waiting for the tracker handshake confirmation."><figcaption></figcaption></figure></div>

#### The two status states

The Installation page shows one of two states:

| Status                  | Meaning                                                                                              |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| 🟢 **Verified**         | The tracker loaded and its `customer_id` matches your account — data is being recorded. You're done. |
| 🟡 **Waiting for data** | The snippet may be present, but no successful handshake or session has been received yet.            |

<div data-with-frame="true"><figure><img src="/files/NXZOQCeo4yOvO2mIB4nt" alt="FullSession status indicator showing tracker verification state with verified and waiting for data statuses." width="563"><figcaption></figcaption></figure></div>

<div data-with-frame="true"><figure><img src="/files/UuubntZsF93gnfuYIpeE" alt="FullSession status indicator showing tracker verification state with verified and waiting for data statuses." width="563"><figcaption></figcaption></figure></div>

The fastest way to flip from **Waiting for data** to **Verified** is to open your site in an incognito window and browse a few pages — that produces a real session and a real `FUS_LOADED` signal.

#### Troubleshooting: "It still says Waiting for data"

Work through these in order:

1. **Are you testing in incognito?** Sessions from signed-in internal users may be excluded. Use a private window. See \[Chapter 4, section 4.4].
2. **Is the snippet actually on the live page?** View page source (Ctrl+U / Cmd+Option+U) and search for `FUS` or `RTSessions`.
3. **Does the Customer ID match?** Verification only succeeds when the snippet's `customer_id` equals your account's Customer ID. If you pasted a malformed or truncated ID, the tracker will load but never verify. Re-copy the snippet from the Installation page.
4. **Is the domain saved as a site?** Sessions are routed by matching their origin domain to your saved site URLs. If you're testing on a domain that isn't listed under **Settings → Sites**, add it so the session has a site to land in.
5. **Is a Content Security Policy (CSP) blocking it?** A strict CSP can stop the recorder from loading or sending data. Allow the FullSession emitter domain (`emitter.fullsession.io`) in your `script-src` and `connect-src` directives.
6. **Did you publish your GTM container?** If you installed via GTM, confirm you **published** a new version — a saved-but-unpublished tag won't fire. See section 3.2.
7. **Are ad blockers active?** Test in a browser without blockers, or allowlist your own test page.

<mark style="color:$warning;">\[img\_place\_holder]</mark> *<mark style="color:$warning;">An example CSP that allows the FullSession recorder to load (</mark><mark style="color:$warning;">`script-src`</mark><mark style="color:$warning;">) and report sessions (</mark><mark style="color:$warning;">`connect-src`</mark><mark style="color:$warning;">).</mark>*

#### Troubleshooting: sessions landing under the wrong site (or no site)

Because routing is based on matching the origin domain to your saved site URLs:

* If sessions aren't showing up under the expected site, confirm the **domain is spelled and saved correctly** under **Settings → Sites** (watch for `www` vs. non-`www`, `http` vs. `https`, and subdomain differences).
* If a domain isn't saved at all, its sessions have **no matching site** to be filed under. Add the domain, and subsequent sessions will route correctly.

#### Troubleshooting: replay shows broken styling

If sessions record but the replay looks unstyled, the recorder usually couldn't read some of your CSS:

* **Cross-origin stylesheets without CORS headers** won't be captured. Host them on the same origin, or serve them with `crossorigin="anonymous"` and a permissive `Access-Control-Allow-Origin` header.
* **Authenticated/login-gated CSS** that the player can't fetch won't render. Make critical styles public or inline them.

#### Troubleshooting: wrong pages, or too many/too few sessions

* Check **Settings → Recording Rules** to see whether any rules limit which URLs are recorded. See \[Chapter 7 — Recording Rules & Element Tracking].
* Remember that bot traffic and pre-render requests are filtered out and won't appear as sessions.

> **The big picture** — one **Customer ID** powers all your domains; the snippet loads an async recorder from `emitter.fullsession.io` keyed to that ID; GTM, the `fullsession` npm package, and the native mobile SDKs are alternative ways to load the same recorder; and FullSession routes each session to the right site by matching its origin domain to your saved site URLs. Once you see **Verified**, every other chapter in this guide is fed by the data the tracker captures.

***

> **Next up:** \[Chapter 4 — Identifying Users] builds on the `FUS.identify` call introduced here, showing how to turn anonymous recordings into searchable, named user journeys.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.fullsession.io/3.-installing-the-tracker.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
