Skip to content

Authentication

API keys, capability scopes, and the console session cookie.

Every request to Retidal authenticates one of two ways, depending on which API it targets.

  • A Retidal project and at least one project API key — see Quickstart to issue one.
  • For Management API calls, a console session (log in at the console) or a key with the metrics:read/metrics:write scope.

Send your project API key in the X-API-Key request header:

bash
curl -X POST https://api.retidal.com/api/v1/t \
-H "X-API-Key: tk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "eventName": "page_view", "visitorId": "vid_abc123" }'

For pixel/beacon contexts where a header cannot be set (navigator.sendBeacon()), the key may instead be passed as the _ak (or key) query parameter. Prefer the header everywhere else — query strings are logged by intermediaries.

Keys carry explicit capability scopes. A legacy key that has never been given scopes behaves as ingest:write only.

Scope Grants
ingest:write / tracking:ingest POST /api/v1/t, POST /api/v1/identify
decide / decision:call POST /api/v1/decide
profile:write PUT /api/v1/profile
coupon:redeem POST /api/v1/coupons/redeem
coupon:read GET /api/v1/coupons/{code}
metrics:read / metrics:write read/write metric definitions (Management API)
mcp:events_read, mcp:events_write, mcp:links_write, mcp:attribution_read Retidal MCP server tools

Scoped keys are issued from the console: POST /api/projects/{projectId}/api-keys (project role ADMIN+) returns the raw key exactly once — it cannot be retrieved again. PATCH /api/projects/{projectId}/api-keys/{id} updates a key’s scopes or name and invalidates its cache entry immediately.

A key is bound to exactly one project. Calling another project’s resource returns the same response shape as “not found” — resource existence is never leaked across tenants. On a representative route like GET /api/auth/me, a revoked or invalid key returns 401; on a scoped route like GET /api/admin/users, a valid key missing the required scope returns 403 (see each endpoint’s own page for its exact codes).

POST /api/v1/t and POST /internal/backfill/events accept any valid project API key with no additional scope gate.

Most /api/projects/{projectId}/... routes authenticate with the console session cookie trackly_session, established by the console login flow, and authorize by the caller’s project role (OWNER / ADMIN / MEMBER, or VIEWER+ for read routes).

The Management API accepts a scoped X-API-Key on only two kinds of route, and each operation states explicitly which:

  1. Routes gated by a capability scope (requireProjectRoleOrApiScope) — currently the metric-definitions endpoints, requiring metrics:read or metrics:write.
  2. A small number of read-only project endpoints that accept the project’s own legacy API key with no scope requirement.

Every other Management route — e.g. GET /api/tenant/profile — rejects an API key with 403 and requires the session cookie.

There is also an internal x-worker-token scheme (WorkerToken) used for worker-to-worker calls — it is not for customer use.

Knowing which credential a call needs unlocks calling every endpoint correctly on the first try. Sending data needs no scope at all — POST /api/v1/t and POST /api/v1/identify declare only ApiKeyAuth, so a valid project key is sufficient, and a scoped key carrying ingest:write or tracking:ingest works the same way. Decisioning is the opposite: it is scope-gated, and a key without the right scope is rejected even though it is valid.

Confirm your key is valid — POST /api/v1/t has no scope gate beyond a valid project key, so this call only proves the key itself is good, not that it carries any particular scope:

bash
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://api.retidal.com/api/v1/t \
-H "X-API-Key: $RETIDAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"eventName": "page_view", "visitorId": "vid_abc123"}'
# expect: 200 (with ?sync=1) or 202 — either confirms the key is valid;
# a 401 means it is not

To confirm a specific scope, call the scoped endpoint itself — e.g. POST /api/v1/decide — and expect 403 if the scope is missing — see the scope table above.

If the POST /api/v1/t ingestion request above returns 401, work through Events not arriving. If a scoped endpoint like POST /api/v1/decide (decide, profile, coupon routes) returns 403, the key is valid but missing that scope — reissue or PATCH the key with the required scope. If a Management API call or console session fails instead, work through the health checklist.