# Debugging

URL: https://supapulse.app/docs/debugging

Find out why pageviews, events or revenue aren't showing up. Debug mode, the Network tab, error responses and the usual causes.

## Checklist [#checklist]

1. The site's domain in Supapulse matches the domain of the pages (or is its parent domain).
2. The token on the page is the site's **public** token and starts with `pub_`.
3. The script loads: no 404 or CSP error for `browser.js` in the console.
4. Requests to `/api/ingest` appear in the Network tab and get `202`.
5. Nothing blocks them: try a private window without extensions.

## 1. Turn on debug mode [#1-turn-on-debug-mode]

```html
<script
  defer
  src="https://cdn.jsdelivr.net/npm/@supapulse/core@1/dist/browser.js"
  data-token="YOUR_PUBLIC_TOKEN"
  data-debug="true"
></script>
```

Or `debug: true` in `init()`. The console then shows:

| Message | Meaning |
| --- | --- |
| `[Supapulse] Initialized` | `init()` ran, with the resolved config. |
| `[Supapulse] Event sent` | A pageview or event was handed to the browser. |
| `[Supapulse] Send failed` | The `fetch` fallback failed; a retry is scheduled. |
| `[Supapulse] Rate limited, retry after Ns` | The API answered `429`. |
| `[Supapulse] Dropped after N attempts` | Retries ran out. |
| `[Supapulse] Not initialized` | `pageview()` or `event()` was called before `init()`. |
| `[Supapulse] Already initialized` | `init()` was called twice on the same instance (harmless). |

## 2. Check the Network tab [#2-check-the-network-tab]

Filter by `ingest`. Requests sent with `sendBeacon` show as type `ping`; the response isn't visible for those, but the request is. A request with status `(canceled)` during navigation is normal for `fetch` fallbacks; `keepalive` still delivers it.

To see the API's answer, send a test request yourself:

```bash
curl -i -X POST https://supapulse.app/api/ingest \
  -H "Content-Type: application/json" \
  -H "X-API-Token: pub_your_token" \
  -H "Origin: https://acme.com" \
  -d '{"type":"pageview","data":{"url":"https://acme.com/","session_id":"debug-test"}}'
```

A `202` here and a pageview in the dashboard a few seconds later means the token and domain are right, and the problem is in the page.

## 3. Read the error [#3-read-the-error]

| Response | Cause | Fix |
| --- | --- | --- |
| `401 Missing API token` | No token sent | Check `data-token` or `apiToken`. |
| `401 Invalid token format` | Token doesn't start with `pub_`/`sec_` | Look for stray quotes or spaces. |
| `401 Invalid API token` | Token regenerated, mistyped, or the site was deleted | Copy it again from the site's settings. |
| `403 Origin not allowed` | Page's domain doesn't match the site | Fix the domain in the site's settings. Subdomains of the site's domain are allowed. |
| `402` | The site's team has no active plan (`plan_inactive`), or reached its monthly event limit (`quota_exceeded`) | Check the team's plan and usage. The tracker doesn't retry a `402`. |
| `422` | Unknown `type`, missing `data`, a field that broke a rule, or `revenue` with a public token | Fix the payload or use the secret token. The response names each field. |
| `429` | Over 2,000 requests per hour from one IP address | The tracker waits and retries. Common when load testing from one machine. |
| `net::ERR_BLOCKED_BY_CLIENT` | An ad or privacy blocker | Nothing to fix in code. |

## 4. Content Security Policy [#4-content-security-policy]

A CSP error in the console names the directive. Allow `https://cdn.jsdelivr.net` in `script-src` and `https://supapulse.app` in `connect-src`.

## 5. Single-page apps [#5-single-page-apps]

- **No pageviews after navigation:** you set `autoPageviews: false` and don't call `pageview()`, or your router only changes `location.hash`. See [SPA support](https://supapulse.app/docs/core/spa-support).
- **Two pageviews per navigation:** your router calls both `pushState` and `replaceState`, or you call `pageview()` with automatic pageviews on.

## 6. React [#6-react]

- **`useSupapulse must be used within a SupapulseProvider`:** the component is outside the provider.
- **`[Supapulse] Not initialized` warning:** an event or `identify()` was sent before the provider initialized the tracker, and it was dropped. Send from handlers, or wait for `isReady`. See [hooks](https://supapulse.app/docs/react/hooks#usesupapulse).
- **Next.js: `createContext only works in Client Components`:** wrap the provider in a `'use client'` component. See [SSR support](https://supapulse.app/docs/react/ssr).
- **Next.js: token is `undefined`:** the variable must start with `NEXT_PUBLIC_`, and you must rebuild after changing it.

## 7. Revenue [#7-revenue]

- **`202` but no new revenue:** a payment with the same `transaction_id` and `event_type` was already stored.
- **Revenue shows as direct:** Supapulse found no visit for the customer. Call `identify()` with the same ID you send as `customer_id`, and do it within the site's salt rotation window (1 to 30 days) of the visit.
- **Stripe revenue missing:** check the last-event time in the site's settings. If it's empty, Stripe isn't reaching the endpoint; see the [Stripe guide](https://supapulse.app/docs/guides/stripe#common-issues).
- **`402`:** the site's team has no active plan.
- **`422`:** a field failed validation (the response names it), or the public token was used. Revenue needs the secret token.

## 8. Still stuck [#8-still-stuck]

Email [support@supapulse.app](mailto:support@supapulse.app) with:

- the page URL and the site's domain as set in Supapulse;
- the console output with debug mode on;
- the status and response body of a failing `/api/ingest` request;
- for revenue, the `transaction_id` and when you sent it.

Never send the secret token.
