atrixANALYTICS
REST API

Region API

Query your data directly in its region with a short-lived region token.

Query results never pass through the control plane. You ask the control plane for a region token, then call your project's region API directly: https://eu.api.analytics.atrix.dev for EU projects.

Get a region token

curl -X POST "https://app.analytics.atrix.dev/v1/projects/$PROJECT_ID/region-token" \
  -H "Authorization: Bearer $ATRIX_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "environment": "production", "scopes": ["query:read"] }'
{
  "token": "eyJhbGciOiJFZERTQSIs…",
  "region_api_url": "https://eu.api.analytics.atrix.dev",
  "expires_at": "2026-09-25T10:10:00Z",
  "region": "eu",
  "environment": "production",
  "scopes": ["query:read"]
}

The token is an EdDSA (Ed25519) JWT that lives 10 minutes by default and never more than 15. It carries the project, environment and scopes; the region reads them only from the token. Its scopes are your token's regional scopes intersected with your role, optionally narrowed further with scopes.

Run a query

The shortcut POST /v1/projects/{project_id}/query validates a Query IR document and returns a query:read region token together with the exact region request to send:

curl -X POST "https://eu.api.analytics.atrix.dev/query" \
  -H "Authorization: Bearer $REGION_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "query": {
      "ir_version": 1,
      "date_range": { "from": "-30d" },
      "insight": {
        "kind": "trends",
        "interval": "day",
        "series": [{ "event": "signed_up", "math": "unique_persons" }],
        "breakdown": { "type": "event", "property": "$os" }
      }
    },
    "include_sql": true
  }'

Insight kinds: trends, funnel, retention, paths, lifecycle, stickiness and revenue.

  • Math: total, unique_persons, unique_sessions, unique_groups, sum, avg, min, max, p50, p90, p99.
  • Filter operators: eq, neq, in, not_in, contains, not_contains, regex, not_regex, gt, gte, lt, lte, is_set, is_not_set.
  • Dates: now, today, yesterday, relative (-7d, -12w, -3m, -1y, -24h), a date or an ISO 8601 timestamp.
  • Defaults: funnel window 14 days, retention 7 periods, paths depth 5 with 50 edges.

include_sql: true returns the compiled, parameterised SQL next to the result, the same SQL Ask cites as evidence. Queries time out after 30 seconds and read at most 500 million rows. Results are cached for 60 seconds for recent data and an hour for historical ranges; refresh: true bypasses the cache. Each project runs at most two queries at once (429 too_many_concurrent_queries beyond that).

Routes

RouteScope
POST /queryquery:read
POST /sqlsql.raw; see SQL
GET /schema/events, GET /schema/propertiesquery:read
GET /events/livequery:read
GET /persons, GET /persons/{id}, GET /persons/{id}/events, GET /persons/{id}/identitypersons:read (personal data masked unless persons.pii)
POST /persons/{id}/unmergepersons:write
GET /groups, GET /groups/types, GET /groups/{index}/{key}persons:read
POST /cohorts/preview, POST /cohorts/{id}/materialize, POST /cohorts/{id}/staticcohorts:*
GET /catalog/discoverycatalog:read
GET /replays, GET /replays/{id}, GET /replays/{id}/blocks/{name}replays:read
POST /ask, GET /ask/conversations, GET /ask/conversations/{id}ai:read
POST /experiments/statsexperiments:read

Errors use the same { "error": { "code", "message" } } shape as the control plane, with codes unauthorized, forbidden, invalid_query, bad_request, not_found, conflict, query_too_large (422), too_many_concurrent_queries (429), unavailable and internal.

On this page