Feature flags
Boolean flags per project — percentage rollouts, per-environment switches and a user allowlist — evaluated over one public endpoint.
A feature flag is a named switch with a rollout. Your app asks one endpoint which flags are on for this user, and the answer is a map of booleans — no SDK to install, no local state to sync.
A flag
Each flag has a key (new_checkout — lowercase, 2–64 characters), an on/off switch, and three kinds of targeting:
- Rollout percent — 0 to 100. Users are bucketed deterministically on a hash of the project, the flag and the user's id, so the same person always lands on the same side of a rollout.
- Environments —
production,preview, or both. A flag that is off for production returnsfalsethere no matter what the rollout says. - Allowed user ids — up to 100 ids that always get the flag, ahead of the percentage.
A project can hold 50 flags. Create and edit them in the console under Flags, or over the session API at GET/POST /v1/projects/<slug>/flags, PATCH/DELETE /flags/:id.
Evaluating
GET /v1/flags/i?userId=user_123&distinctId=anon_abc
x-camplax-analytics-key: cak_live_…
{ "flags": { "new_checkout": true, "beta_nav": false }, "ttlSec": 30 }
Authenticate with the project's public ingest key (cak_live_…), sent either as x-camplax-analytics-key or Authorization: Bearer — the same key analytics and errors use, and the one injected into your app as CAMPLAX_ANALYTICS_KEY. Because it is a publishable key, it is safe to call from a browser.
Pass userId when the user is signed in — evaluation prefers it, so signing in does not rebucket a person. Anonymous visitors are bucketed on distinctId. Send both when you have both.
Two things the endpoint guarantees: the environment is stamped server-side (a caller cannot claim to be preview or production — it comes from where the request actually ran), and the allowlist is never returned. The response is a plain key→boolean map, cacheable for ttlSec (30s). It is rate-limited to 120 evaluations per minute per key and IP.
From cx.js
If your app already serves the analytics snippet, flags are one call away — cx.js fetches /v1/flags/i for you and caches for the TTL:
if (await window.cx.flags.get("new_checkout")) { /* … */ }
const all = await window.cx.flags.all();
What flags are not
They are booleans, not remote config — a flag answers on or off, never a value. If you need a string or a number per user, that belongs in your database or secrets.