Tracking
Fix tracking that is not working
Symptom by symptom: no events, a domain that never turns green, a rejected request, missing revenue, an anonymous person, absent conversions.
Find your symptom, then work down its list. The checks are ordered by how often they turn out to be the answer.
| Symptom | Go to |
|---|---|
| Nothing arrives at all | No events at all |
| A hostname you did not expect | Events from an unexpected hostname |
A domain stuck on Waiting for events | The install checklist never turns green |
Tracking domain will not verify | The tracking host stays unverified |
| Your backend gets an error | The events route rejects the request |
| Money accepted but not visible | Revenue is not showing |
Someone stays Anonymous | A person is not identified |
| Google shows no conversions | Google Ads conversions are not arriving |
No events at all
Live still says it is waiting for your first event.
- Is the snippet on the page you loaded? Open the page in a browser, view its source and look for the script tag. It has to be in the
<head>of the page you actually loaded, not only on the home page. - Did you deploy? A snippet pasted into a builder or a template does nothing until the site is published. This is the single most common cause.
- Is the snippet gated behind your own consent check? If your code only renders the tag after someone accepts, everybody who declines or never answers sends nothing. Load the tag unconditionally — the script handles consent itself.
- Is a content blocker stopping it? Try another browser, or one with no extensions. Blocked events never leave the page.
- Are you using the minimal variant with
lt(…)calls of your own? A call made before the script has loaded throws in your page and the event is lost. Use the loader snippet — see Install the tracking script.
Events from an unexpected hostname
Or: Also sending events shows a hostname you never registered.
- Compare the two spellings.
acme.comandwww.acme.comare two different hostnames, andwwwis never stripped. - Add the one visitors actually use with
Add to checklist, and remove the one that never sends events.
Registering a hostname changes no collection: events are stored from whatever host runs your script, listed or not. The checklist is a report, not a filter — see Register the domains you track.
The install checklist never turns green
A domain sits on Waiting for events.
- Load a page on that domain yourself. Verification is driven by real traffic; a site nobody visits never turns green.
- Check the spelling against
Also sending events, as above. - Check the snippet is on that domain’s pages, and that the site is deployed.
- Wait for the page to recheck. It refreshes about every ten seconds.
- Check how old the traffic is. A domain counts as seen if events arrived in the last 30 days. A domain that goes quiet for longer falls back to
Waiting for events— which is a true statement about your install.
One misconception worth clearing up. A visitor who declines consent does not stop a domain verifying. Their events still arrive — without cookies and without identifiers, but carrying the page address — so the hostname is seen. The only thing that sends nothing at all is a snippet your own code hid behind a consent check.
The tracking host stays unverified
The binding on Tracking domain reads Waiting for DNS to publish, or Verification failed.
- Publish the records exactly as shown, at your DNS provider, against the host you claimed.
- Press
Check nowas soon as your records are out, instead of waiting for the next automatic check. - Check what the host answers. The script is fetched over your host and JavaScript is required back. A redirect, a parked page, an error status or a holding page fails the check — a host that merely resolves is not enough.
- Read the reason on
Verification failed. It names what the last check got: a redirect, an HTTP status, a content type that was not JavaScript, or no answer. - Press
Retry. Automatic checks give up after 72 hours, and DNS changes can take that long by themselves, so failure often just means the two clocks did not line up. Retry as often as you like; the first check that passes flips the binding toActive.
After a binding goes active, copy the snippet again from Install. The script URL is inside the HTML you pasted.
The events route rejects the request
You call the events route and get an error instead of an accounting.
| What you get | What it means | Fix |
|---|---|---|
401 missing api key | No X-Api-Key header reached Tracking | Send the key in X-Api-Key, not Authorization. |
401 invalid api key | The key is unknown, rotated or revoked | Deploy the current secret. Rotation kills the old key instantly, with no overlap. |
403 api key does not match the product in the URL | The key belongs to another product | Use the endpoint copied from the same product’s API keys page. |
400 request body must be { "events": [ … ] } | The envelope is wrong | Wrap your events in an events array. |
400 events batch exceeds the 500-event limit | Too many events | Split the batch. |
413 | The body is over 64 KiB | Send fewer events per request. |
A success carrying entries in rejected is not a failure of the request — the other events were stored. Read the reason on each entry: the usual ones are a name outside the six lifecycle names, a payment or refund without amount_minor, an amount_minor without a currency, and a ts outside the writable window (more than 24 hours ahead or about 90 days back). See Send revenue from your backend and the Tracking API reference.
Revenue is not showing
The money is accepted but does not appear where you expect it.
- Check where you are looking. A
paymentshows in the person’s journey at once, but theLifetime valuetile is recomputed on a schedule and can take up to an hour. - Check the person is identified. Money reaches a person through the
email— or ananonymous_idyou captured in the browser — on the revenue event. With neither, the payment is stored but belongs to nobody: noCustomer, no lifetime value, and it lands inUnattributedin the reports. - Check the amount unit.
amount_minoris a whole number of minor units. A payment sent as49when you meant 49.00 EUR is stored as 0.49 EUR and reads as a plausible small number everywhere. - Check the currency. Nothing is ever summed across currencies, so revenue sent in a second currency appears as its own line rather than being added to the first.
- For the reports specifically, check the conversion.
Revenue by sourcecounts people whose first paid conversion falls in the date range, and a subscription only exists when you sendsubscription_startedwith asubscription_refinprops. Payments alone give a person money andCustomerstatus but no conversion for the cohort to select. - Check the date range. The range picks people by their first conversion, not payments by their date. A renewal from an older customer never appears in a recent range.
A person is not identified
They stay Anonymous on People when you expected an email address.
- Check that something told Tracking who they are. Identity comes from a form the visitor submitted, from your own
lt('identify', …)call, or from theemailon a backend event. It is never taken from the properties of atrackcall. - Check the form. An email is read from any field whose value looks like one, but a phone number only from a field that is clearly a phone field. Password and hidden fields are never read. If your sign-up does not use a plain form submit — a single-page app, a social login — call
identifyyourself. - Check the consent state. In cookieless mode form capture is not installed and an
identifycall is dropped whole. A declining visitor cannot become a person, by design. - Check the phone format. International form — a leading
+and 8 to 15 digits — is always accepted; other shapes may be ignored rather than risk merging two people. - Call
identifyagain on every page load where the visitor is signed in. Repeating a known identity costs nothing and repairs a lost cookie.
See Identify a visitor.
Google Ads conversions are not arriving
You turned on conversion feedback but Google shows nothing.
- Check the connection.
Conversionsshows a connect screen instead of its settings whenever the account is not connected. - Check the master switch and the rules. Feedback is off by default, and a rule has to be enabled as well.
- Run one pass manually with
Run nowinstead of waiting for the daily run. - Read the funnel on
Upload status. It says how many conversions were considered, how many were tied to a click, how many were uploaded, and where the rest went.No click id foundandno value yetare the two usual answers, and neither is silent. - If it is no click id: check paid tracking health and auto-tagging. Without Google’s auto-tagging there is no
gclid, and without agclida conversion cannot be uploaded. Visitors who declined consent carry no click id either. - If it is no value yet: the money has not landed. A revenue-valued rule waits for the revenue; those rows are picked up by a later run.
- Give Google time. Offline conversions can take hours to appear on their side.
See Close the loop on Google Ads.
Nothing here matches
Two places show you the raw truth before any report does:
Live— the events as they arrive, with the page, the source and the visitor behind each one.Events— every event name your sites have ever sent, with counts. A typo shows up here as a near-duplicate row with a small count.
If Live shows what you expect and a report does not, the question is about attribution, not delivery.
Next steps
- Read the attribution reports: what each number in a report actually counts.
- Register the domains you track: the three domain lists and how each is verified.
- What tracking does with a consent answer: why a declining visitor never becomes a person.
- Look up a setting: every default and every limit in one place.