API reference

Generated from the frozen OpenAPI contract (v1.0.0). All routes live under /v1; dashboard routes authenticate with a Clerk session JWT, SDK ingest routes with a per-org API key (X-API-Key).

Endpoints

GET/v1/experiments

List experiments for the caller's org

Returns every experiment the caller's org can see (latest configuration version each). Use it to populate the dashboard list, then drill into an id for its computed results.

auth: clerkJwt · responses: 200, 401

POST/v1/experiments

Create an experiment (a new version is minted)

Creates an experiment from a config (variants, allocation weights, metrics), minted at version 1. The `salt` and `bucket_count` you set here freeze how users are bucketed, so keep them stable for the life of the experiment. Returns the stored experiment with its id.

auth: clerkJwt · responses: 201, 400, 401

GET/v1/experiments/{id}

Get an experiment

Fetches one experiment (its latest version) by id, scoped to the caller's org.

auth: clerkJwt · responses: 200, 404

PUT/v1/experiments/{id}

Update an experiment (mints a new version; sticky assignments preserved)

Applies a config change by minting a new version. Because the salt and bucketing are preserved, users already assigned keep their variant — in-flight traffic is never reshuffled.

auth: clerkJwt · responses: 200, 400

POST/v1/assign

Resolve a variant for a user (SDK ingest plane)

Server-side variant resolution for callers that can't run the SDK's local bucketing (e.g. a non-supported language). Deterministic and sticky. Prefer the SDK's in-process `assign` on hot paths — it needs no network round-trip.

auth: apiKeyPublishable | apiKeySecret · responses: 200, 401, 429

POST/v1/events

Ingest a batch of events (SDK ingest plane, async write path)

Asynchronous write path for `exposure`, `model_call`, and `business_event` records. Returns immediately with an ack and the current `config_version` (a staleness hint the SDK uses to refresh). The SDK batches, retries, and spills offline for you — call `track`, not this.

auth: apiKeyPublishable | apiKeySecret · responses: 202, 401, 429

POST/v1/events/import

Bulk import historical events (server-side only)

Backfill historical events as newline-delimited JSON using a secret key (server-side only, never a browser). Use it to seed an experiment from data you already collected. See the "Bring your own data" guide for the field mapping.

auth: apiKeySecret · responses: 202

GET/v1/config

Fetch the current experiment config (versioned, poll-friendly)

SDKs and the assignment service poll this on an interval using `If-None-Match`. An unchanged config returns `304`. The org is derived from the API key, never a query param.

auth: apiKeyPublishable | apiKeySecret · responses: 200, 304

GET/v1/experiments/{id}/results

Get computed results for an experiment

Returns the latest per-metric results: effect + 95% CI, raw and FDR-corrected p-values, the test used, the per-arm value distribution, and the SRM gate status. Accepts an optional `day` query parameter to read one run — an ISO date (nightly experiment-day) or a stored manual run's `manual-<ISO instant>` label; the response's `available_days` lists every day and permanently stored manual run with computed rows and `computed_for` names the served run. `GET /v1/experiments/{id}/traffic` (additive route) serves the daily exposure counts per variant for the traffic timeline. Responds `404` until the daily pipeline has produced results (the dashboard shows a "collecting" state meanwhile).

auth: clerkJwt · responses: 200, 404

POST/v1/keys

Issue or rotate an org API key

Issues or rotates a `publishable` (safe client-side, write-only) or `secret` (server-side) org key. The key is returned once and never shown again — store it immediately. Rotating invalidates the previous key of that scope.

auth: clerkJwt · responses: 201

POST/v1/business-events

Receive a business event via HMAC-signed webhook

Ingests a downstream business outcome (conversion, revenue, retention) via an HMAC-signed webhook, so post-experience metrics can be attributed back to the experiment and variant.

auth: hmacSignature · responses: 202, 401

POST/v1/webhooks/clerk

Clerk identity-sync webhook (Svix-signed)

Upserts user/organization/membership changes into LLMJury's Postgres (internal UUIDs with `clerk_*` external-reference columns; CONVENTIONS §11). The Svix signature is verified.

auth: svixSignature · responses: 204, 401

GET/v1/billing/summary

The caller org's plan and subscription state

Returns the org's current plan (`free`/`pro`/`business`/`enterprise`), its Stripe subscription status, and whether billing is available on this environment. An org that has never checked out is the free tier.

auth: clerkJwt · responses: 200

POST/v1/billing/checkout-session

Start a Stripe Checkout for a paid plan (admin-only)

Creates a Stripe-hosted Checkout Session for the `pro` or `business` subscription and returns its URL; the browser is redirected there, so no card data ever transits LLMJury. The completed purchase arrives back via the Stripe webhook.

auth: clerkJwt · responses: 200, 409

POST/v1/billing/portal-session

Open the Stripe Billing Portal (admin-only)

Creates a Stripe Billing Portal session (manage payment method, cancel, invoices) for an org that has completed a checkout, and returns its URL. 409 when no billing account exists.

auth: clerkJwt · responses: 200, 409

POST/v1/webhooks/stripe

Stripe billing webhook (signature-verified)

Applies subscription lifecycle events (`checkout.session.completed`, `customer.subscription.created/updated/deleted`) to the org's plan. The `Stripe-Signature` header is verified against the endpoint secret; processing is an idempotent upsert, so Stripe retries are safe. Other event types are acknowledged and ignored.

auth: stripeSignature · responses: 204, 401

Schemas

Schema summary
SchemaRequired fieldsDescription
ProblemRFC 9457 problem details (application/problem+json).
AllocationSlicevariant, weight
ExperimentA persisted experiment — its config plus server-set timestamps.
ExperimentConfigname, salt, bucket_count, allocation, metricsThe versioned experiment config SDKs poll. Embeds the frozen bucketing inputs.
MetricDefinitionname, kind, category, method_default, metric_version, shared_org_wide
StatTest
Eventevent_id, type, experiment_id, user_id, timestampSee schemas/event.schema.json (authoritative). v1 stores raw prompt/response.
MetricResulttest, statistic, p_value, p_value_fdr, effect, ci_low, ci_high, n_per_arm, srm_p, permutations_used, sample_cap_used, window_days, retention_truncated, preliminarySee schemas/metric-result.schema.json (authoritative).
AssignRequestexperiment_id, user_id
AssignResponsevariant, config_version
IngestAckaccepted, config_version
ApiKeyscope, key, created_at
BillingSummaryplan, has_billing_account, billing_configured
HostedUrlurl
PlanLimitplan, retention_days, max_permutations, max_sample_per_arm, monthly_event_allowanceSee schemas/plan-limits.schema.json (authoritative).

The full contract lives at contracts/openapi.yaml in the repository; this page is regenerated from it on every build.