Skip to main content
The SDK writes two kinds of console output:
  • Debug logs ([Wendung] ...): opt-in via debug: true, trace normal SDK activity.
  • Warnings ([Wendung - Warning] ...): always on, emitted when the SDK hits something unexpected.

Enable debug mode

With debug: true, the SDK logs:

Warning messages

Warnings are emitted regardless of the debug setting. This is the complete list. Invalid configuration makes init() throw instead of warning; see init() options.

Verifying your integration

After your first step() call, confirm events are flowing:
  1. Network tab. Filter for events in DevTools. A POST to the ingest endpoint appears within 5 seconds (sooner if the batch fills). A 2xx response means the batch was accepted.
  2. Dashboard. Your user appears in the Recent identities card on the Overview within a minute.
  3. Debug mode. debug: true shows the init log, queued events, and flushes in the console.
  4. Pageviews. With trackPageviews: true, the site appears on the Sites page within a minute of the first pageview, and the green badge next to its name counts visitors from the last five minutes.

Common issues

Most common causes, in order:
  1. init() wasn’t called, or ran after the first step(). Initialize at app startup.
  2. The apiKey is wrong or deactivated. Check Settings → API Keys in your dashboard.
  3. A 4xx response is dropping your batches. Look for [Wendung - Warning] Endpoint rejected batch batchId=... with status 401; dropping events.
  4. The origin isn’t allowed. A 403 means the page’s origin is missing from Settings → Allowed origins. A new project rejects every origin until you add one.
  5. An ad-blocker or privacy extension is blocking the request. Blocked requests show as (blocked) in the Network tab.
  6. Check Wendung.getConsent() in the console. denied stops all collection. pending drops custom events and identification; it sends only aggregate pageviews when trackPageviews is 'auto'. See the consent guide.
Every method except init() and getConsent() throws [Wendung] Call .init() first when the SDK isn’t initialized. Usual causes:
  • A module calls an SDK method at import time, before your entry file runs init().
  • In Next.js, a Server Component or Route Handler used the SDK. It is browser-only; move the call into a Client Component.
On visibilitychange (hidden) and pagehide, the SDK drains the whole queue with parallel fetch requests using keepalive, so unload delivery normally needs no extra work. If events still go missing:
  • A custom unload flow in your app may preempt the SDK’s listeners. Call sendBeacon() from your own handler to drain the queue.
  • Mobile Safari is aggressive about killing background tabs. Some event loss on mobile is unavoidable.
The SDK drops the batch on any 4xx response and never retries it. Check:
  • 401 Unauthorized: the apiKey is wrong, disabled, or belongs to a different project.
  • 403 Forbidden: the request’s origin isn’t on the project’s allowed origins list.
  • 429 Too Many Requests: the monthly event quota is used up, or you’ve hit your plan’s rate limit. Check usage in the dashboard.
  • 400 Bad Request: the payload shape doesn’t match the ingest endpoint, usually a self-hosted version mismatch.
Only origins on the allowed origins list can send events, so http://localhost:3000 is rejected unless you added it. The ingest endpoint answers 403 and the SDK logs Endpoint rejected batch batchId=... with status 403; dropping events.Turn on Dev mode in Settings → Allowed origins. For the next 24 hours the project also accepts loopback origins (localhost, *.localhost, 127.0.0.0/8, [::1], on any port). It switches itself off after that.The change can take a few minutes to reach the ingest endpoint. Private network addresses such as 192.168.1.42 are not covered, so testing from a phone on the same Wi-Fi still needs that origin on the allowed list. Requests without an Origin header, such as curl or server-side scripts, are always rejected.
init() is safe to call twice: each call tears down the previous instance, so Strict Mode won’t duplicate timers or listeners.Duplicate step() events usually come from calling step() in a useEffect, which Strict Mode runs twice in development. Either move the call into an event handler, or guard with a ref:
  • trackPageviews is off. Use true for pageviews after consent, or 'auto' for aggregate pageviews while pending and full pageviews after consent. denied stops both. See consent.
  • The hostname is not registered. Pageviews are stored for every hostname, but the reports are per site: add it under Sites in the dashboard, or open All sites.
  • The workspace is on the free plan, which has no pageview allowance. Web analytics needs a paid plan.
  • The monthly pageview allowance is used up. The ingest endpoint still answers 202 but reports the pageviews as dropped, and the Sites page shows the quota state. Your step() events keep flowing.
  • The request was rejected entirely. A 403 means the origin is not on the allowed origins list; see the issue above.
With trackPageviews: 'auto' and pending consent, this is expected. Aggregate pageviews contain no visitor or session identifiers. They increase pageview totals without increasing unique visitors, current visitors, or sessions. Bounce rate and other session metrics describe consented traffic only.Changing the configuration does not remove earlier consented visitors from the selected date range. Check the new request body for collection: 'aggregate' and no identity or sessionId. See aggregate pageviews.
  • identify() replaces the identity but doesn’t start a new session, so events recorded between logins can share a session ID. Call reset() on every logout.
  • Traits carrying over from a previous login also mean a missing reset() at logout.

Still stuck?

Reach out to support with:
  • Your project ID (dashboard, Settings → API Keys)
  • A console dump with debug: true enabled
  • The request/response from your Network tab for a failing event
Contact support →