Skip to main content
The dashboard is a thin React app over a Cloudflare Worker. Everything you do in the UI, you can do from your own code by hitting the same endpoints. This page is the authoritative list, generated from the worker source.
There is no long-lived API key mechanism today. All calls to /api/* require a Clerk session JWT in the Authorization header. The workflow is: obtain a Clerk session, pull its JWT with Clerk’s session.getToken(), and forward it as Authorization: Bearer <jwt>. If you need service account credentials for automation, tell us at enterprise@abtestly.com and we will scope the ask.

Base URL

Every authenticated endpoint below is mounted under /api/*. Public endpoints (snippet delivery, event ingestion) are documented in the beacon protocol reference.

Authentication

  • Verified on every request against Clerk’s JWKS.
  • Missing or malformed → 401 unauthorized.
  • Expired → 401 token_expired. Refresh via the Clerk client and retry.
  • Valid JWT for a user with no access to the requested resource → 403 forbidden.
Cross-site requests are allowed from https://app.abtestly.com only. Server-to-server calls do not need CORS.

Rate limits

  • 100 requests per minute per user across all /api/* endpoints.
  • Exceeding the limit returns 429 rate_limited with a Retry-After header in seconds.
  • Public endpoints (/s, /e, /g, /m, /err) have their own limits documented on Rate limits.

Errors

All errors return JSON with a stable machine-readable code and a human-readable message.
Common codes: unauthorized, forbidden, not_found, validation_failed, conflict, rate_limited, internal_error.

Endpoint reference

Every path below is under https://api.abtestly.com. Path parameters are marked :name. All POST, PATCH, PUT, and DELETE calls except where noted expect and return JSON.

Account and identity

Organization membership and permissions

Organization identity itself is managed by Clerk; ABTestly does not expose CRUD on the organization record. Everything below operates on the member roster and per-site permissions inside an org.

Invitations

Sites

Experiments

Variant code history

The two read endpoints behind Code history. :variantKey is the variation’s short key, not a row id. Both answer 403 with { "error": "forbidden" } when the experiment does not exist or you cannot read it, before any other check. Anyone who can read the experiment can read its versions, Viewers included. There is no endpoint that writes a version: versions are stored only as a side effect of saving, creating, cloning and Signals drafts. List versions
A limit or before that is not a positive integer answers 400 with { "error": "validation_error" }.
Get one version
Returns { "revision": { ... } } with every field above plus jsCode, cssCode and libraryKeys, in load order. A :revision that is not a positive integer, or that this variation does not have, answers 404 with { "error": "not_found" }. Unlike most errors on this page, these three carry only the error code, with no message.

Result exports

Goals

The goals-library router is mounted under /api/sites, so every path below sits inside a site.

Audiences

Mounted under /api/sites.

Saved locations

Mounted under /api/sites.

Mutual exclusion groups

Billing

Public endpoints

Not authenticated with Clerk. Used by the snippet on the visitor’s device. The wire format for each of these is on the beacon protocol page.

Not yet published

  • OpenAPI spec. We are drafting one. Until it ships, treat this page as the source of truth.
  • Long-lived API keys. No self-serve keys today. If you have a server-side automation use case, tell us at enterprise@abtestly.com.
  • Webhooks out. Paddle webhooks come in; we do not send outbound webhooks to your infrastructure yet.
If you are building against these and something on this page does not match reality, email support@abtestly.com with the endpoint and the response you got. That is the fastest way for us to fix the doc.
Last modified on September 12, 2026