Tracker Script Reference
Every option the Attribi tracking script accepts — restricting tracking to acquisition pages on a single-page app, stopping your login form creating leads, capturing OAuth sign-ups, and checking the tracker from the console.
Every option the Attribi tracking script accepts, what it does, and when you need it.
The script tag Attribi generates for you already contains everything required. The attributes below are for the cases the default cannot know about — a single-page app, a login form, a staging environment.
The script tag
<script src="https://track.yourdomain.com/tracker.js"
data-site-id="YOUR_SITE_ID"
data-api-key="YOUR_API_KEY"
defer></script>
Place it before </head> on every page, not just your landing page — a visitor's
journey usually spans several pages, and that journey is the thing being measured.
Using Next.js App Router? There is no </head> to paste into; it is generated from
the metadata export. Render the script from your root layout with next/script and
strategy="afterInteractive" instead. Top of <body> is equivalent for a defer script.
Required
| Attribute | What it is |
|---|---|
data-site-id | Identifies your site. |
data-api-key | Public by design — it ships in a <script> tag on your website, so it is visible to anyone. It is not a secret. Requests carrying it are only accepted from origins registered to your site, so it cannot be used from anywhere else. |
data-only-paths — restrict which pages are tracked
You need this if your site is a single-page app (React, Next.js, Vue, Svelte).
data-only-paths="/,/pricing,/blog"
Only those paths record pageviews. Absent or empty means every path is tracked.
Why a single-page app needs it
The tracker follows client-side navigation, which it must do to see a journey that never reloads the page. The mechanism it uses is global and outlives the component that loaded the script — so if you render the tag once in a root layout, it keeps recording pageviews for every route the visitor reaches afterwards, including your signed-in application.
Three consequences, and the second is the one that quietly damages your data:
- Privacy. You would be recording your customers' in-product activity as marketing data.
- Attribution is corrupted. Application routes carry no campaign parameters, so a visitor who arrives from a campaign and then moves into your app lands an unattributed touchpoint there — which becomes their last touch, in place of the campaign that actually brought them.
- Cost. In-app navigation is the highest-volume, lowest-value traffic on your site, and it counts against your plan's monthly event allowance.
Choosing the paths — the rule that matters
List your acquisition pages, not merely your public pages.
The two are not the same, and the difference is where people go wrong.
Do not include /login, /logout, or OAuth callback pages, even though they are
public. They are not acquisition — they are where existing users re-enter. A Google
sign-in that returns to a tracked page books a touchpoint sourced from
accounts.google.com, which looks like Google referral traffic in your reports. Left in
place, you accumulate a fictional acquisition channel that grows with retention rather
than marketing, and it will read as one of your best-performing sources.
Matching rules
/matches the homepage exactly. It is not a prefix, or the list would mean nothing./blogmatches/blogand/blog/any-post, but not/blogroll.- Whitespace around entries is ignored.
Pageviews only. Conversions are never suppressed by this list — losing a real lead
because it happened on an unlisted page would be far worse than an extra pageview. To
scope conversions, use data-convert-exclude-paths below.
data-convert-exclude-paths — stop capturing leads on certain pages
data-convert-exclude-paths="/login,/account/security"
Automatic conversion capture watches every email field on your site. That is what makes ordinary contact and sign-up forms work with no code — and it is also why your login form can create a lead every time an existing customer signs in.
That matters beyond a wrong number: leads are uploaded to your ad platforms as conversions, so it teaches their bidding algorithms to optimise toward people who already have accounts — real budget aimed at your worst-performing audience, while conversion volume goes up and reads as success.
This is a denylist, not an allowlist: anything not listed is still captured. A forgotten entry costs you an extra lead, never a missing one.
You may not need it
Attribi also skips forms that the HTML autocomplete contract marks as sign-in:
| Field | Meaning | Captured? |
|---|---|---|
autocomplete="current-password" | an existing credential — sign-in | ❌ no |
autocomplete="new-password" | a credential being created — sign-up | ✅ yes |
| neither | not evidence either way | ✅ yes |
Most auth libraries set these correctly, so your login form is often already excluded.
To check: open your login page with DevTools → Network filtered to your tracking
domain, type an email into the field and click away. If no POST /api/convert appears,
you are already covered.
A skipped capture logs its reason once to the console:
MTA: conversion capture skipped — form looks like a sign-in (autocomplete="current-password").
data-capture-signin-forms — turn the sign-in heuristic off
data-capture-signin-forms="true"
Only needed if the heuristic above is wrong for you — for example a sign-up form
mistakenly marked current-password, whose sign-ups would otherwise never be captured.
Disables the heuristic only. data-convert-exclude-paths still applies, because a
denylist can suppress but never un-suppress.
Capturing conversions
A conversion is what creates a lead and attaches the visitor's whole journey to it. Without one you have anonymous traffic and nothing to attribute.
Automatic — no code required
When a visitor types a valid email and the field loses focus, Attribi reads the
surrounding form and captures email, phone and name together. Field matching is
case-insensitive across type, name, id, placeholder and autocomplete, and names
are also assembled from separate first/last fields.
This works on React and on embedded form builders (HubSpot, Marketo, Pardot), because the tracker also watches for forms added to the page after it loads.
Note it fires on blur, not on submit. Someone who types their email and clicks away without submitting is still recorded. That is intentional — it captures form abandoners — but it means your lead count includes people who never pressed the button.
Manual — window.MTA.convert()
Automatic capture needs an email field to observe. Call it yourself when there isn't one:
window.MTA?.convert({
email: user.email,
name: user.name, // optional
phone: user.phone, // optional
})
Required in three cases:
- Sign-up with Google, Apple or another social login — no email field exists on the page
- Forms inside a cross-origin iframe (Typeform, Calendly, HubSpot iframe mode)
- Multi-step flows where the account is only created at the final step
Two rules:
Fire it on account creation only — never on every authenticated page render, or every returning user becomes a new lead and is uploaded to your ad platforms as a conversion.
Never call it on a URL carrying a token or an email — magic-link, password-reset and email-confirmation landing pages. Worth checking where your auth provider's confirmation and recovery emails actually land, since that is easy to overlook.
Checking the tracker from the console
window.MTA.lastStatus
// → { endpoint: "pageview", status: 200, reason: "ok", at: "…" }
reason is the field to read.
reason | Meaning |
|---|---|
ok | Recorded. |
rejected: origin not allowed | The page's origin is not registered to this site. Add it under Settings → Site & Tracker → Staging & development origins. |
plan_limit_reached | Your monthly event allowance is used up. Requests still return success so your site never errors, but events are not recorded. |
rate limited | Too many requests in a short window. |
rejected: invalid API key | data-api-key does not match this site. |
network failure | The request never reached us — commonly an ad blocker, or an unregistered origin whose rejection the browser will not let us read. |
Testing before you go live
By default only your registered production domain may send tracking data, so localhost
and preview deployments are refused. Register them under Settings → Site & Tracker →
Staging & development origins — full origins including scheme and port, e.g.
http://localhost:3000.