Skip to content

Tracking

Install the tracking script

Choose a snippet variant, paste it into your site, and carry one visitor identity across several domains you own.

The tracking script is one small JavaScript file that runs on your website. You do not install it directly: you install a short snippet that loads it, generated for your product and shown on the Install page.

If you only want your first event, follow Track your first visitor. This page is the detail — which variant to use, what the script collects on its own, how it sends what it collects, and how to make several of your domains count as one visitor.

Choose a variant

Two forms are shown on the Install page. Copy one, paste it into the <head> of every page you want to track, and deploy.

VariantDefines window.lt before the script loadsUse it when
Loader snippet (Recommended)Yes — early lt(…) calls are queued and recordedAlways, unless you have a reason not to
Minimal variant (behind the advanced toggle)No — an early lt(…) call throws a ReferenceError in your page and the event is lostYou never call lt(…) yourself, or every call of yours certainly runs after load

Both collect exactly the same things automatically. Changing variant means re-pasting the snippet on your site.

The loader snippet is the canonical form:

<script>
  /* Lessly Tracking — paste into <head>.
     Load unconditionally. Do NOT wrap this in a consent check: the tracker
     detects the visitor's consent itself and falls back to a cookieless,
     no-identifier mode when statistics consent is declined or not yet given.
     Gating this tag hides declining and undecided visitors completely. */
  !(function (w, d) {
    w.lt=w.lt||function () { (w.lt.q=w.lt.q||[]).push(Array.prototype.slice.call(arguments)); };
    var s = d.createElement('script');
    s.async=1;
    s.src = 'https://<tracking host>/<product id>/tracking/script/lt.v1.js';
    var f = d.getElementsByTagName('script')[0];
    f.parentNode.insertBefore(s, f);
  })(window, document);
</script>

It does two things: it inserts an async <script> tag that fetches the real tracking script, and it defines window.lt as a stub that pushes every call into a queue. When the real script finishes loading it drains that queue, so calls made in the moments before the script is ready are recorded rather than lost.

The minimal variant is one tag:

<!-- Lessly Tracking. Load unconditionally — never behind a consent gate;
     the tracker handles consent itself and goes cookieless when it is absent. -->
<script async src="https://<tracking host>/<product id>/tracking/script/lt.v1.js"></script>

What the script URL carries

The src in your snippet has this shape:

https://<tracking host>/<product id>/tracking/script/lt.v1.js

Everything the script needs is in that address, which is what makes one snippet cover a whole product:

  • The tracking host is the shared Lessly edge by default. Claim a host on a domain of your own on the Tracking domain page and the snippet is regenerated to point at it, so the script becomes first-party on your site — see Register the domains you track.
  • The product id is what identifies whose data this is. There is nothing to fill in.
  • The version is part of the filename. A future version ships as a new filename, so a snippet already pasted on your site keeps working untouched. Fixes inside the current version reach your visitors on their own: the file is cached at the edge for ten minutes and then revalidated, so you never re-paste the snippet to pick up a new build.
  • The collector address is derived from it. Everything from /script/ onwards is replaced with /collect. Nothing else is configured.

Carry one identity across several of your domains

A visitor is recognised by a first-party cookie, and a cookie belongs to one domain. Subdomains of the same site share it — acme.com, www.acme.com and app.acme.com are one visitor with no setup at all. Two different domains, acme.com and acme-app.io, do not: to the browser they are unrelated sites, and the same person arriving on the second is a new anonymous visitor.

Cross-domain linking closes that gap. You declare which root domains belong together, and the script carries the visitor’s id from one to the other as they follow a link.

  1. Declare the group. It is one list per product — your marketing site, your app, a second brand of yours. Only add domains you own: anything in the group can receive a visitor id. The list is the link domains described in Register the domains you track.
  2. Copy the snippet from the Install page again. The group is carried as a data-link-domains attribute on the script tag.
  3. Replace the snippet on every site in the group — including the ones you did not change — and deploy each of them.

