API overview
Tokens, scopes, errors, rate limits, dry runs and idempotency for the Atrix REST API.
Atrix has three HTTP surfaces:
| Surface | Base URL | Auth | For |
|---|---|---|---|
| Control plane | https://app.analytics.atrix.dev/v1 | Bearer token | Projects, keys, flags, experiments, integrations, region tokens |
| Region API | https://eu.api.analytics.atrix.dev | Region token (short-lived JWT) | Queries, SQL, persons, replays, Ask. See Region API. |
| Ingest | https://eu.i.analytics.atrix.dev | Public project key | Events, replay, flags, revenue webhooks. See Capture API. |
The control plane's OpenAPI document is served at
app.analytics.atrix.dev/openapi.json, and a snapshot of it
renders the reference.
Tokens
Create a personal access token in the console under Settings → Tokens. Tokens cannot create tokens.
- Pick the scopes and, optionally, the projects (up to 100) and an expiry (1–365 days; none means it never expires).
- You can only grant scopes your role already holds, and scopes are re-checked against your role on every call: if your role is reduced, your tokens lose reach immediately.
- The secret is shown once. Only a SHA-256 hash is stored.
curl https://app.analytics.atrix.dev/v1/me -H "Authorization: Bearer atx_pat_…"GET /v1/me returns the token, its user and its effective scopes. OAuth access tokens (atx_oat_…) from the
MCP flow work on /v1 too.
Scopes
Scopes only ever narrow. There is no wildcard, and unknown scopes are rejected.
Domains, each with :read and :write (write implies read): projects, org, query, insights,
dashboards, cohorts, catalog, flags, experiments, replays, persons, alerts, ai,
annotations, usage.
Named scopes are never implied by anything else, and must be granted explicitly:
| Scope | Allows |
|---|---|
flags.production | Making a production flag configuration live |
experiments.ship | Shipping an experiment's winning variant |
sql.raw | Running SQL against your data |
persons.pii | Seeing personal-data properties unmasked |
persons.erase | Erasure requests (admins and owners) |
export | Parquet exports to your bucket |
billing.write | Plan changes and the spend cap |
members.write | Inviting and removing members |
Roles map to scopes: viewer has every read; member has read and write on everything except projects
and the organisation, plus export and sql.raw; admin has everything except billing.write; owner
has everything.
Errors
Errors have one shape:
{ "error": { "code": "missing_scope", "message": "requires scope `projects:write`" } }Codes: validation, unauthorized, invalid_credentials, missing_scope, scope_not_held, not_found,
rate_limited, unavailable, internal. A 401 carries WWW-Authenticate: Bearer realm="atrix-analytics".
Another tenant's resource is always 404, never 403, so ids cannot be probed. A 403 means you are in the right organisation but your role or token lacks a scope. A malformed id is also a 404.
Rate limits
Per token: 240 calls and 30 writes per minute. Dry runs do not count as writes. Over the limit you get
429 rate_limited with Retry-After. Request bodies are limited to 64 KB, and requests time out after 30
seconds.
Dry runs
Every /v1 write that changes something important accepts ?dry_run=true: the request is validated and
the response describes what would happen, with status 200, and nothing changes.
curl -X POST "https://app.analytics.atrix.dev/v1/orgs/$ORG_ID/projects?dry_run=true" \
-H "Authorization: Bearer $ATRIX_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "name": "Acme mobile", "region": "eu" }'Idempotency
Send Idempotency-Key (1–255 visible ASCII characters) on POST, PATCH, PUT or DELETE to make a retry
safe. Non-5xx responses are kept for 24 hours and replayed with Idempotent-Replayed: true. The same key with a
different request answers 422 idempotency_key_reused; a key whose first request is still running answers
409 idempotency_in_progress.
What is on /v1
| Area | Routes |
|---|---|
| Identity | GET /v1/me |
| Organisations | GET /v1/orgs, GET /v1/orgs/{org_id} |
| Projects | GET, POST /v1/orgs/{org_id}/projects; GET /v1/projects; GET, PATCH, DELETE /v1/projects/{project_id}; GET …/environments |
| Keys | GET …/keys; POST …/keys/{key_id}/rotate; POST …/keys/{key_id}/revoke |
| Region | POST …/region-token; POST …/query |
| Revenue integrations | GET, POST …/integrations; GET, PATCH, DELETE …/integrations/{id}; POST …/integrations/{id}/rotate-token |
| Tracking plan | GET …/catalog/codegen |
| Erasure | GET, POST …/erasure-requests; GET …/erasure-requests/{id}; GET …/erasure-requests/{id}/certificate; public POST /v1/erasure-certificates/verify and GET /v1/erasure-certificates/keys. See Erasure. |
| Flags | GET, POST …/flags; GET, PATCH, DELETE …/flags/{flag_id}; PATCH …/flags/{flag_id}/environments/{environment}; GET …/flags/publications |
| Experiments | GET, POST …/experiments; GET, PATCH, DELETE …/experiments/{id}; POST …/{id}/launch, …/stop, …/ship; GET …/{id}/results; POST …/experiments/plan |
… is /v1/projects/{project_id}. The flags and experiments routes on /v1 take the same bodies as their
console equivalents under /app/projects/{project_id}/…, which are the ones listed in the
console reference; the flags, experiments and erasure routes are not yet in the OpenAPI document. Insights,
dashboards, cohorts and annotations are console-only today.