Isotherm
Docs

Install

One async script tag, once, in a shared layout. The hard part is not the tag — it is putting it somewhere that renders on every page you care about and nowhere you do not.

Add a site in the dashboard, list the domains the key may be used from, then paste this before </body>:

<script async src="https://isotherm.app/isotherm.js" data-site="pk_live_xxx"></script>

The key is not a secret. It is visible in the page source of every tracked page by design. What actually authorizes recording is the Origin allowlist: a browser cannot forge the Origin header, so a key copied out of your HTML is useless on any other domain. Do not put it in an environment variable.

Where it goes

Laravel (Blade)

The shared layout, usually resources/views/layouts/app.blade.php. If the project has several layouts — an auth screen, a marketing page — add it only to the ones whose pages you actually want recorded. Put it in a @stack only if you are certain that stack is flushed on every page: a stack nobody pushes to produces no tag and no error.

Filament

Filament owns its own layout, so register a render hook in a service provider instead:

use Filament\Support\Facades\FilamentView;
use Filament\View\PanelsRenderHook;

FilamentView::registerRenderHook(
    PanelsRenderHook::BODY_END,
    fn (): string => Blade::render('<script async src="..." data-site="..."></script>'),
);

BODY_END, not HEAD_END — the tracker must load after the DOM exists.

Nova

Nova is a SPA, so a Blade tag would only run on the first load. Register it from NovaServiceProvider::boot() with Nova::script(). Client-side routing is already handled: the SDK patches history.pushState and files each route as its own page.

Livewire and Inertia

Nothing special. Both swap the DOM without a page load; the SDK listens on the document in the capture phase and patches the history API, so it keeps seeing clicks on elements that were replaced.

One caveat worth knowing on Livewire-heavy pages: constant DOM updates suppress dead-click detection, because Isotherm treats "the DOM changed after the click" as evidence the click did something. You will see fewer dead clicks. That is a deliberate bias toward false negatives — a missed dead click costs nothing, a false one sends someone hunting a bug that does not exist.

WordPress

Do not paste it into a theme template; a theme update will erase it. Enqueue it from a child theme or a one-file mu-plugin, in the footer.

Verify it is actually recording

Do not trust a green checkmark anywhere, including your own:

  1. Load a tracked page and confirm isotherm.js returns 200 in the Network tab.
  2. Click something, wait about six seconds, and confirm a POST to /api/v1/collect returns 202.
  3. A 403 means the page's origin is not on that site's allowlist. Add the domain in the dashboard — do not change the key.

Pinning a version

/isotherm.js is the floating latest, cached for five minutes, so a fix reaches every site without anyone redeploying. If you would rather not have a script change underneath you, pin the immutable build — it is byte-identical forever and cached for a year:

<script async src="https://isotherm.app/isotherm-0.2.0.js" data-site="pk_live_xxx"></script>

There is also /isotherm.debug.js — the same code unminified, with a source map, for when a stack trace into S(e,t) is not helping anyone.

Options

All optional, set as data-* attributes on the tag.

AttributeDefaultWhat it does
data-siteRequired. The site's public key.
data-move1Record mouse movement. 0 cuts payloads sharply.
data-sample1Fraction of sessions to record. Decided once per session, so a recorded visitor stays recorded for the whole visit.
data-grid16Movement quantization in px. Bigger is smaller and coarser.
data-strip-query0Treat ?page=2 and ?page=3 as one page.
data-text1Capture text of clicked elements, and keep text in page copies. See Privacy.
data-dnt1Honour navigator.doNotTrack.
data-debug0Log to the console.