The snippet is deliberately quiet: it never confirms whether an id was recognised, and a browser without a cookie makes no request at all. That is right for privacy and wrong for debugging, so the **Beacon health** card on **Workspace → Website** is where you look instead of the browser console.



## Reading the health card

The card shows the time of the last beacon and, per UTC day for the last seven days, what arrived at the beacon endpoint and what SendOps did with it:

| Column | Meaning | What it tells you |
|---|---|---|
| **Accepted** | The id resolved to a contact and an activity was recorded. | The number you want to see climbing. |
| **Unknown visitor** | The id was well-formed but matched no active contact. | Normal in small numbers: cookies belonging to contacts you have since archived. A large share means your cookie value is not what identify returned. |
| **Unknown key** | The site key in the beacon is not one of yours, or is past its rotation grace. | A stale deployment or Tag Manager container still carrying an old key. |
| **Bad name** | A custom event whose name did not match `site_` plus lowercase letters, digits and underscores. | Always a bug in your own `sendops.track` or `sendops_track` calls. Shown as a warning above the table because it is never expected. |
| **Rate limited** | More than the endpoint allows from one address or one visitor. | A loop calling `track`, or a single office address behind a NAT. |

Counters are kept for eight days and then roll off. They are counts of beacons, not of people.

## Nothing has arrived

If the snippet is installed and the card still says **No beacons yet**, it is almost always one of four things. None of them produce an error anywhere, which is why they are listed here.

### The cookie is `HttpOnly`

Many frameworks default cookies to `HttpOnly`. The snippet reads `so_vid` with JavaScript, so an `HttpOnly` cookie is invisible to it and the browser sends nothing. Check the cookie in your browser's developer tools (Application → Cookies): the HttpOnly column must be empty for `so_vid`.

### The cookie's `Domain` is your app host

If your app sets the cookie without a `Domain` attribute, or with `Domain=app.example.com`, only the app can read it. The snippet on `www.example.com` never sees it. Set `Domain=.example.com` — your registrable domain — so every site under it can read the cookie. Visiting your app itself will not prove anything here: the app is the one place the cookie always works.

### Consent is required and never granted

With `requireConsent: true` the snippet holds everything until `sendops.consent(true)` is called or Consent Mode grants `analytics_storage`. If nothing does either, nothing is ever sent. The same happens in Tag Manager when both the template's consent declaration and its *Wait for in-page consent* checkbox are on: the tag waits for a second signal that never comes. Use one gate, not two.

### The snippet went in before identify was wired

The snippet only reads. Until your backend calls identify and sets the cookie, no visitor carries an id and the snippet does nothing. Check a contact's detail page: the **Website** badge appears once they have been issued a visitor id. If no contact has one, the problem is upstream of the snippet.


  The fastest end-to-end check: log into your product in a fresh browser session, confirm `so_vid` is set with the right `Domain` and without `HttpOnly`, then open your marketing site in the same browser. Refresh the Website page: the health card counts the beacon, and the contact's timeline shows a `site_visit`.


## Visits stop after a week on Safari

That is the signature of a cookie written by JavaScript. Safari caps script-written cookies at seven days. Move the `Set-Cookie` to your server's login response and re-set it on every login; see [Installing the snippet](/website/installing-the-snippet).

## A contact was archived but still visits

Archiving a contact revokes its visitor id. The cookie in their browser keeps being sent, and each beacon is counted as **Unknown visitor** and dropped. Nothing is recorded for them.

## Rotating or revoking the key

**Rotate** issues a new key and keeps the old one working for 24 hours. **Revoke** retires the key without a replacement, also with a 24-hour grace. Either way, once the grace ends, beacons carrying the old key count as **Unknown key**. Redeploy the script tag or publish the new Tag Manager container version within that window.

## Rate limits

Two limits apply, and neither counts against your API keys:

| Limit | Applies to |
|---|---|
| 600 beacons per minute | one client IP address |
| 60 custom events per minute | one visitor |

Excess is dropped and counted as **Rate limited**. A page that calls `sendops.track` in a loop, on scroll, or on every keystroke is the usual cause.