Installing the script
Coming soon — not open for wide use yet. Installing the tracking script on your own website isn't generally available. Tracking currently runs only on pages and forms published from the app, where it's included automatically and there is nothing to install. Keep this page for when the feature opens up; get in touch if you'd like early access.
This page is for whoever manages your website. It's the one place in this documentation with code in it. If that's not you, send them this link — they won't need anything else.
The snippet
Add this to every page you want tracked, ideally in the <head>:
<script defer
src="https://your-tracking-host/s/thoughtfuly.js"
data-site-key="YOUR_SITE_KEY"></script>
Your site key and tracking host are provisioned for your account rather than being self-service — ask your account administrator for them if you don't already have them. The site key is not a secret: it sits in your page source, and it only identifies which account the data belongs to.
That's the whole installation. Page views start immediately.
If your site is a web artifact published from this platform, the script is already there. Nothing to do.
Options
All configuration is done with attributes on the script tag.
| Attribute | Default | What it does |
|---|---|---|
data-site-key | — | Required. Identifies your account. |
data-require-consent | false | Wait for consent before tracking anything. Set this if you use your own cookie banner. |
data-consent-ui | off | banner or modal to show the built-in consent prompt. Implies data-require-consent. |
data-capture-on-change | false | Capture an email as it's typed, not only on submit. See the warning below. |
data-cookie-domain | auto | Override the cookie domain. Only needed for unusual setups. |
About data-require-consent
Without it, tracking starts on page load (do-not-track is still honoured — that can't be
switched off). With it, nothing is stored or sent until something calls
thoughtfuly.consent.grant().
⚠️ If you use your own cookie banner, you must set this. Otherwise the script tracks
immediately and your banner's "decline" arrives too late to have prevented anything.
Setting data-consent-ui turns it on for you, since showing a banner and tracking anyway
would be incoherent.
About data-capture-on-change
Off by default, on purpose. Turning it on captures an email address as soon as the visitor types a valid one — including addresses they then delete and never submit. That is a meaningfully different privacy proposition from capturing on submit, and in several jurisdictions a legally different one. Turn it on knowingly, or leave it alone.
About data-cookie-domain
The script works out the right cookie domain automatically so that
example.com, www.example.com and shop.example.com share one visitor.
Set this only if that detection gets it wrong — for example on an internal
hostname with an unusual suffix. Use a leading dot: data-cookie-domain=".example.com".
The JavaScript API
The script exposes a single global, thoughtfuly. Nothing else is added to
window.
thoughtfuly.page() // send a page view manually
thoughtfuly.track(name, props) // send a custom event
thoughtfuly.consent.status() // "granted" | "denied" | "unknown"
thoughtfuly.consent.grant() // allow tracking; flushes anything queued
thoughtfuly.consent.deny() // stop tracking; clears stored identifiers
thoughtfuly.consent.reset() // back to undecided
thoughtfuly.consent.onChange(fn) // called whenever the state changes
Custom events
thoughtfuly.track("signup_started", { plan: "pro" });
Event names must be 1–64 characters, letters, numbers, underscore or hyphen — no spaces or dots. Invalid names are rejected with a console warning rather than silently dropped, so you'll notice during development.
Single-page apps
Route changes are detected automatically, including pushState navigation, and
a page view is sent when the path changes. Query-string-only changes don't
fire one.
If your router does something unusual and views aren't appearing, call
thoughtfuly.page() yourself after each navigation.
Consent integration
If you already run a cookie banner, wire it to these two calls. The pattern is the same whatever product you use:
// When the visitor accepts your analytics category:
thoughtfuly.consent.grant();
// When they decline, or withdraw a previous acceptance:
thoughtfuly.consent.deny();
A typical integration, using whatever event your banner emits:
myConsentBanner.on("change", (categories) => {
categories.analytics
? thoughtfuly.consent.grant()
: thoughtfuly.consent.deny();
});
Until grant() is called, nothing is stored and nothing is sent — page views
that occur while the visitor is deciding are held in memory and flushed on
grant(), or discarded on deny(). Setting data-consent-ui is unnecessary
if you're doing this; use one or the other, not both.
See Consent for the behaviour in full.
Things worth knowing
It won't break your site. Every entry point is wrapped. If the tracking host is unreachable, slow, or returns errors, the script fails quietly and your page carries on. It never throws into your code.
It adds one global. window.thoughtfuly, and nothing else. If that name
already exists, the script logs a warning and does nothing rather than
overwrite it.
Calls are safe before load. The API is available as soon as the script runs; you don't need to wait for a ready event.
Content Security Policy. If you run a CSP, allow your tracking host in both
script-src (to load the script) and connect-src (to send data).
Checking it works
- Load a tracked page with your browser's network tab open.
- Look for a request to
/ton your tracking host. It should return200. - Check cookies for the page —
_tfy_vkshould be set. - Submit a form containing an email address; you should see a request to
/i, and_tfy_ihshould appear. - Navigate to a subdomain of the same site —
_tfy_vkshould be unchanged.
If step 5 shows a different value, the cookie domain was detected incorrectly.
Set data-cookie-domain explicitly.
Related
- Overview — what web tracking does and why.
- Cookies and privacy — exactly what's stored.
- Consent — consent behaviour in detail.