# Fix tracking that is not working

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](#no-events-at-all) |
| A hostname you did not expect | [Events from an unexpected hostname](#events-from-an-unexpected-hostname) |
| A domain stuck on `Waiting for events` | [The install checklist never turns green](#the-install-checklist-never-turns-green) |
| `Tracking domain` will not verify | [The tracking host stays unverified](#the-tracking-host-stays-unverified) |
| Your backend gets an error | [The events route rejects the request](#the-events-route-rejects-the-request) |
| Money accepted but not visible | [Revenue is not showing](#revenue-is-not-showing) |
| Someone stays `Anonymous` | [A person is not identified](#a-person-is-not-identified) |
| Google shows no conversions | [Google Ads conversions are not arriving](#google-ads-conversions-are-not-arriving) |

## No events at all

`Live` still says it is waiting for your first event.

1. **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.
2. **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.
3. **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.
4. **Is a content blocker stopping it?** Try another browser, or one with no extensions. Blocked events never leave the page.
5. **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](/acquire/tracking/install).

## Events from an unexpected hostname

Or: `Also sending events` shows a hostname you never registered.

1. **Compare the two spellings.** `acme.com` and `www.acme.com` are two different hostnames, and `www` is never stripped.
2. **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](/acquire/tracking/domains).

## The install checklist never turns green

A domain sits on `Waiting for events`.

1. **Load a page on that domain yourself.** Verification is driven by real traffic; a site nobody visits never turns green.
2. **Check the spelling** against `Also sending events`, as above.
3. **Check the snippet is on that domain's pages**, and that the site is deployed.
4. **Wait for the page to recheck.** It refreshes about every ten seconds.
5. **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`.

1. **Publish the records exactly as shown**, at your DNS provider, against the host you claimed.
2. **Press `Check now`** as soon as your records are out, instead of waiting for the next automatic check.
3. **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.
4. **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.
5. **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 to `Active`.

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](/acquire/tracking/revenue) and the [Tracking API reference](/reference/openapi/tracking).

## Revenue is not showing

The money is accepted but does not appear where you expect it.

1. **Check where you are looking.** A `payment` shows in the person's journey at once, but the `Lifetime value` tile is recomputed on a schedule and can take up to an hour.
2. **Check the person is identified.** Money reaches a person through the `email` — or an `anonymous_id` you captured in the browser — on the revenue event. With neither, the payment is stored but belongs to nobody: no `Customer`, no lifetime value, and it lands in `Unattributed` in the reports.
3. **Check the amount unit.** `amount_minor` is a whole number of minor units. A payment sent as `49` when you meant 49.00 EUR is stored as 0.49 EUR and reads as a plausible small number everywhere.
4. **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.
5. **For the reports specifically, check the conversion.** `Revenue by source` counts people whose first paid conversion falls in the date range, and a subscription only exists when you send `subscription_started` with a `subscription_ref` in `props`. Payments alone give a person money and `Customer` status but no conversion for the cohort to select.
6. **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.

1. **Check that something told Tracking who they are.** Identity comes from a form the visitor submitted, from your own `lt('identify', …)` call, or from the `email` on a backend event. It is never taken from the properties of a `track` call.
2. **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 `identify` yourself.
3. **Check the consent state.** In cookieless mode form capture is not installed and an `identify` call is dropped whole. **A declining visitor cannot become a person, by design.**
4. **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.
5. **Call `identify` again 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](/acquire/tracking/identity).

## Google Ads conversions are not arriving

You turned on conversion feedback but Google shows nothing.

1. **Check the connection.** `Conversions` shows a connect screen instead of its settings whenever the account is not connected.
2. **Check the master switch and the rules.** Feedback is off by default, and a rule has to be enabled as well.
3. **Run one pass manually** with `Run now` instead of waiting for the daily run.
4. **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 found` and `no value yet` are the two usual answers, and neither is silent.
5. **If it is no click id: check paid tracking health and auto-tagging.** Without Google's auto-tagging there is no `gclid`, and without a `gclid` a conversion cannot be uploaded. Visitors who declined consent carry no click id either.
6. **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.
7. **Give Google time.** Offline conversions can take hours to appear on their side.

See [Close the loop on Google Ads](/acquire/tracking/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](/acquire/tracking/reports): what each number in a report actually counts.
- [Register the domains you track](/acquire/tracking/domains): the three domain lists and how each is verified.
- [What tracking does with a consent answer](/privacy/tracking-consent): why a declining visitor never becomes a person.
- [Look up a setting](/acquire/tracking/settings): every default and every limit in one place.
