Every beacon the snippet sends is text/plain on purpose, that
lets sendBeacon work without a CORS preflight. Payloads are JSON
strings.
Fields marked optional are omitted from the body when they have no
meaningful value; do not send them as null.
POST /e, exposures and goals
The main beacon. Batched exposures and goal fires from a pageview.
s, u, v, t, always present. Site id, visitor id, config
version, client ms since epoch.
nr, n for new (within 30-min gap), r for returning.
rf, bucketed traffic channel (direct/organic/paid/social).
uc, utm_campaign verbatim.
p, LCP in ms plus per-experiment attribution.
e[], exposure records.
g[], goal records.
Each item’s bn is l for “bucketed on page load” or s for
“bucketed on an SPA route change”.
gn is the exclusion-group generation counter, if the experiment is
a member of a group.
For goals, eid is a UUIDv7 per fire, the ledger uses it for dedup
on retries.
Response: 200 OK with an empty body.
POST /e/validate, preview verdict
Same payload as /e. Server does not persist. Returns:
or
The preview modal uses this to explain why an experiment did or did
not fire on the current page.
POST /err, snippet errors
First-occurrence-per-(phase, expId) per session. Deduped server-side.
POST /m, library telemetry
Per-library-load event from the dev libraries dependency manager.
Small, non-per-visitor.
GET /g, edge geo
Returns:
Cached per-session by the snippet. Bounded wait of 250 ms.
GET /preview-config?exp=<uuid>, one experiment for preview
Returns a draft or paused experiment’s config blob. CORS *. Used by
the preview modal to fetch experiments not yet in the site config.
CORS
Every visitor-facing beacon is Access-Control-Allow-Origin: *.
sendBeacon with text/plain does not preflight, so wildcards work.
Auth
None on the visitor-facing endpoints. The snippet is public; anyone
can read a config for a site they know the id of. That is deliberate
clients cannot secure themselves.
Dashboard endpoints under /api/* require a Clerk bearer token
(there is no long-lived API key mechanism today). Rate-limited at 100
requests per minute per authenticated user.
If you build your own client
Everything on this page is stable. Method versions in the ledger are
locked per experiment; the wire format follows semver, a breaking
change bumps a version.
If you build a serious integration, tell us. Helps us not break you. Last modified on August 5, 2026