Debugging
Events not showing up? Work down three checks, in order: is the tracker running, are requests going out, did they arrive. Most problems show up at the first one.
Updated
1. Is the tracker running?
Open your site on one of its listed domains, then the browser’s developer console:
// In the browser console, on your site
typeof window.clickclacks?.optOut
// 'function' → the tracker is running
// 'undefined' → it hasn't started (see below)
window.clickclacks?.q
// An array → your queue stub is there, but c.js hasn't started yet
localStorage.getItem('cco')
// '1' → this browser opted out; run clickclacks('optIn') to undoIf it hasn’t started, one of these is true:
- The script didn’t load. An ad blocker, a privacy extension or your Content Security Policy blocked
c.js. Check the Network tab for it. - The hostname isn’t listed. The page’s hostname must match an entry in
data-domainsexactly:www.acme.comandacme.comare different. data-keyis missing, or was stripped by whatever inserted the tag. Compare the tag in the Elements panel with the one on the source’s page.- Storage is blocked. The tracker needs local and session storage, and stays off quietly where the browser refuses them.
2. Are requests going out?
In the Network tab, filter for ingest (or /e on a custom domain). Events are batched, so a request goes out every 5 seconds, as soon as 25 events are waiting, and when you switch tab.
POST https://app.clickclacks.io/api/ingest?key=pk_live_3f9a1c7e5b2d4f6a8c0e1b3d
Content-Type: text/plain
HTTP/1.1 202 Accepted
{"accepted": 3}| Response | What it means |
|---|---|
202 {"accepted": n} | Received. n can be 0 when every event was filtered out: a known bot’s user agent, or clicks with click autocapture off. |
403 | Refused. The page’s origin isn’t in the source’s saved domains, or the key is wrong. Check the hostname in the address bar against the source’s domains. |
400 | The request body wasn’t valid. The tracker never sends one; look for a second, modified copy of it. |
410 | Your custom domain has been live for more than 30 days, and this page still loads c.js from app.clickclacks.io. Update its snippet. |
503 | Temporarily unavailable. The tracker keeps the events and retries with the next batch. |
3. Did they arrive?
- Realtime shows who is on your site now and every event as it arrives, over the last 30 minutes. Filter it to one source.
- Events, under Data, lists every event with its properties. Search for a custom event by its exact name.
- Sources shows each source’s health: Waiting for events until the first one, Receiving when the last event was under 30 minutes ago, Quiet after that, and Stopped sending after 24 hours without one.
Reports such as Overview and Insights fill in as traffic arrives, usually within a minute.
Common problems
Nothing from my own browser
Usually an ad blocker or privacy extension. Try a private window with extensions off. If you opted out while testing a consent banner, that choice is remembered: run clickclacks('optIn') or clear the site’s data.
Nothing from localhost
localhost isn’t one of your listed domains, so the tracker stays off there, which also keeps development traffic out of your data. Test on a deployed preview whose hostname you’ve added to the source.
Pageviews arrive but no clicks
Check Autocapture clicks is on in the source’s settings. The tracker also turns click capture off for a page when it can’t reach ClickClacks for its settings, which a Content Security Policy without app.clickclacks.io in connect-src causes.
A custom event never arrives
Invalid calls are ignored without an error. Check that the name is 1 to 128 characters and doesn’t start with $, and that properties are a plain object (not null, an array or a class instance). If the call runs before c.js loads, add the queue stub or it is skipped. An event whose properties are over 2 KB arrives without them.
Every pageview counted twice
The tag is on the page twice: in the HTML and again from a tag manager, a plugin or a component. Keep one.
Every page shows the same path
Your app routes with the URL hash (/#/settings), which is never sent. See hash routers.
One person shows up as several
Call identify on every domain where they’re signed in, use the same ID everywhere (including your backend), and call reset on sign-out. Links between your domains carry identity only when both hostnames are listed; the carry-over rate on Sources › Identity tells you how often it works.
Server-side events
Send the batch with ?validate=true: the response shows every item exactly as it would be stored, with every error, and nothing is stored. Error responses carry a stable code and a link to its explanation. See dry runs and errors.
Next steps
- Install the tracker: placement in every framework.
- Domains and identity: listing hostnames.
- Limits: sizes and rates in one table.