Skip to main content
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 or an API key. 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.