Verify and troubleshoot
Prove your tags are installed and your conversions arrive, and find the reason when they do not.
Tracking fails quietly. A tag that never loads, a conversion that never arrives and a visitor who refused cookies all look the same from inside your app: nothing happens, and no error is raised anywhere. This page is how you tell them apart.
Prove the tags are installed
Open your app with devtools on the Network tab.
- Our configuration arrives. Look for a request to
/api/v1/public/<org>/tracking. It should answer200with your scripts. Empty means nothing is attached to this app's auth client - check Auth › Clients, not the tag library. - The vendor's script loads.
googletagmanager.com/gtag/js,connect.facebook.net/en_US/fbevents.js, and so on. A request that never appears is usually the Content-Security-Policy; see Providers. - Consent comes first. In the console,
window.dataLayer[0]should be["consent","default",{…all denied…}], before any Google tag. - What we think is installed: run
window.__buildbase.trackingin the console. It lists every script the SDK actually mounted, and the app it mounted for.
Prove the events arrive
| Vendor | Where to look | How to get there |
|---|---|---|
| GA4 | Admin › DebugView, or Reports › Realtime | Install the Google Analytics Debugger extension, or pass debug: true |
| GTM | Preview | Your container's Preview button - our events appear as data layer pushes |
| Meta | Events Manager › Test events | The Meta Pixel Helper extension for the browser side |
| Others | Each vendor's own live-events view | - |
Warning
Checking GA4 in the Network tab takes two filters, not one. GA4 collects
on both www.google-analytics.com/g/collect and
analytics.google.com/g/collect, and which one a page uses depends on its
configuration - a property linked to Google Ads commonly uses the second.
Filtering for the first alone shows an empty list while every hit is leaving
normally. Filter on /collect instead.
Two more things make a correct GA4 hit look like a missing one. It batches:
several events queued together go in one POST body, so only the first
event name appears in the query string and the rest are invisible unless you
open the request. And its hits are sent as beacons, so a hit fired next to a
navigation is often reported as (canceled) or ERR_ABORTED in devtools -
which means the browser stopped waiting for a reply it never needed, not that
the event failed to send.
Set tracking={{ debug: true }} and the SDK logs every decision it makes to the console: what it installed, what it refused, what it fired and what it handed to your own callback.
<SaaSOSProvider tracking={{ debug: true, onEvent: (e) => console.log(e) }}>For the server-side half, Settings › Tracking & Tags › Send a test conversion shows what the vendor said, and the log below it lists every send with its reason. For GA4 the test also sends a live buildbase_test event, which carries no revenue and appears in DebugView - that is the only way to prove an API secret works, because GA4 answers 204 to a wrong one.
When something is wrong
No events at all
- Nothing is attached to this app. A script in the library loads nowhere until it is ticked on the auth client under Auth › Clients.
- The id is missing. A row with no measurement id or pixel id is a placeholder: it is deliberately not served, not installed and not named in your consent banner.
- The visitor has not answered. Marketing tags do not mount until consent is given. Analytics tags load in a denied state and send cookieless pings, which is Consent Mode working, not failing.
- Your CSP blocks it. The browser console says so once, as a
Refused to loadline.
Events arrive, but conversions do not attribute
- The click id never reached the sale. It is captured on the landing page and kept in a cookie on your registrable domain. If your landing page and your app are different registrable domains -
abc.comandabc.io- nothing carries across, and no ad platform will attribute the sale. - The buyer refused marketing. Google's tags still count the visit; the ad network does not get the conversion. That is the correct outcome, not a bug.
- You are looking at a new user. A GA4 Measurement Protocol hit sent without the browser's client id starts a fresh user. We refuse to send one rather than do that - it appears in the log as
no GA4 client id for this buyer.
The same sale counted twice
Every event carries one id, shared by the browser and the server, which is what Meta deduplicates on. GA4 has no such mechanism, so we never send it a purchase your browser already reported. If you are seeing doubles:
- You also have your own pixel on the page. We refuse to install a tag whose id is already present, and say so in the debug log - but a second pixel with a different id is a second pixel, and both will count.
- You fire the event yourself as well. Set that event to
manualso only one of you sends it. See Events. - A renewal reused the sale's id. If you send conversions from your own backend, a renewal needs an id of its own; reusing
tracking.eventIdmakes every renewal look like the original sale.
The numbers do not match the ad platform
They never match exactly, and the gaps have names: ad blockers drop browser hits, attribution windows differ, and an ad platform counts a conversion against the click's date while your database counts it against today. Compare trends, not totals, and use the server-side send to narrow the first gap.
Our own visits show up
Tracking runs in development too, because a tag that only exists in production is a tag nobody has tested. Set tracking={{ enabled: false }} where you do not want it, or exclude your own traffic in the vendor.
Next
- Events - what fires, and what you send yourself.
- Server-side conversions - the delivery log and its reasons.
- Providers - ids, CSP and per-vendor traps.