Skip to content

Troubleshooting

POST /ingest always answers 204, whether an event was accepted or dropped, so there is no error to read in the Network tab. Start every “no data showing up” investigation with GET /v1/validate.

  1. data-domain doesn’t match any trusted domain (typo, or the domain was never added).
  2. Page origin doesn’t match a trusted domain. Usually a www. vs bare-domain mismatch, a staging subdomain nobody added, or file:///opaque origins.
  3. Local development without the project’s “Allow local origins” toggle or an explicit localhost domain. See Localhost & development.
  4. A Content-Security-Policy blocking the script or the API origin.
  5. An adblocker blocking cdn.analytics.canarycoders.es or api.analytics.canarycoders.es.
  6. The project’s daily event cap has been reached.
  7. Your browser was flagged as internal traffic (window.lytics.internal(true)): those events are stored, but hidden from stats until you ask for them with includeInternal.
  8. A hand-rolled ingest request with an invalid body (bad JSON, non-UUID sessionId, an event name or props that fail validation). The snippet never produces these; custom clients can.

Causes 1 to 3 are exactly what /v1/validate catches. The rest are invisible to it, so see what /v1/validate can’t see.

Call it with the exact domain/origin pair a real visitor’s browser would send. Pull them off the actual page (location.hostname for domain, location.origin for origin) rather than guessing:

Terminal window
curl "https://api.analytics.canarycoders.es/v1/validate?domain=myapp.com&origin=https://staging.myapp.com" \
-H "Authorization: Bearer cly_live_5f3a9c2e1b7d4a80f6c3e9d2a1b4c7e0"
{
"domain": "myapp.com",
"origin": "https://staging.myapp.com",
"wouldAccept": true,
"checks": {
"domainOwnedByProject": true,
"originPresent": true,
"originParseable": true,
"originIsLocal": false,
"allowLocalOrigins": false,
"originTrusted": true
},
"reasons": []
}

Each checks field mirrors one step of the real /ingest logic:

Field Fails when
domainOwnedByProject domain doesn’t match any of this project’s trusted domains. Matching is on label boundaries, so evil-myapp.com never matches myapp.com.
originPresent No origin was supplied to the check. This mirrors a request with no Origin header, which is always dropped.
originParseable origin isn’t a valid URL (it should look like https://app.example.com).
originIsLocal Informational only. true when the origin’s host is localhost, 127.0.0.1, or *.localhost.
originTrusted The origin’s host doesn’t pass the trust rule for its kind: local origins need allowLocalOrigins (or localhost explicitly trusted); everything else needs a label-boundary match against a trusted domain.

wouldAccept is domainOwnedByProject && originTrusted. When it’s false, reasons spells out which checks failed in plain English, so you don’t have to reverse-engineer the table by hand.

It is a pure server-side check of the domain and Origin acceptance rules. It cannot tell you whether a request ever reached the server, whether the body it carried was valid, or whether events landed but are hidden from the default stats view.

A CSP that doesn’t allow the snippet or the API blocks the request in the browser before it is sent, so nothing shows up server-side to validate against. Add both origins:

Content-Security-Policy: script-src 'self' https://cdn.analytics.canarycoders.es; connect-src 'self' https://api.analytics.canarycoders.es;

Ad and tracker blockers may block lytics.js itself or the /ingest request by URL pattern. This is also invisible to /v1/validate, since the check runs against a hypothetical request you provide rather than observing what actually left a real visitor’s browser.

Once a project’s daily event count exceeds its cap, further events are dropped silently: same 204, no signal to the client. Check current usage against the cap on the dashboard’s Settings page. This one isn’t exposed via /v1 or the SDK, only the dashboard UI.

Events tagged as internal traffic are accepted and stored, then excluded from every default stats view. If you ran window.lytics.internal(true) in a browser and forgot, that browser’s visits won’t show up. Turn the flag off with window.lytics.internal(false), or query with includeInternal=true to see the hidden sessions.

/ingest silently drops requests whose body fails validation (bad JSON, a non-UUID sessionId, a disallowed event name, oversized props): same 204 as every other drop. /v1/validate only checks the domain/origin pair, never a body. The official snippet always sends valid bodies, so this only bites hand-rolled ingest clients; compare your payload against the wire format.

Confirm GET /v1/validate with the exact domain/origin pair a real visitor’s browser would send. A mismatch there is the only failure mode this endpoint is guaranteed to catch. Everything else on this page fails client-side, before the server ever sees a request.