Skip to main content
Every beacon client speaks this: the JavaScript SDK, the PHP client, the WordPress plugin. Any server that implements it can receive their traffic. This page is the working summary. The normative document is PROTOCOL.md in the repository.

The request

One to 500 hits per request. path and userAgent are the only required fields on a hit; a hit missing either is dropped and counted, and the rest of the batch still lands. A server must never reject a whole batch over one bad item, because clients are heterogeneous enough that one always will be. The key travels in X-Beacon-Key rather than Authorization: it identifies a site, not a user, and it has to survive proxies that strip auth headers.

Fields a server should know about

Clients send raw facts. They do not label a hit as a crawler, name a provider, or claim a verification state.

The response

accepted + skipped always equals hits.length. unrecognized is normal traffic that is not an AI client, and it is usually the largest number.

Status codes

This is the part that bites. Clients branch on the status, and getting one wrong silences a site.
Never return a bare 403. A client reads any 403 that is not BEACON_HOST_NOT_ALLOWED as “this account is not entitled” and stops sending for an hour. If you are writing a collector, use 403 for host rejection and nothing else.
Always send a status for an oversized body, rather than closing the connection. A client that sees a socket error backs off and retries a payload it can never deliver.

Timestamps

occurredAt lets a client buffer and send later, which every client does. A server must ignore a timestamp more than 24 hours from its own clock and substitute the receive time. A skewed client clock would otherwise land hits in the wrong day bucket permanently, and there is no way to notice after the fact.

Verification states

A valid signature outranks the address verdict. A signature that fails falls through to the address verdict rather than condemning the hit: the signature base is rebuilt from a forwarded payload, so a gap in your reconstruction must never be recorded as somebody else’s forgery.

Things that are deliberately not solved

Hits are not idempotent. A client that retries after a timeout may double-count. Buffers are small and the data is analytical, so this is accepted rather than papered over with an idempotency key. There is no read API in the protocol. Ingest is the contract. What a server exposes for reading is its own business.