Install the Tag
Integration guide for developers
ClickLens runs from a single script tag. It collects browser signals, scores each
session, and never blocks rendering or touches your page's own behaviour. The tag is
about 13 KB gzipped and loads with defer,
so it never delays your content.
The dashboard generates a copy-paste snippet for each site with your site key and beacon token already filled in (Settings → Site Settings, or the onboarding install step). This guide explains every part of that snippet so you can adapt it to a tag manager, a CMS, or a server-rendered template.
The snippet
Paste this just before the closing </head>
tag on every page that receives paid traffic, including your landing pages and your
thank-you or confirmation pages.
<script>window.clicklens=window.clicklens||function(){(window.clicklens.q=window.clicklens.q||[]).push(arguments)};</script>
<script
defer
src="https://app.clicklens.io/t.js"
data-site="YOUR_SITE_KEY"
data-cl-token="YOUR_BEACON_TOKEN"
data-endpoint="https://app.clicklens.io/api/v1/beacon"
></script>
Keep both lines, in that order. The first one is a queue. The tag loads with
defer, so a
clicklens('conversion', …)
call that runs while the page is still parsing — the usual shape of a thank-you
page — happens before the tag has defined that function. The queue holds those
calls; the tag replays them the moment it loads. Drop the line and those conversions are
never recorded, and nothing in the browser console says so.
If your
Content-Security-Policy
blocks inline scripts, that first line needs a nonce or a hash or the browser drops it —
see Content Security Policy below
for the exact directives and hash values.
There is no integrity
attribute, and adding one will eventually stop your tracking. Subresource Integrity pins
a script to one exact build. We change the tag whenever detection improves, which changes
its bytes, and a browser holding a hash from an earlier build refuses to run the new one
— no console error on your side, no beacons, and a dashboard that quietly goes to
zero. The tag is served from our own origin over HTTPS, which is the same connection your
beacons already trust.
Attributes
| Attribute | Required | Purpose |
|---|---|---|
src | Yes | The tag URL, https://app.clicklens.io/t.js. |
defer | Recommended | Loads the tag without blocking page render. It runs after the document parses. |
data-site | Yes | Your site key. Identifies which site the beacons belong to. It is public and grants no dashboard access. |
data-cl-token | Recommended | The per-site beacon signing token. The tag signs each beacon with it (HMAC-SHA256) so a forged or replayed beacon is flagged. If omitted, beacons are sent unsigned and flagged server-side, never rejected. |
data-endpoint | Optional | Where beacons are sent. If omitted, the tag derives it from its own src origin, which is correct for the standard install. |
data-cl-auto-convert | Optional | Set to "true" to fire a conversion on every form submit. Off by default. Only safe when every form on the page is a genuine conversion — see the conversion tracking guide. |
Configuring without data attributes
A tag manager or a programmatic injection cannot always set attributes on the script
element. Define window.__clicklens
before the tag loads and it reads its config from there instead.
<script>
window.__clicklens = {
siteId: 'YOUR_SITE_KEY',
token: 'YOUR_BEACON_TOKEN',
endpoint: 'https://app.clicklens.io/api/v1/beacon',
autoConversion: false,
};
</script>
<script defer src="https://app.clicklens.io/t.js"></script>
A data attribute wins over the global config when both are present. The Google Tag Manager
template sets window.__clicklens
for you; see the GTM guide.
Consent and privacy controls
The tag honors visitor opt-out signals. When any of the signals below is present it stops collecting device-fingerprint surfaces — the canvas and WebGL hashes, the audio hash, and font and voice enumeration. The session beacon still fires: behavioural events and the automation checks that detect bots are unaffected, because fraud detection is a legitimate-interest function rather than cross-site tracking. Passive properties the browser sends on every request anyway (user agent, language, screen size) are unchanged.
| Signal | Source |
|---|---|
navigator.globalPrivacyControl === true | Global Privacy Control, set by the browser or a privacy extension. The current opt-out standard. |
window.__clicklens_consent === false | Explicit hook for your consent manager. Only an exact false opts out; unset or any other value counts as consent. |
navigator.doNotTrack === "1" | Legacy Do Not Track. Also matches the value "yes" and the older window.doNotTrack. |
To wire a consent manager, set window.__clicklens_consent
before the tag script runs:
<script>
// false until your CMP confirms the visitor has consented to fingerprinting
window.__clicklens_consent = false;
</script>
<script defer src="https://app.clicklens.io/t.js"></script>
The tag reads consent once, when it collects signals on load. Set the value before the tag
script executes. Changing it after the tag has fired does not retroactively suppress the
first beacon. To withhold all collection until the visitor decides, load
t.js only after your
consent banner returns a decision. With an opt-out active the beacon still appears in the
Network tab and returns 204;
the fingerprint fields are dropped before it is sent.
Content Security Policy
A strict Content-Security-Policy
blocks the tag unless you allow it explicitly, and nothing fails loudly: the script is
dropped, the console stays quiet unless you have CSP violation reporting wired up, and no
sessions ever reach your dashboard.
Two directives need an entry for your ClickLens host —
https://app.clicklens.io,
or your data-endpoint host
if you set a custom one:
-
script-src— to loadt.js, and to run the inline stub script that opens the snippet. Consent-gated mode (above) opens with a second inline script and needs the same treatment. -
connect-src— the tag's beacon is a POST to this host. Leave it out and the script still loads, but every beacon is blocked, which looks identical to the tag never having loaded.
An inline script needs a nonce or a hash to run under a strict policy;
'unsafe-inline' defeats
the point of having a CSP. A nonce has to be generated fresh on the server for every response
and set on both the header and the script tag's own
nonce attribute:
Content-Security-Policy:
script-src 'self' https://app.clicklens.io 'nonce-{YOUR_PER_REQUEST_NONCE}';
connect-src 'self' https://app.clicklens.io;
On a fully static page, with nowhere to generate a per-request nonce, use a hash instead. A
hash covers the exact text between the
<script> tags, so
it only matches that one script. The stub in the snippet above is a fixed, unchanging line,
so this hash is stable across every install that pastes it unedited:
script-src 'self' https://app.clicklens.io 'sha256-QxMPZHVkkQkxoyH1h4mLabI4UROCeadNxxgcshOMODA='; Consent-gated mode opens with its
own inline script — the one-line, uncommented
window.__clicklens_consent=false;
the dashboard configurator generates when you turn that toggle on — and needs a second
hash:
'sha256-lWBzrATHXghIfhmyILYxJQ9CCTASlfwFpk5fa0ZgrDg=' Both hashes cover the exact snippet the dashboard generates. If you hand-write your own consent line — different formatting, an added comment, extra whitespace — it is a different string and needs its own hash, computed from your own text. A nonce has no such fragility, so prefer it wherever your stack can generate one per response.
What happens after install
-
On the first page view the tag creates a session id and stores it in
sessionStorage, so the same browser tab keeps one session across page navigations. - It collects immediate signals (canvas and WebGL fingerprint, hardware and locale markers, automation tells) and tracks behavioural events — pointer movement, scrolling, keystroke cadence — in the page. The fingerprint surfaces are skipped for visitors who have opted out (see Consent and privacy controls).
- It sends an initial beacon a few seconds after load and a final beacon when the page is hidden or unloaded, signed with your beacon token. The scoring engine classifies the session as human, suspect, or bot.
- The session appears in your dashboard. Nothing on your page changes: the tag never blocks, redirects, or alters a visitor's experience.
Verify the install
- Open your site in a normal browser tab so the tag fires a real session.
- In the dashboard, the onboarding install step shows “Tag is working” with a session count within a minute. New sessions also appear on your dashboard's sessions list.
-
In your browser's developer tools, the Network tab shows a request to
/api/v1/beaconreturning204.
No sessions after a minute? Check these in order:
- The tag is on the domain you registered in ClickLens, not a staging or preview host.
- An ad blocker or privacy extension is not blocking the request. Test in a private window with extensions off.
- The snippet sits before the closing
</head>tag and the page actually loads it. -
The script tag carries no
integrityattribute. If an older snippet left one there, the browser console reports a Subresource Integrity error and runs nothing — delete the attribute.
Next steps
- Report sales and signups so the engine scores the conversion, not only the click: conversion tracking.
- Understand what the tag collects and our privacy posture: tag security.
-
Running a strict
Content-Security-Policy? See Content Security Policy above. - Platform-specific installs: WordPress, Shopify, Google Tag Manager, WooCommerce.