# Cloudflare Web Analytics — beacon-injected, not API-configured

Cloudflare Web Analytics is the privacy-first RUM (real-user-monitoring) product that counts visits, page views, and referrers for a site without cookies. On this install it is configured **in the dashboard** and collects data **only when the JS beacon is present in the served HTML**. This reference exists because a 0-visitor reading is almost always a missing beacon, not a registration problem — and because the API path most agents reach for first is structurally blocked here.

Pair this with `hosting-sites.md` (how the site is built and deployed) and `dashboard-guide.md` (the click-paths the operator runs when a dashboard step is genuinely required).

---

## The RUM site API is blocked without a RUM permission group

The `/accounts/{account_id}/rum/site_info` family of endpoints (list/create/edit RUM sites) requires an **Account · Web Analytics** permission group on the token. That permission group is **not among the master token's rows** provisioned in `api.md` § Provisioning (which owns the authoritative list). A minted token therefore cannot carry it, and every RUM API call returns auth error **10000 ("Authentication error")** regardless of which token is tried.

Do not chase 10000 by re-minting. Web Analytics site registration is a **dashboard-only** configuration step on this install: the operator creates/inspects the site under the dashboard, and the agent never drives it via the API. Treat the RUM API as unavailable, not as a token bug to fix.

---

## Registered ≠ collecting — 0 visitors means the beacon is missing

A site can show as **registered/enabled** in Web Analytics and still report **0 visitors**. Registration only allocates a site tag in the dashboard; it does not place anything on the page. Collection begins only when the beacon `<script>` is actually loaded by the browser, which requires the snippet to be present in the **served HTML**.

So when the operator asks "why are there no analytics / 0 visitors" on a site that is registered:

- The diagnosis is **not** "registration failed" and **not** "the dashboard is wrong".
- The diagnosis is "the beacon JS is not in the HTML". Confirm it directly against the live site:

```bash
curl -s https://<host>/ | grep cloudflareinsights || echo "BEACON ABSENT"
```

If `cloudflareinsights` does not appear, the beacon is missing and no amount of dashboard reconfiguration will start collection.

---

## Auto-injection needs a proxied DNS zone — Pages-only custom domains do not have one

Cloudflare can **auto-inject** the beacon at the edge, but only for hostnames served through a **Cloudflare-proxied DNS zone** (orange-cloud), where Cloudflare controls the HTML response on the way out. A **Pages-only custom domain** — e.g. a `*.pages.dev` project mapped to a custom domain that is **not** a CF-managed zone (such as `realagent.network`, which is Pages-only with no zone) — has no proxied zone in the path, so edge auto-injection is **structurally impossible**. There is no flag that turns it on; the prerequisite (a zone) does not exist.

Do not claim auto-injection "would work if the domain were a zone" as a fix — for a Pages-only custom domain the domain is not a zone, so the only path is manual injection into the site's own HTML.

---

## The working path: inject the beacon into the site HTML, rebuild, redeploy

For a Pages-only custom domain the beacon must be placed in the source HTML the site renders, then built and deployed:

1. Get the site's beacon snippet from the dashboard (the `<script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token":"<site-tag>"}'></script>` block, with the site's own token).
2. Add it to the rendered HTML — for a Next.js app, the document `<head>` in `app/layout.tsx` (or the framework's equivalent root layout) so every route carries it.
3. Rebuild the site and `wrangler pages deploy` per `hosting-sites.md`.
4. Confirm the beacon is live (outcome contract below).

This is exactly the fix applied to `realagent.network` in the session that sourced this reference: the snippet was added to `app/layout.tsx`, rebuilt, redeployed, and confirmed in the served HTML.

---

## Outcome contract

Web Analytics is collecting when the beacon appears in the **live served HTML**, not when the dashboard says "enabled":

```bash
curl -s https://<host>/ | grep cloudflareinsights
# a match (the beacon.min.js script line) is the proof
```

A registered site in the dashboard is **not** the contract; a present beacon in the response is. Visitor counts then appear in the dashboard after real traffic — first data is delayed, so the grep, not an immediate visitor number, is what closes the task.