Step 3 is the one that is easy to miss. The group is baked into the pasted HTML, not fetched at run time, so a change has no effect at all on sites still carrying the old snippet: a domain added but not deployed receives no id, and a domain removed but not deployed keeps being decorated.

What the script does with the group

On the way out. The script watches clicks on your pages. When a link points at a domain in the group that the current cookie cannot reach, it appends the visitor’s id to that link’s address as a _lt_id query parameter, just before the browser navigates. Links anywhere else are left exactly as they are — including links to a domain that already shares the cookie, which needs no help, and any domain outside the group, which never receives an id.

On the way in. On arrival, the landing page’s script does three things, in this order:

  1. Removes _lt_id from the address bar immediately, before anything else, so it is not left in the URL, in a bookmark or in a referrer.
  2. Refuses the value if it is more than about two minutes old. A decorated link that was shared or bookmarked cannot hand someone else’s identity over later.
  3. Adopts the id only if this browser has no visitor id on this domain yet. An established visitor is never overwritten.

Only the anonymous visitor id travels this way; no personal data is put in the link. Ordinary links are what carries it — a visitor who types the second domain into the address bar, or arrives from a search result, has no link to carry the id and is recognised as the same person once they identify themselves.

None of it runs for a visitor who declined statistics consent or has not yet answered your banner: there is no visitor id to carry, links are left undecorated, and an incoming _lt_id is never adopted.

What the script captures on its own

Once it is on the page, with no code from you. This is what a visit looks like on a site with no consent banner, or when the visitor granted statistics consent; a declining visitor produces smaller events and no identity at all.

  • Page views. A page_view on load, and another on every route change in a single-page app — pushState, replaceState and the browser’s back and forward buttons all count. Navigating to the URL that is already open is not counted twice.
  • Form identity. When a form is submitted and it contains an email address, or a phone number in a field that is clearly a phone field, the script records an identify event and the visitor becomes a known person. Password and hidden fields are never read, and fields that look like card numbers, CVVs or one-time codes are never taken as a phone number. The same identity submitted twice in a row is only recorded once.
  • Page and device context. The URL, referrer and page title of each event, plus screen size, language and time zone.

Anything beyond that is your own lt('track', …) call.

How a visitor is recognised

The script writes two first-party cookies. It writes neither of them, and mints no identifiers at all, for a visitor who declined statistics consent or has not yet answered a banner.

CookieWhat it holdsLifetime
_lt_aidThe anonymous visitor id400 days, refreshed on every event
_lt_sidThe session id30 minutes of inactivity, on a sliding window; a new session starts on the next event after that

Both are set on the broadest domain your browser accepts for your site, which the script works out by trying and checking rather than by consulting a list — so app.acme.com and docs.acme.com share one visitor identity. On localhost and on bare IP addresses no domain is set.

Both identifiers travel inside the event, not as cookies sent to Lessly. There is no fingerprinting and no third-party cookie. If cookies are blocked entirely the script keeps one id for the lifetime of the page, so a single visit still holds together.

How events are sent

Events are collected in memory and sent in batches, in the background, using the browser’s beacon mechanism — and fetch with keepalive where beacons are unavailable. A batch goes out every 5 seconds, when the page is hidden or is being left so the last events of a visit are not lost, or as soon as it reaches 20 events or roughly 60 KB.

Delivery is fire-and-forget: the script never waits for a response and never blocks the page. It is wrapped so that it cannot throw into your page — if something goes wrong your site keeps working and events are simply missing. The script is small and loads asynchronously, so it does not hold up the rest of your page.

Where the script cannot run

The script only works on pages whose HTML you control. Externally hosted pages that cannot run it — a payment provider’s hosted checkout, for example — never appear on the Install page, and events from them have to come from your own backend instead. That is what tracking API keys are for.

Next steps

Was this page helpful?
Esc

Start typing to search the docs.

navigateselect