# API reference

Every endpoint of the 3x REST API v1 (157 routes), generated from the route contracts the API itself is built from.
The machine-readable form is the OpenAPI 3.1 document at `GET /v1/openapi.json`; an interactive reference is served at `/docs/api` and a markdown
rendering of the full schemas at `/docs/api.md`. Conventions (authentication, errors, pagination, idempotency, rate limits, filters) are described
in the other pages of the API section.

## System

| Operation | Endpoint | Auth |
|---|---|---|
| [`system.health`](#systemhealth) | `GET /v1/health` | public |
| [`system.cron`](#systemcron) | `GET /v1/cron/{job}` | `CRON_SECRET` |

### system.health

`GET /v1/health` – Health check.

- Auth: public
- Response: 200 JSON (`Health`)

### system.cron

`GET /v1/cron/{job}` – Run scheduled jobs (Vercel Cron / external scheduler).

job = tick (all due jobs) or a single job name from the job registry. Bearer CRON_SECRET.

- Auth: `CRON_SECRET`
- Response: 200 JSON (`CronResult`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `job` | string | yes |  |

## Ingest

| Operation | Endpoint | Auth |
|---|---|---|
| [`ingest.collect`](#ingestcollect) | `POST /v1/e` | publishable key (`pk_3x_…`) |
| [`ingest.collectAlias`](#ingestcollectalias) | `POST /v1/collect` | publishable key (`pk_3x_…`) |
| [`ingest.clientConfig`](#ingestclientconfig) | `GET /v1/c/{pk}` | publishable key (`pk_3x_…`) |
| [`ingest.script`](#ingestscript) | `GET /v1/s/{file}` | public |
| [`ingest.sdkPackage`](#ingestsdkpackage) | `GET /v1/sdk/{file}` | public |

### ingest.collect

`POST /v1/e` – Collect a tracking batch (browser SDK).

text/plain JSON WireBatch v2 with the publishable key in `k`. Always answers without body: 204 accepted or dropped (bots, budgets), 400 invalid, 403 origin/key, 413 too large, 429 rate limited.

- Auth: publishable key (`pk_3x_…`)
- Response: 204, no body
- Body: text/plain (`WireBatch`)

### ingest.collectAlias

`POST /v1/collect` – Alias of POST /v1/e (same handler; /v1/e is shorter and blocker-neutral).

- Auth: publishable key (`pk_3x_…`)
- Response: 204, no body
- Body: text/plain (`WireBatch`)

### ingest.clientConfig

`GET /v1/c/{pk}` – Client configuration for a publishable key.

- Auth: publishable key (`pk_3x_…`)
- Response: 200 JSON (`ClientConfig`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `pk` | string | yes |  |

### ingest.script

`GET /v1/s/{file}` – Browser SDK script (x3.js, x3.full.js, x3.{video,sections,forms,identify,consent,bookings}.js, versioned x3@1.2.3.js); ?k=pk_… inlines the client config.

- Auth: public
- Response: 200 javascript

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `file` | string | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `k` | string | no |  |

### ingest.sdkPackage

`GET /v1/sdk/{file}` – npm tarball of @3x-engine/sdk built with this deployment – `npm install <api origin>/v1/sdk/3x-engine-sdk-<version>.tgz` (used by the install recipes).

- Auth: public
- Response: 200 gzip

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `file` | string | yes |  |

## Events

| Operation | Endpoint | Auth |
|---|---|---|
| [`ingest.serverEvents`](#ingestserverevents) | `POST /v1/projects/{project}/events` | secret key or user session · `events:write` · `Idempotency-Key` |
| [`analytics.liveEvents`](#analyticsliveevents) | `GET /v1/projects/{project}/events/live` | secret key or user session · `analytics:read` |
| [`events.catalog`](#eventscatalog) | `GET /v1/projects/{project}/event-catalog` | secret key or user session · `analytics:read` |
| [`eventSchemas.upsert`](#eventschemasupsert) | `PUT /v1/projects/{project}/event-schemas/{name}` | secret key or user session · `config:write` |
| [`eventSchemas.delete`](#eventschemasdelete) | `DELETE /v1/projects/{project}/event-schemas/{name}` | secret key or user session · `config:write` |
| [`events.validate`](#eventsvalidate) | `POST /v1/events/validate` | public |

### ingest.serverEvents

`POST /v1/projects/{project}/events` – Send server-side events attached to a browser session.

- Auth: secret key or user session · `events:write` · `Idempotency-Key`
- Response: 202 JSON (`ServerEventsResult`)
- Body: application/json (`ServerEvents`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### analytics.liveEvents

`GET /v1/projects/{project}/events/live` – Events received since a cursor (long-poll up to ?wait=20 seconds, ≤ 200 rows).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`LiveEvents`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `cursor` | string | no |  |
| `wait` | string | no |  |
| `names` | string \| string[] | no |  |
| `limit` | string | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |

### events.catalog

`GET /v1/projects/{project}/event-catalog` – Built-in events, presets, registered and observed custom events.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### eventSchemas.upsert

`PUT /v1/projects/{project}/event-schemas/{name}` – Register or update a custom event schema.

- Auth: secret key or user session · `config:write`
- Response: 200 JSON (`EventSchema`)
- Body: application/json (`EventSchemaWrite`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `name` | string | yes |  |

### eventSchemas.delete

`DELETE /v1/projects/{project}/event-schemas/{name}` – Remove a custom event schema (events stay; they become unregistered).

- Auth: secret key or user session · `config:write`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `name` | string | yes |  |

### events.validate

`POST /v1/events/validate` – Validate a custom event payload against the built-in catalog (and a project's schemas when authorized).

- Auth: public
- Response: 200 JSON (`EventValidateResult`)
- Body: application/json (`EventValidate`)

## Auth

| Operation | Endpoint | Auth |
|---|---|---|
| [`auth.signup`](#authsignup) | `POST /v1/auth/signup` | public |
| [`auth.login`](#authlogin) | `POST /v1/auth/login` | public |
| [`auth.logout`](#authlogout) | `POST /v1/auth/logout` | dashboard user session |
| [`auth.me`](#authme) | `GET /v1/auth/me` | dashboard user session |
| [`auth.sudo`](#authsudo) | `POST /v1/auth/sudo` | dashboard user session |
| [`auth.magicLink`](#authmagiclink) | `POST /v1/auth/magic-link` | public |
| [`auth.magicLinkVerify`](#authmagiclinkverify) | `POST /v1/auth/magic-link/verify` | public |
| [`auth.password`](#authpassword) | `POST /v1/auth/password` | dashboard user session · sudo |
| [`auth.invitationInfo`](#authinvitationinfo) | `GET /v1/invitations/{token}` | public |
| [`auth.invitationAccept`](#authinvitationaccept) | `POST /v1/invitations/{token}/accept` | dashboard user session |
| [`oauthGrants.mine`](#oauthgrantsmine) | `GET /v1/auth/oauth-grants` | dashboard user session |

### auth.signup

`POST /v1/auth/signup` – Create a user (self-signup when enabled, or with an invitation token).

- Auth: public
- Response: 201 JSON (`SessionToken`)
- Body: application/json (`Signup`)

### auth.login

`POST /v1/auth/login` – Log in with e-mail and password; returns a session token (x3s_…).

- Auth: public
- Response: 200 JSON (`SessionToken`)
- Body: application/json (`Login`)

### auth.logout

`POST /v1/auth/logout` – Revoke the current session (all=true: every session of the user).

- Auth: dashboard user session
- Response: 200 JSON (`Ok`)
- Body: application/json

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `all` | boolean | no |  |

### auth.me

`GET /v1/auth/me` – Current user, memberships and accessible projects.

- Auth: dashboard user session
- Response: 200 JSON (`Me`)

### auth.sudo

`POST /v1/auth/sudo` – Re-authenticate for sensitive actions (15 minutes).

- Auth: dashboard user session
- Response: 200 JSON
- Body: application/json (`Sudo`)

### auth.magicLink

`POST /v1/auth/magic-link` – Request a magic link (same response whether the account exists).

- Auth: public
- Response: 202 JSON (`Ok`)
- Body: application/json (`MagicLinkRequest`)

### auth.magicLinkVerify

`POST /v1/auth/magic-link/verify` – Exchange a magic-link token for a session.

- Auth: public
- Response: 200 JSON (`SessionToken`)
- Body: application/json (`MagicLinkVerify`)

### auth.password

`POST /v1/auth/password` – Change the password (sudo).

- Auth: dashboard user session · sudo
- Response: 200 JSON (`Ok`)
- Body: application/json (`PasswordChange`)

### auth.invitationInfo

`GET /v1/invitations/{token}` – Invitation details for the accept page.

- Auth: public
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `token` | string | yes |  |

### auth.invitationAccept

`POST /v1/invitations/{token}/accept` – Accept an invitation as the logged-in user.

- Auth: dashboard user session
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `token` | string | yes |  |

### oauthGrants.mine

`GET /v1/auth/oauth-grants` – Connected apps (OAuth grants) of the logged-in user.

- Auth: dashboard user session
- Response: 200 JSON

## Organizations

| Operation | Endpoint | Auth |
|---|---|---|
| [`orgs.list`](#orgslist) | `GET /v1/orgs` | secret key or user session |
| [`orgs.create`](#orgscreate) | `POST /v1/orgs` | dashboard user session |
| [`orgs.get`](#orgsget) | `GET /v1/orgs/{org}` | secret key or user session |
| [`orgs.update`](#orgsupdate) | `PATCH /v1/orgs/{org}` | secret key or user session · `org:admin` |
| [`orgs.delete`](#orgsdelete) | `DELETE /v1/orgs/{org}` | dashboard user session · `org:admin` · sudo |
| [`audit.list`](#auditlist) | `GET /v1/orgs/{org}/audit` | secret key or user session · `org:admin` |
| [`oauthGrants.list`](#oauthgrantslist) | `GET /v1/orgs/{org}/oauth-grants` | secret key or user session · `org:admin` |
| [`oauthGrants.revoke`](#oauthgrantsrevoke) | `DELETE /v1/orgs/{org}/oauth-grants/{id}` | secret key or user session |

### orgs.list

`GET /v1/orgs` – Organizations of the caller.

- Auth: secret key or user session
- Response: 200 JSON

### orgs.create

`POST /v1/orgs` – Create an organization (the caller becomes owner).

- Auth: dashboard user session
- Response: 201 JSON (`Organization`)
- Body: application/json (`OrganizationCreate`)

### orgs.get

`GET /v1/orgs/{org}` – Organization.

- Auth: secret key or user session
- Response: 200 JSON (`Organization`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### orgs.update

`PATCH /v1/orgs/{org}` – Rename an organization.

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON (`Organization`)
- Body: application/json (`OrganizationUpdate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### orgs.delete

`DELETE /v1/orgs/{org}` – Delete an organization with all projects and data (owner, sudo).

- Auth: dashboard user session · `org:admin` · sudo
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### audit.list

`GET /v1/orgs/{org}/audit` – Audit log (keys, members, projects, config, conversions, connections).

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `limit` | string | no |  |
| `cursor` | string | no |  |
| `project` | string | no |  |
| `action` | string | no |  |

### oauthGrants.list

`GET /v1/orgs/{org}/oauth-grants` – OAuth grants into the organization (all members).

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### oauthGrants.revoke

`DELETE /v1/orgs/{org}/oauth-grants/{id}` – Revoke an OAuth grant (all its tokens).

- Auth: secret key or user session
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `id` | string | yes |  |

## Members

| Operation | Endpoint | Auth |
|---|---|---|
| [`members.list`](#memberslist) | `GET /v1/orgs/{org}/members` | secret key or user session · `org:admin` |
| [`members.update`](#membersupdate) | `PATCH /v1/orgs/{org}/members/{userId}` | dashboard user session · `org:admin` · sudo |
| [`members.remove`](#membersremove) | `DELETE /v1/orgs/{org}/members/{userId}` | dashboard user session · `org:admin` · sudo |
| [`invitations.list`](#invitationslist) | `GET /v1/orgs/{org}/invitations` | secret key or user session · `org:admin` |
| [`invitations.create`](#invitationscreate) | `POST /v1/orgs/{org}/invitations` | dashboard user session · `org:admin` · sudo |
| [`invitations.revoke`](#invitationsrevoke) | `DELETE /v1/orgs/{org}/invitations/{id}` | secret key or user session · `org:admin` |

### members.list

`GET /v1/orgs/{org}/members` – Members of an organization.

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### members.update

`PATCH /v1/orgs/{org}/members/{userId}` – Change a member's role or personal-data permission (sudo).

- Auth: dashboard user session · `org:admin` · sudo
- Response: 200 JSON (`Member`)
- Body: application/json (`MemberUpdate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `userId` | string | yes |  |

### members.remove

`DELETE /v1/orgs/{org}/members/{userId}` – Remove a member (sudo; the last owner cannot be removed).

- Auth: dashboard user session · `org:admin` · sudo
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `userId` | string | yes |  |

### invitations.list

`GET /v1/orgs/{org}/invitations` – Open and past invitations.

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### invitations.create

`POST /v1/orgs/{org}/invitations` – Invite a member (link valid 7 days, single use).

- Auth: dashboard user session · `org:admin` · sudo
- Response: 201 JSON (`InvitationCreated`)
- Body: application/json (`InvitationCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### invitations.revoke

`DELETE /v1/orgs/{org}/invitations/{id}` – Revoke an invitation.

- Auth: secret key or user session · `org:admin`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `id` | string | yes |  |

## Keys

| Operation | Endpoint | Auth |
|---|---|---|
| [`keys.list`](#keyslist) | `GET /v1/orgs/{org}/keys` | secret key or user session · `org:admin` |
| [`keys.create`](#keyscreate) | `POST /v1/orgs/{org}/keys` | secret key or user session · `org:admin` · sudo |
| [`keys.roll`](#keysroll) | `POST /v1/orgs/{org}/keys/{id}/roll` | secret key or user session · `org:admin` · sudo |
| [`keys.revoke`](#keysrevoke) | `DELETE /v1/orgs/{org}/keys/{id}` | secret key or user session · `org:admin` · sudo |
| [`publishableKeys.list`](#publishablekeyslist) | `GET /v1/projects/{project}/publishable-keys` | secret key or user session · `analytics:read` |
| [`publishableKeys.create`](#publishablekeyscreate) | `POST /v1/projects/{project}/publishable-keys` | secret key or user session · `project:admin` |
| [`publishableKeys.roll`](#publishablekeysroll) | `POST /v1/projects/{project}/publishable-keys/{id}/roll` | secret key or user session · `project:admin` |
| [`proxyKeys.list`](#proxykeyslist) | `GET /v1/projects/{project}/proxy-keys` | secret key or user session · `project:admin` |
| [`proxyKeys.create`](#proxykeyscreate) | `POST /v1/projects/{project}/proxy-keys` | secret key or user session · `project:admin` |
| [`proxyKeys.revoke`](#proxykeysrevoke) | `DELETE /v1/projects/{project}/proxy-keys/{id}` | secret key or user session · `project:admin` |
| [`publishableKeys.revoke`](#publishablekeysrevoke) | `DELETE /v1/projects/{project}/publishable-keys/{id}` | secret key or user session · `project:admin` |

### keys.list

`GET /v1/orgs/{org}/keys` – Secret keys of the organization (never the secret).

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### keys.create

`POST /v1/orgs/{org}/keys` – Create a secret key – the value is returned once (sudo for users; scopes ≤ caller's).

- Auth: secret key or user session · `org:admin` · sudo
- Response: 201 JSON (`ApiKeyCreated`)
- Body: application/json (`SecretKeyCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### keys.roll

`POST /v1/orgs/{org}/keys/{id}/roll` – Roll a secret key: new key now, the old one expires after the overlap (default 7 days).

- Auth: secret key or user session · `org:admin` · sudo
- Response: 201 JSON (`ApiKeyCreated`)
- Body: application/json (`KeyRoll`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `id` | string | yes |  |

### keys.revoke

`DELETE /v1/orgs/{org}/keys/{id}` – Revoke a secret key immediately.

- Auth: secret key or user session · `org:admin` · sudo
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `id` | string | yes |  |

### publishableKeys.list

`GET /v1/projects/{project}/publishable-keys` – Publishable keys (pk_3x_…) of the project.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### publishableKeys.create

`POST /v1/projects/{project}/publishable-keys` – Create a publishable key.

- Auth: secret key or user session · `project:admin`
- Response: 201 JSON (`ApiKeyCreated`)
- Body: application/json (`PublishableKeyCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### publishableKeys.roll

`POST /v1/projects/{project}/publishable-keys/{id}/roll` – Roll a publishable key (old key keeps working during the overlap).

- Auth: secret key or user session · `project:admin`
- Response: 201 JSON (`ApiKeyCreated`)
- Body: application/json (`KeyRoll`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### proxyKeys.list

`GET /v1/projects/{project}/proxy-keys` – First-party proxy keys of the project (secret keys with exactly ingest:proxy; never the secret).

- Auth: secret key or user session · `project:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### proxyKeys.create

`POST /v1/projects/{project}/proxy-keys` – Create a proxy key (scope ingest:proxy, bound to this project) – the value is returned once.

Requires project:admin and the right to grant ingest:proxy (owners/admins; keys and OAuth grants that hold ingest:proxy). Keys created by an OAuth grant or another key are bound to it.

- Auth: secret key or user session · `project:admin`
- Response: 201 JSON (`ApiKeyCreated`)
- Body: application/json (`ProxyKeyCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### proxyKeys.revoke

`DELETE /v1/projects/{project}/proxy-keys/{id}` – Revoke a proxy key.

- Auth: secret key or user session · `project:admin`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### publishableKeys.revoke

`DELETE /v1/projects/{project}/publishable-keys/{id}` – Revoke a publishable key.

- Auth: secret key or user session · `project:admin`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

## Projects

| Operation | Endpoint | Auth |
|---|---|---|
| [`projects.list`](#projectslist) | `GET /v1/projects` | secret key or user session |
| [`projects.create`](#projectscreate) | `POST /v1/orgs/{org}/projects` | secret key or user session · `project:create` |
| [`projects.get`](#projectsget) | `GET /v1/projects/{project}` | secret key or user session |
| [`projects.update`](#projectsupdate) | `PATCH /v1/projects/{project}` | secret key or user session · `project:admin` |
| [`projects.delete`](#projectsdelete) | `DELETE /v1/projects/{project}` | secret key or user session · `org:admin` · sudo |
| [`projects.getConfig`](#projectsgetconfig) | `GET /v1/projects/{project}/config` | secret key or user session · `analytics:read` |
| [`projects.updateConfig`](#projectsupdateconfig) | `PATCH /v1/projects/{project}/config` | secret key or user session · `project:admin` |
| [`projects.status`](#projectsstatus) | `GET /v1/projects/{project}/status` | secret key or user session · `analytics:read` |
| [`projects.purgeData`](#projectspurgedata) | `POST /v1/projects/{project}/purge` | secret key or user session · `project:admin` · sudo |

### projects.list

`GET /v1/projects` – Projects the caller can access (all orgs).

- Auth: secret key or user session
- Response: 200 JSON

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | no |  |
| `slug` | string | no |  |

### projects.create

`POST /v1/orgs/{org}/projects` – Create a project (returns it with its first publishable key); a project-narrowed key/grant gains access to it.

- Auth: secret key or user session · `project:create`
- Response: 201 JSON (`ProjectCreated`)
- Body: application/json (`ProjectCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### projects.get

`GET /v1/projects/{project}` – Project.

- Auth: secret key or user session
- Response: 200 JSON (`Project`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### projects.update

`PATCH /v1/projects/{project}` – Update project settings (name, time zone, currency, retention, limits …).

- Auth: secret key or user session · `project:admin`
- Response: 200 JSON (`Project`)
- Body: application/json (`ProjectUpdate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### projects.delete

`DELETE /v1/projects/{project}` – Delete a project and all its data (sudo).

- Auth: secret key or user session · `org:admin` · sudo
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### projects.getConfig

`GET /v1/projects/{project}/config` – Tracking configuration (domains, origins, page groups, personal pages, consent, presets …).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`ProjectTrackingConfigResponse`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### projects.updateConfig

`PATCH /v1/projects/{project}/config` – Update tracking configuration (top-level keys replace stored values; bumps configRev).

- Auth: secret key or user session · `project:admin`
- Response: 200 JSON (`ProjectTrackingConfigResponse`)
- Body: application/json (`TrackingConfigPatch`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### projects.status

`GET /v1/projects/{project}/status` – Tracking status: database, storage, cleanup, rescoring progress, budgets, proxy warning, last event.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`ProjectStatus`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### projects.purgeData

`POST /v1/projects/{project}/purge` – Delete tracking data in a date range or all of it (sudo).

- Auth: secret key or user session · `project:admin` · sudo
- Response: 200 JSON
- Body: application/json

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `scope` | "all" \| "range" \| "test" | yes |  |
| `from` | string | no |  |
| `to` | string | no |  |

## Analytics

| Operation | Endpoint | Auth |
|---|---|---|
| [`analytics.overview`](#analyticsoverview) | `GET /v1/projects/{project}/analytics/overview` | secret key or user session · `analytics:read` |
| [`analytics.timeseries`](#analyticstimeseries) | `GET /v1/projects/{project}/analytics/timeseries` | secret key or user session · `analytics:read` |
| [`analytics.funnel`](#analyticsfunnel) | `GET /v1/projects/{project}/analytics/funnel` | secret key or user session · `analytics:read` |
| [`analytics.breakdown`](#analyticsbreakdown) | `GET /v1/projects/{project}/analytics/breakdown` | secret key or user session · `analytics:read` |
| [`analytics.relevanceRanking`](#analyticsrelevanceranking) | `GET /v1/projects/{project}/analytics/relevance-ranking` | secret key or user session · `analytics:read` |
| [`analytics.pages`](#analyticspages) | `GET /v1/projects/{project}/analytics/pages` | secret key or user session · `analytics:read` |
| [`analytics.scroll`](#analyticsscroll) | `GET /v1/projects/{project}/analytics/scroll` | secret key or user session · `analytics:read` |
| [`analytics.sections`](#analyticssections) | `GET /v1/projects/{project}/analytics/sections` | secret key or user session · `analytics:read` |
| [`analytics.engagement`](#analyticsengagement) | `GET /v1/projects/{project}/analytics/engagement` | secret key or user session · `analytics:read` |
| [`analytics.clicks`](#analyticsclicks) | `GET /v1/projects/{project}/analytics/clicks` | secret key or user session · `analytics:read` |
| [`analytics.videos`](#analyticsvideos) | `GET /v1/projects/{project}/analytics/videos` | secret key or user session · `analytics:read` |
| [`analytics.videoRetention`](#analyticsvideoretention) | `GET /v1/projects/{project}/analytics/videos/{video}/retention` | secret key or user session · `analytics:read` |
| [`analytics.answers`](#analyticsanswers) | `GET /v1/projects/{project}/analytics/answers` | secret key or user session · `analytics:read` |
| [`analytics.flow`](#analyticsflow) | `GET /v1/projects/{project}/analytics/flow` | secret key or user session · `analytics:read` |
| [`analytics.goals`](#analyticsgoals) | `GET /v1/projects/{project}/analytics/goals` | secret key or user session · `analytics:read` |
| [`analytics.filterOptions`](#analyticsfilteroptions) | `GET /v1/projects/{project}/analytics/filter-options` | secret key or user session · `analytics:read` |
| [`analytics.events`](#analyticsevents) | `GET /v1/projects/{project}/analytics/events` | secret key or user session · `analytics:read` |
| [`sql.query`](#sqlquery) | `POST /v1/projects/{project}/sql` | secret key or user session · `analytics:read` |
| [`schema.get`](#schemaget) | `GET /v1/projects/{project}/schema` | secret key or user session · `analytics:read` |

### analytics.overview

`GET /v1/projects/{project}/analytics/overview` – KPIs, extras, time series, default funnel, previous-period comparison.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`Overview`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `bucket` | "hour" \| "day" \| "week" | no |  |
| `compare` | "0" \| "1" \| "true" \| "false" | no |  |

### analytics.timeseries

`GET /v1/projects/{project}/analytics/timeseries` – Zero-filled series of sessions, visitors, pageviews, conversions.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `bucket` | "hour" \| "day" \| "week" | no |  |

### analytics.funnel

`GET /v1/projects/{project}/analytics/funnel` – Funnel steps (default funnel or ?funnel=<key>).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `funnel` | string | no |  |

### analytics.breakdown

`GET /v1/projects/{project}/analytics/breakdown` – Full session aggregate per value of a dimension.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`Breakdown`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `dimension` | "channel" \| "utm_source" \| "utm_medium" \| "utm_campaign" \| "utm_content" \| "utm_term" \| "utm_id" \| "ref_host" \| "entry_page" \| "entry_group" \| "variant" \| "device" \| "browser" \| "os" \| "country" \| "lang" \| "exit_page" \| "exit_group" \| "entry_host" \| "screen" \| "stitch" \| "attr_from" \| "click_id" \| "converted" \| "quiz_answers" \| "consent" \| "relevance_tier" \| "goal" \| "ad_platform" \| "ad_campaign" \| "ad_adset" \| "ad" \| "day" \| "week" \| "month" \| "hour_of_day" \| "weekday" | yes |  |
| `sort` | string | no |  |
| `dir` | "asc" \| "desc" | no |  |
| `limit` | string | no |  |
| `min_sessions` | string | no |  |

### analytics.relevanceRanking

`GET /v1/projects/{project}/analytics/relevance-ranking` – Rank values of a dimension by relevance (default utm_campaign).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`RelevanceRanking`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `dimension` | "channel" \| "utm_source" \| "utm_medium" \| "utm_campaign" \| "utm_content" \| "utm_term" \| "utm_id" \| "ref_host" \| "entry_page" \| "entry_group" \| "variant" \| "device" \| "browser" \| "os" \| "country" \| "lang" | no |  |
| `sort` | "avg" \| "hot" \| "hot_rate" \| "engaged_rate" \| "sessions" | no |  |
| `min_sessions` | string | no |  |
| `limit` | string | no |  |

### analytics.pages

`GET /v1/projects/{project}/analytics/pages` – Pages: pageviews, sessions, entries, exits, engagement, scroll, fold-only, clicks, video.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `limit` | string | no |  |

### analytics.scroll

`GET /v1/projects/{project}/analytics/scroll` – Scroll curve, fold-only rate, fold share, milestones (restored scroll positions excluded).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |

### analytics.sections

`GET /v1/projects/{project}/analytics/sections` – Section reach per page.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `limit` | string | no |  |

### analytics.engagement

`GET /v1/projects/{project}/analytics/engagement` – Engaged-time distribution per pageview or session.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `level` | "pageview" \| "session" | no |  |

### analytics.clicks

`GET /v1/projects/{project}/analytics/clicks` – Top clicks by label/kind/tag/href/section.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `limit` | string | no |  |

### analytics.videos

`GET /v1/projects/{project}/analytics/videos` – Video plays, start rate, watched time, coverage, completion, retention.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |

### analytics.videoRetention

`GET /v1/projects/{project}/analytics/videos/{video}/retention` – Downsampled retention curve of one video, overall and per format.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `video` | string | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `step` | string | no |  |

### analytics.answers

`GET /v1/projects/{project}/analytics/answers` – Questionnaire (quiz) funnel and option distribution per question.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `questionnaire` | string | no |  |

### analytics.flow

`GET /v1/projects/{project}/analytics/flow` – Entry pages, exit pages and page transitions.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `limit` | string | no |  |

### analytics.goals

`GET /v1/projects/{project}/analytics/goals` – Goal completions (sessions, rate, value).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |

### analytics.filterOptions

`GET /v1/projects/{project}/analytics/filter-options` – Top values per filter dimension for the date range.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`FilterOptions`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |

### analytics.events

`GET /v1/projects/{project}/analytics/events` – Raw events with session context (names, prop containment, prop text search).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`EventsQueryResult`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `names` | string \| string[] | no |  |
| `props` | string | no |  |
| `prop_key` | string | no |  |
| `prop_text` | string | no |  |
| `session_id` | string | no |  |
| `order` | "asc" \| "desc" | no |  |
| `limit` | string | no |  |

### sql.query

`POST /v1/projects/{project}/sql` – Read-only SQL over the project's analytics tables (one SELECT/WITH, row-level isolated).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SqlResult`)
- Body: application/json

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `query` | string | yes |  |
| `maxRows` | int | no |  |

### schema.get

`GET /v1/projects/{project}/schema` – Tables and columns available to SQL, filter semantics, conventions.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SchemaDoc`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

## Sessions

| Operation | Endpoint | Auth |
|---|---|---|
| [`sessions.list`](#sessionslist) | `GET /v1/projects/{project}/sessions` | secret key or user session · `analytics:read` |
| [`sessions.get`](#sessionsget) | `GET /v1/projects/{project}/sessions/{sessionId}` | secret key or user session · `analytics:read` |

### sessions.list

`GET /v1/projects/{project}/sessions` – Sessions explorer (sortable, paginated).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SessionList`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `sort` | "started_at" \| "relevance" \| "engaged" \| "pages" \| "clicks" \| "scroll" \| "video" | no |  |
| `dir` | "asc" \| "desc" | no |  |
| `limit` | string | no |  |
| `offset` | string | no |  |

### sessions.get

`GET /v1/projects/{project}/sessions/{sessionId}` – One session: attribution, relevance breakdown, pageviews with merged timeline, conversions, leads.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SessionDetail`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `sessionId` | string | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `sections` | "0" \| "1" \| "true" \| "false" | no |  |
| `locale` | "en" \| "de" | no |  |

## Goals & funnels

| Operation | Endpoint | Auth |
|---|---|---|
| [`goals.list`](#goalslist) | `GET /v1/projects/{project}/goals` | secret key or user session · `analytics:read` |
| [`goals.create`](#goalscreate) | `POST /v1/projects/{project}/goals` | secret key or user session · `config:write` |
| [`goals.update`](#goalsupdate) | `PATCH /v1/projects/{project}/goals/{key}` | secret key or user session · `config:write` |
| [`goals.delete`](#goalsdelete) | `DELETE /v1/projects/{project}/goals/{key}` | secret key or user session · `config:write` |
| [`funnels.list`](#funnelslist) | `GET /v1/projects/{project}/funnels` | secret key or user session · `analytics:read` |
| [`funnels.create`](#funnelscreate) | `POST /v1/projects/{project}/funnels` | secret key or user session · `config:write` |
| [`funnels.update`](#funnelsupdate) | `PATCH /v1/projects/{project}/funnels/{key}` | secret key or user session · `config:write` |
| [`funnels.delete`](#funnelsdelete) | `DELETE /v1/projects/{project}/funnels/{key}` | secret key or user session · `config:write` |
| [`questionnaires.list`](#questionnaireslist) | `GET /v1/projects/{project}/questionnaires` | secret key or user session · `analytics:read` |
| [`questionnaires.upsert`](#questionnairesupsert) | `PUT /v1/projects/{project}/questionnaires/{id}` | secret key or user session · `config:write` |
| [`questionnaires.delete`](#questionnairesdelete) | `DELETE /v1/projects/{project}/questionnaires/{id}` | secret key or user session · `config:write` |

### goals.list

`GET /v1/projects/{project}/goals` – Goals.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `archived` | "0" \| "1" \| "true" \| "false" | no |  |

### goals.create

`POST /v1/projects/{project}/goals` – Create a goal (bumps goalsVersion → sessions are recomputed in the background).

- Auth: secret key or user session · `config:write`
- Response: 201 JSON (`Goal`)
- Body: application/json (`GoalWrite`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### goals.update

`PATCH /v1/projects/{project}/goals/{key}` – Update a goal.

- Auth: secret key or user session · `config:write`
- Response: 200 JSON (`Goal`)
- Body: application/json

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `key` | string | yes |  |

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `name` | string | no |  |
| `match` | object | no |  |
| `isConversion` | boolean | no |  |
| `value` | number \| null | no |  |
| `sort` | int | no |  |
| `archived` | boolean | no |  |

### goals.delete

`DELETE /v1/projects/{project}/goals/{key}` – Delete a goal.

- Auth: secret key or user session · `config:write`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `key` | string | yes |  |

### funnels.list

`GET /v1/projects/{project}/funnels` – Funnel definitions.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### funnels.create

`POST /v1/projects/{project}/funnels` – Create a funnel.

- Auth: secret key or user session · `config:write`
- Response: 201 JSON (`Funnel`)
- Body: application/json (`FunnelWrite`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### funnels.update

`PATCH /v1/projects/{project}/funnels/{key}` – Update a funnel.

- Auth: secret key or user session · `config:write`
- Response: 200 JSON (`Funnel`)
- Body: application/json

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `key` | string | yes |  |

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `name` | string | no |  |
| `mode` | "independent" \| "sequential" | no |  |
| `steps` | object[] | no |  |
| `isDefault` | boolean | no |  |
| `sort` | int | no |  |

### funnels.delete

`DELETE /v1/projects/{project}/funnels/{key}` – Delete a funnel.

- Auth: secret key or user session · `config:write`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `key` | string | yes |  |

### questionnaires.list

`GET /v1/projects/{project}/questionnaires` – Questionnaire (quiz) definitions.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### questionnaires.upsert

`PUT /v1/projects/{project}/questionnaires/{id}` – Create or replace a questionnaire (validates quiz_answer events, labels answer analytics).

- Auth: secret key or user session · `config:write`
- Response: 200 JSON (`Questionnaire`)
- Body: application/json

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `label` | string \| object | yes |  |
| `questions` | object[] | yes |  |

### questionnaires.delete

`DELETE /v1/projects/{project}/questionnaires/{id}` – Delete a questionnaire.

- Auth: secret key or user session · `config:write`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

## Relevance

| Operation | Endpoint | Auth |
|---|---|---|
| [`relevance.get`](#relevanceget) | `GET /v1/projects/{project}/relevance` | secret key or user session · `analytics:read` |
| [`relevance.update`](#relevanceupdate) | `PUT /v1/projects/{project}/relevance` | secret key or user session · `config:write` |
| [`relevance.preview`](#relevancepreview) | `POST /v1/projects/{project}/relevance/preview` | secret key or user session · `analytics:read` |
| [`relevance.rescore`](#relevancerescore) | `POST /v1/projects/{project}/relevance/rescore` | secret key or user session · `config:write` |

### relevance.get

`GET /v1/projects/{project}/relevance` – Active relevance model, version history and rescoring progress.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`RelevanceState`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### relevance.update

`PUT /v1/projects/{project}/relevance` – Save a new relevance model version (all sessions are rescored retroactively).

- Auth: secret key or user session · `config:write`
- Response: 200 JSON (`RelevanceState`)
- Body: application/json

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `config` | object | yes |  |
| `note` | string | no |  |

### relevance.preview

`POST /v1/projects/{project}/relevance/preview` – Score distribution of a proposed model on recent sessions (nothing is stored).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`RelevancePreview`)
- Body: application/json

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `config` | object | yes |  |
| `sample` | int | no |  |

### relevance.rescore

`POST /v1/projects/{project}/relevance/rescore` – Run a rescoring chunk now (normally done by the scheduler).

- Auth: secret key or user session · `config:write`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

## Conversions

| Operation | Endpoint | Auth |
|---|---|---|
| [`conversions.list`](#conversionslist) | `GET /v1/projects/{project}/conversions` | secret key or user session · `conversions:read` |
| [`conversions.create`](#conversionscreate) | `POST /v1/projects/{project}/conversions` | secret key or user session · `conversions:write` · `Idempotency-Key` |
| [`conversions.get`](#conversionsget) | `GET /v1/projects/{project}/conversions/{id}` | secret key or user session · `conversions:read` |
| [`conversions.retry`](#conversionsretry) | `POST /v1/projects/{project}/conversions/{id}/retry` | secret key or user session · `conversions:write` |
| [`conversions.updateStatus`](#conversionsupdatestatus) | `PATCH /v1/projects/{project}/conversions/{id}/status` | secret key or user session · `conversions:write` |
| [`conversions.beacon`](#conversionsbeacon) | `POST /v1/cb` | publishable key (`pk_3x_…`) |
| [`conversions.googleAdsFeed`](#conversionsgoogleadsfeed) | `GET /v1/projects/{project}/feeds/google-ads.csv` | public |
| [`conversionDefinitions.list`](#conversiondefinitionslist) | `GET /v1/projects/{project}/conversion-definitions` | secret key or user session · `conversions:read` |
| [`conversionDefinitions.upsert`](#conversiondefinitionsupsert) | `PUT /v1/projects/{project}/conversion-definitions/{key}` | secret key or user session · `conversions:write` + `config:write` |
| [`conversionDefinitions.delete`](#conversiondefinitionsdelete) | `DELETE /v1/projects/{project}/conversion-definitions/{key}` | secret key or user session · `conversions:write` + `config:write` |
| [`destinations.list`](#destinationslist) | `GET /v1/projects/{project}/destinations` | secret key or user session · `conversions:read` |
| [`destinations.update`](#destinationsupdate) | `PUT /v1/projects/{project}/destinations/{provider}` | secret key or user session · `project:admin` · sudo |
| [`destinations.test`](#destinationstest) | `POST /v1/projects/{project}/destinations/{provider}/test` | secret key or user session · `project:admin` |

### conversions.list

`GET /v1/projects/{project}/conversions` – Conversions with per-provider upload status (never hashes, IP, user agent).

- Auth: secret key or user session · `conversions:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `status` | "held" \| "pending" \| "uploaded" \| "skipped" | no |  |
| `source` | "quiz" \| "booking" \| "whatsapp_manual" \| "manual" \| "event" \| "goal" \| "lead" \| "api" | no |  |
| `definition` | string | no |  |
| `session_id` | string | no |  |
| `provider` | "meta" \| "linkedin" \| "google_ads" \| "tiktok" | no |  |
| `provider_status` | "pending" \| "uploaded" \| "feed" \| "failed" \| "skipped" \| "not_configured" \| "dry_run" \| "error" | no |  |
| `days` | string | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `limit` | string | no |  |
| `cursor` | string | no |  |

### conversions.create

`POST /v1/projects/{project}/conversions` – Record a conversion and upload it to the configured destinations.

sessionId (from the browser's getIds()) or match {at, windowMinutes, …} to find the session; contact data is hashed on receipt. Ambiguous matches return ≤ 10 candidates. Idempotent via eventId and the Idempotency-Key header.

- Auth: secret key or user session · `conversions:write` · `Idempotency-Key`
- Response: 200 JSON (`ConversionCreateResult`)
- Body: application/json (`ConversionCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### conversions.get

`GET /v1/projects/{project}/conversions/{id}` – One conversion.

- Auth: secret key or user session · `conversions:read`
- Response: 200 JSON (`Conversion`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### conversions.retry

`POST /v1/projects/{project}/conversions/{id}/retry` – Retry uploads (default: every provider not uploaded/feed); force resets attempts.

- Auth: secret key or user session · `conversions:write`
- Response: 200 JSON (`Conversion`)
- Body: application/json (`ConversionRetry`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### conversions.updateStatus

`PATCH /v1/projects/{project}/conversions/{id}/status` – Manual bookkeeping of conversion/provider status.

- Auth: secret key or user session · `conversions:write`
- Response: 200 JSON (`Conversion`)
- Body: application/json (`ConversionStatusUpdate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### conversions.beacon

`POST /v1/cb` – Browser conversion beacon (booking parity): allowed origins only, ≤ 4 KB, 4/min/IP, one per session and definition.

- Auth: publishable key (`pk_3x_…`)
- Response: 202, no body
- Body: text/plain

Body fields:

| Name | Type | Required | Description |
|---|---|---|---|
| `k` | string | yes |  |
| `definition` | string | yes |  |
| `eventId` | string | yes |  |
| `sid` | string \| null | yes |  |
| `pv` | string \| null | yes |  |
| `url` | string \| null | yes |  |
| `attr` | object \| null | yes |  |
| `test` | boolean | yes |  |
| `em` | string | no |  |
| `emg` | string | no |  |
| `fn` | string | no |  |
| `ln` | string | no |  |
| `ph` | string | no |  |
| `fbp` | string | no |  |
| `fbc` | string | no |  |
| `ttp` | string | no |  |

### conversions.googleAdsFeed

`GET /v1/projects/{project}/feeds/google-ads.csv` – Google Ads scheduled-upload CSV (HTTP Basic with the feed credentials; last 60 days).

- Auth: public
- Response: 200 csv

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `source` | string | no |  |
| `definition` | string | no |  |

### conversionDefinitions.list

`GET /v1/projects/{project}/conversion-definitions` – Conversion definitions (trigger → provider events).

- Auth: secret key or user session · `conversions:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### conversionDefinitions.upsert

`PUT /v1/projects/{project}/conversion-definitions/{key}` – Create or replace a conversion definition.

- Auth: secret key or user session · `conversions:write` + `config:write`
- Response: 200 JSON (`ConversionDefinition`)
- Body: application/json (`ConversionDefinitionWrite`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `key` | string | yes |  |

### conversionDefinitions.delete

`DELETE /v1/projects/{project}/conversion-definitions/{key}` – Delete a conversion definition (existing conversions keep it as text).

- Auth: secret key or user session · `conversions:write` + `config:write`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `key` | string | yes |  |

### destinations.list

`GET /v1/projects/{project}/destinations` – Conversion destinations (Meta, LinkedIn, Google Ads, TikTok) – secrets are never returned.

- Auth: secret key or user session · `conversions:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### destinations.update

`PUT /v1/projects/{project}/destinations/{provider}` – Configure a destination (settings, encrypted secrets, transport direct/composio/feed, mode).

- Auth: secret key or user session · `project:admin` · sudo
- Response: 200 JSON (`Destination`)
- Body: application/json (`DestinationWrite`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `provider` | "meta" \| "linkedin" \| "google_ads" \| "tiktok" | yes |  |

### destinations.test

`POST /v1/projects/{project}/destinations/{provider}/test` – Send a test event (Meta test_event_code, Google validateOnly, LinkedIn/TikTok validation).

- Auth: secret key or user session · `project:admin`
- Response: 200 JSON (`DestinationTestResult`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `provider` | "meta" \| "linkedin" \| "google_ads" \| "tiktok" | yes |  |

## Leads

| Operation | Endpoint | Auth |
|---|---|---|
| [`leads.list`](#leadslist) | `GET /v1/projects/{project}/leads` | secret key or user session · `leads:read` |
| [`leads.get`](#leadsget) | `GET /v1/projects/{project}/leads/{id}` | secret key or user session · `leads:read` |
| [`leads.create`](#leadscreate) | `POST /v1/projects/{project}/leads` | secret key or user session · `leads:write` · `Idempotency-Key` |
| [`leads.delete`](#leadsdelete) | `DELETE /v1/projects/{project}/leads/{id}` | secret key or user session · `leads:write` |
| [`leads.export`](#leadsexport) | `GET /v1/projects/{project}/leads.csv` | secret key or user session · `leads:read` |

### leads.list

`GET /v1/projects/{project}/leads` – Leads (personal data).

- Auth: secret key or user session · `leads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `days` | string | no |  |
| `form` | string | no |  |
| `source` | "identify" \| "api" \| "import" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_spam` | "0" \| "1" \| "true" \| "false" | no |  |
| `limit` | string | no |  |
| `cursor` | string | no |  |

### leads.get

`GET /v1/projects/{project}/leads/{id}` – One lead.

- Auth: secret key or user session · `leads:read`
- Response: 200 JSON (`Lead`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### leads.create

`POST /v1/projects/{project}/leads` – Record a lead from the site's server (optionally with a conversion).

- Auth: secret key or user session · `leads:write` · `Idempotency-Key`
- Response: 201 JSON
- Body: application/json (`LeadCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### leads.delete

`DELETE /v1/projects/{project}/leads/{id}` – Erase a lead (GDPR); linked conversions lose their hashed identifiers.

- Auth: secret key or user session · `leads:write`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### leads.export

`GET /v1/projects/{project}/leads.csv` – CSV export of leads.

- Auth: secret key or user session · `leads:read`
- Response: 200 csv

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `days` | string | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_spam` | "0" \| "1" \| "true" \| "false" | no |  |

## Ads

| Operation | Endpoint | Auth |
|---|---|---|
| [`ads.connections.list`](#adsconnectionslist) | `GET /v1/projects/{project}/ad-connections` | secret key or user session · `ads:read` |
| [`ads.connections.create`](#adsconnectionscreate) | `POST /v1/projects/{project}/ad-connections` | dashboard user session · `ads:write` · sudo |
| [`ads.connections.complete`](#adsconnectionscomplete) | `POST /v1/projects/{project}/ad-connections/{id}/complete` | dashboard user session · `ads:write` |
| [`ads.connections.discover`](#adsconnectionsdiscover) | `GET /v1/projects/{project}/ad-connections/{id}/accounts` | secret key or user session · `ads:write` |
| [`ads.connections.delete`](#adsconnectionsdelete) | `DELETE /v1/projects/{project}/ad-connections/{id}` | secret key or user session · `ads:write` · sudo |
| [`ads.accounts.list`](#adsaccountslist) | `GET /v1/projects/{project}/ad-accounts` | secret key or user session · `ads:read` |
| [`ads.accounts.select`](#adsaccountsselect) | `PUT /v1/projects/{project}/ad-accounts` | secret key or user session · `ads:write` |
| [`ads.sync.run`](#adssyncrun) | `POST /v1/projects/{project}/ads/sync` | secret key or user session · `ads:write` |
| [`ads.sync.runs`](#adssyncruns) | `GET /v1/projects/{project}/ads/sync-runs` | secret key or user session · `ads:read` |
| [`ads.report`](#adsreport) | `GET /v1/projects/{project}/ads/report` | secret key or user session · `ads:read` |
| [`ads.timeseries`](#adstimeseries) | `GET /v1/projects/{project}/ads/timeseries` | secret key or user session · `ads:read` |
| [`ads.unmatched`](#adsunmatched) | `GET /v1/projects/{project}/ads/unmatched` | secret key or user session · `ads:read` |
| [`ads.entities`](#adsentities) | `GET /v1/projects/{project}/ads/entities` | secret key or user session · `ads:read` |
| [`ads.mappingRules.list`](#adsmappingruleslist) | `GET /v1/projects/{project}/ads/mapping-rules` | secret key or user session · `ads:read` |
| [`ads.mappingRules.create`](#adsmappingrulescreate) | `POST /v1/projects/{project}/ads/mapping-rules` | secret key or user session · `ads:write` |
| [`ads.mappingRules.delete`](#adsmappingrulesdelete) | `DELETE /v1/projects/{project}/ads/mapping-rules/{id}` | secret key or user session · `ads:write` |
| [`ads.utmTemplates`](#adsutmtemplates) | `GET /v1/projects/{project}/ads/utm-templates` | secret key or user session · `ads:read` |
| [`ads.orgConnections.list`](#adsorgconnectionslist) | `GET /v1/orgs/{org}/ad-connections` | secret key or user session · `org:admin` |
| [`ads.orgConnections.create`](#adsorgconnectionscreate) | `POST /v1/orgs/{org}/ad-connections` | dashboard user session · `org:admin` · sudo |
| [`ads.orgConnections.complete`](#adsorgconnectionscomplete) | `POST /v1/orgs/{org}/ad-connections/{id}/complete` | dashboard user session · `org:admin` |
| [`ads.orgConnections.discover`](#adsorgconnectionsdiscover) | `GET /v1/orgs/{org}/ad-connections/{id}/accounts` | secret key or user session · `org:admin` |
| [`ads.orgConnections.delete`](#adsorgconnectionsdelete) | `DELETE /v1/orgs/{org}/ad-connections/{id}` | dashboard user session · `org:admin` · sudo |
| [`ads.webhook`](#adswebhook) | `POST /v1/webhooks/composio` | webhook signature |

### ads.connections.list

`GET /v1/projects/{project}/ad-connections` – Ad-platform connections of the project.

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.connections.create

`POST /v1/projects/{project}/ad-connections` – Start connecting an ad platform (returns the hosted OAuth URL, valid 10 minutes).

- Auth: dashboard user session · `ads:write` · sudo
- Response: 201 JSON (`AdConnectionStarted`)
- Body: application/json (`AdConnectionCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.connections.complete

`POST /v1/projects/{project}/ad-connections/{id}/complete` – Finish the OAuth callback (nonce + connected account verified) and discover ad accounts.

- Auth: dashboard user session · `ads:write`
- Response: 200 JSON
- Body: application/json (`AdConnectionComplete`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### ads.connections.discover

`GET /v1/projects/{project}/ad-connections/{id}/accounts` – Ad accounts reachable through a connection.

- Auth: secret key or user session · `ads:write`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### ads.connections.delete

`DELETE /v1/projects/{project}/ad-connections/{id}` – Disconnect (revoke at Composio); cost data is kept unless purge=true.

- Auth: secret key or user session · `ads:write` · sudo
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `purge` | "true" \| "false" | no |  |

### ads.accounts.list

`GET /v1/projects/{project}/ad-accounts` – Ad accounts assigned to the project.

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.accounts.select

`PUT /v1/projects/{project}/ad-accounts` – Assign ad accounts of a connection to the project (starts a 90-day backfill).

- Auth: secret key or user session · `ads:write`
- Response: 200 JSON
- Body: application/json (`AdAccountsSelect`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.sync.run

`POST /v1/projects/{project}/ads/sync` – Queue a sync (entities, metrics, Google click_view, backfill, session matching).

- Auth: secret key or user session · `ads:write`
- Response: 202 JSON
- Body: application/json (`AdSyncRequest`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.sync.runs

`GET /v1/projects/{project}/ads/sync-runs` – Recent sync runs and their status.

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `limit` | string | no |  |
| `platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |

### ads.report

`GET /v1/projects/{project}/ads/report` – Spend × sessions × conversions: CPC, cost per session, cost per relevant session, CPL, ROAS.

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `level` | "platform" \| "campaign" \| "adset" \| "ad" | no |  |
| `sort` | string | no |  |
| `dir` | "asc" \| "desc" | no |  |
| `limit` | string | no |  |
| `relevant_min` | string | no |  |

### ads.timeseries

`GET /v1/projects/{project}/ads/timeseries` – Daily/weekly spend next to sessions, relevant sessions and conversions.

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `bucket` | "day" \| "week" | no |  |

### ads.unmatched

`GET /v1/projects/{project}/ads/unmatched` – Paid sessions whose UTMs could not be tied to an ad entity (for manual mapping).

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | Start day YYYY-MM-DD (project time zone, inclusive). Wins over preset. |
| `to` | string | no | End day YYYY-MM-DD (inclusive). Default today. |
| `preset` | "today" \| "yesterday" \| "7d" \| "14d" \| "30d" \| "90d" \| "365d" \| "this_month" \| "last_month" \| "all" | no | Date range when from/to are absent. Default 30d; all = last 731 days. |
| `page` | string | no | Canonical page path, e.g. /pricing |
| `page_group` | string | no | Page group key from the project config |
| `page_mode` | "any" \| "entry" \| "exit" | no | Session metrics: any = a pageview on page/page_group (default), entry = entered there, exit = left there |
| `channel` | "google_ads" \| "google_organic" \| "bing" \| "search_other" \| "meta" \| "linkedin" \| "tiktok" \| "social_other" \| "email" \| "ai_assistant" \| "referral" \| "direct" \| "other" | no |  |
| `utm_source` | string | no |  |
| `utm_medium` | string | no |  |
| `utm_campaign` | string | no |  |
| `utm_content` | string | no |  |
| `utm_term` | string | no |  |
| `utm_id` | string | no |  |
| `ref_host` | string | no | Entry referrer host, e.g. www.google.com |
| `variant` | string | no | Page variant of the entry pageview |
| `device` | "mobile" \| "tablet" \| "desktop" \| "unknown" | no |  |
| `browser` | "chrome" \| "safari" \| "firefox" \| "edge" \| "samsung" \| "opera" \| "other" | no |  |
| `os` | "windows" \| "macos" \| "ios" \| "android" \| "linux" \| "chromeos" \| "other" | no |  |
| `country` | string | no | ISO country code, uppercase |
| `lang` | string | no |  |
| `screen` | "xs" \| "sm" \| "md" \| "lg" \| "xl" | no |  |
| `converted` | "0" \| "1" \| "true" \| "false" | no |  |
| `has` | string \| string[] | no |  |
| `goal` | string \| string[] | no |  |
| `event` | string \| string[] | no |  |
| `click_id` | "any" \| "none" \| "gclid" \| "gbraid" \| "wbraid" \| "fbclid" \| "li_fat_id" \| "ttclid" \| "msclkid" | no | any/none or a specific click id present on the session |
| `traffic` | "paid" \| "organic" | no | paid = an ad click id or a paid utm_medium (cpc, paid, …) on the session; organic = neither |
| `stitch` | "new" \| "memory" \| "storage" \| "hash" | no |  |
| `consent` | "0" \| "1" \| "true" \| "false" | no |  |
| `include_test` | "0" \| "1" \| "true" \| "false" | no |  |
| `min_relevance` | string | no |  |
| `max_relevance` | string | no |  |
| `relevance_tier` | string | no | Tier key, e.g. hot |
| `ad_platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `ad_account_id` | string | no |  |
| `ad_campaign_id` | string | no |  |
| `ad_adset_id` | string | no |  |
| `ad_id` | string | no |  |
| `ad_match` | "matched" \| "unmatched" | no | Sessions with/without an ad-entity match |
| `limit` | string | no |  |

### ads.entities

`GET /v1/projects/{project}/ads/entities` – Synced campaigns, ad sets and ads (search by name/id).

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `platform` | "google_ads" \| "meta_ads" \| "linkedin_ads" \| "tiktok_ads" | no |  |
| `level` | "campaign" \| "adset" \| "ad" | no |  |
| `q` | string | no |  |
| `limit` | string | no |  |

### ads.mappingRules.list

`GET /v1/projects/{project}/ads/mapping-rules` – Manual UTM → ad entity mapping rules.

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.mappingRules.create

`POST /v1/projects/{project}/ads/mapping-rules` – Create a mapping rule (sessions are re-matched in the background).

- Auth: secret key or user session · `ads:write`
- Response: 201 JSON (`AdMappingRule`)
- Body: application/json (`AdMappingRuleWrite`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.mappingRules.delete

`DELETE /v1/projects/{project}/ads/mapping-rules/{id}` – Delete a mapping rule.

- Auth: secret key or user session · `ads:write`
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `id` | string | yes |  |

### ads.utmTemplates

`GET /v1/projects/{project}/ads/utm-templates` – Recommended ID-based UTM templates per platform.

- Auth: secret key or user session · `ads:read`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### ads.orgConnections.list

`GET /v1/orgs/{org}/ad-connections` – Org-level ad-platform connections (usable by every project of the organization).

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### ads.orgConnections.create

`POST /v1/orgs/{org}/ad-connections` – Start an org-level ad-platform connection (returns the hosted OAuth URL, valid 10 minutes).

- Auth: dashboard user session · `org:admin` · sudo
- Response: 201 JSON (`AdConnectionStarted`)
- Body: application/json (`AdConnectionCreate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |

### ads.orgConnections.complete

`POST /v1/orgs/{org}/ad-connections/{id}/complete` – Finish the OAuth callback of an org-level connection and discover ad accounts.

- Auth: dashboard user session · `org:admin`
- Response: 200 JSON
- Body: application/json (`AdConnectionComplete`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `id` | string | yes |  |

### ads.orgConnections.discover

`GET /v1/orgs/{org}/ad-connections/{id}/accounts` – Ad accounts reachable through an org-level connection.

- Auth: secret key or user session · `org:admin`
- Response: 200 JSON

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `id` | string | yes |  |

### ads.orgConnections.delete

`DELETE /v1/orgs/{org}/ad-connections/{id}` – Disconnect an org-level connection (its accounts are removed from every project); cost data is kept unless purge=true.

- Auth: dashboard user session · `org:admin` · sudo
- Response: 204, no body

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `org` | string | yes |  |
| `id` | string | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `purge` | "true" \| "false" | no |  |

### ads.webhook

`POST /v1/webhooks/composio` – Composio webhook (connected account expired/activated); signature verified.

- Auth: webhook signature
- Response: 204, no body

## Setup

| Operation | Endpoint | Auth |
|---|---|---|
| [`setup.frameworks`](#setupframeworks) | `GET /v1/setup/frameworks` | public |
| [`setup.detect`](#setupdetect) | `POST /v1/setup/detect` | public |
| [`setup.instructions`](#setupinstructions) | `GET /v1/setup/instructions` | public |
| [`setup.snippet`](#setupsnippet) | `GET /v1/projects/{project}/setup/snippet` | secret key or user session · `project:admin` |
| [`setup.verify`](#setupverify) | `POST /v1/projects/{project}/setup/verify` | secret key or user session · `project:admin` |
| [`setup.status`](#setupstatus) | `GET /v1/projects/{project}/setup/status` | secret key or user session · `analytics:read` |
| [`setup.progress`](#setupprogress) | `PATCH /v1/projects/{project}/setup` | secret key or user session · `analytics:read` |
| [`setup.installation`](#setupinstallation) | `GET /v1/projects/{project}/setup/installation` | secret key or user session · `analytics:read` |
| [`setup.prompts`](#setupprompts) | `GET /v1/projects/{project}/setup/prompts` | secret key or user session · `analytics:read` |
| [`setup.prompt`](#setupprompt) | `GET /v1/projects/{project}/setup/prompts/{kind}` | secret key or user session · `analytics:read` |
| [`setup.testConversion`](#setuptestconversion) | `POST /v1/projects/{project}/setup/test-conversion` | secret key or user session · `project:admin` |
| [`setup.mcpActivity`](#setupmcpactivity) | `GET /v1/projects/{project}/setup/mcp-activity` | secret key or user session · `analytics:read` |
| [`setup.detectStack`](#setupdetectstack) | `POST /v1/projects/{project}/setup/detect-stack` | secret key or user session · `analytics:read` |

### setup.frameworks

`GET /v1/setup/frameworks` – Supported frameworks and proxy options.

- Auth: public
- Response: 200 JSON

### setup.detect

`POST /v1/setup/detect` – Detect framework/router/proxy option from package.json dependencies and a file list.

- Auth: public
- Response: 200 JSON (`DetectStackResult`)
- Body: application/json (`DetectStackInput`)

### setup.instructions

`GET /v1/setup/instructions` – Ordered install steps (files, code, env vars, commands) for a framework.

- Auth: public
- Response: 200 JSON (`SetupInstructions`)

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `framework` | "next-app" \| "next-pages" \| "react-vite" \| "astro" \| "wordpress" \| "webflow-html" \| "shopify" \| "other" | yes |  |
| `proxy` | "route-handler" \| "cloudflare-worker" \| "nginx" \| "netlify" \| "wordpress" \| "cname" \| "none" | no |  |
| `consent` | "none" \| "custom" \| "cookiebot" \| "usercentrics" \| "complianz" \| "borlabs" \| "google_consent_mode" | no |  |
| `typescript` | "true" \| "false" | no |  |
| `project` | string | no |  |

### setup.snippet

`GET /v1/projects/{project}/setup/snippet` – Install code with the project's real publishable key, API origin and proxy path.

- Auth: secret key or user session · `project:admin`
- Response: 200 JSON (`SetupInstructions`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `framework` | "next-app" \| "next-pages" \| "react-vite" \| "astro" \| "wordpress" \| "webflow-html" \| "shopify" \| "other" | yes |  |
| `proxy` | "route-handler" \| "cloudflare-worker" \| "nginx" \| "netlify" \| "wordpress" \| "cname" \| "none" | no |  |
| `consent` | "none" \| "custom" \| "cookiebot" \| "usercentrics" \| "complianz" \| "borlabs" \| "google_consent_mode" | no |  |
| `typescript` | "true" \| "false" | no |  |

### setup.verify

`POST /v1/projects/{project}/setup/verify` – Verify an installation: fetch the page (SSRF-guarded), check snippet/proxy/CSP, look for recent events.

- Auth: secret key or user session · `project:admin`
- Response: 200 JSON (`VerifyInstallationResult`)
- Body: application/json (`VerifyInstallationInput`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### setup.status

`GET /v1/projects/{project}/setup/status` – Onboarding status per step, derived from real data (tracking, goals, ad platforms, conversions, Claude/MCP, team).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SetupStatus`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### setup.progress

`PATCH /v1/projects/{project}/setup` – Wizard position, dismissal and manual step overrides (skip, re-open, team done).

Each key in `steps` needs one of the step's scopes (goals: config:write, ads: ads:write, conversions: project:admin, ai: analytics:read, team: org:admin); `dismissed` needs project:admin; `lastStep` alone needs analytics:read. Returns the fresh status.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SetupStatus`)
- Body: application/json (`SetupProgressUpdate`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### setup.installation

`GET /v1/projects/{project}/setup/installation` – Live installation check: hosts seen, SDK version, proxy, consent, custom events, last verification (test traffic included).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SetupInstallation`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `since` | string | no |  |

### setup.prompts

`GET /v1/projects/{project}/setup/prompts` – Ready-made Claude Code prompts for this project (titles and descriptions).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SetupPromptList`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `locale` | "de" \| "en" | no |  |

### setup.prompt

`GET /v1/projects/{project}/setup/prompts/{kind}` – One project-specific Claude Code prompt (never contains secrets).

Kinds install, events and server_conversions need config:write or project:admin; mcp_analysis, troubleshoot and connection_test need analytics:read.

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SetupPrompt`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |
| `kind` | "install" \| "events" \| "server_conversions" \| "mcp_analysis" \| "troubleshoot" \| "connection_test" | yes |  |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `stack` | "next-app" \| "next-pages" \| "react-vite" \| "astro" \| "wordpress" \| "webflow-html" \| "shopify" \| "other" \| "auto" | no |  |
| `site_url` | string | no |  |
| `proxy` | "auto" \| "on" \| "off" | no |  |
| `locale` | "de" \| "en" | no |  |

### setup.testConversion

`POST /v1/projects/{project}/setup/test-conversion` – Send one synthetic test conversion to a configured provider in its test mode and return the provider's answer.

- Auth: secret key or user session · `project:admin`
- Response: 200 JSON (`SetupTestConversionResult`)
- Body: application/json (`SetupTestConversion`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

### setup.mcpActivity

`GET /v1/projects/{project}/setup/mcp-activity` – Newest MCP calls for this project (Claude connection test).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SetupMcpActivity`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `limit` | string | no |  |

### setup.detectStack

`POST /v1/projects/{project}/setup/detect-stack` – Guess the website's stack from its HTML (SSRF-guarded fetch of an allowed origin).

- Auth: secret key or user session · `analytics:read`
- Response: 200 JSON (`SetupDetectStackResult`)
- Body: application/json (`SetupDetectStack`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `project` | string | yes | Project id, `project-slug` or `org-slug/project-slug` (URL-encode the slash as %2F) |

## Docs

| Operation | Endpoint | Auth |
|---|---|---|
| [`docs.openapi`](#docsopenapi) | `GET /v1/openapi.json` | public |
| [`docs.list`](#docslist) | `GET /v1/docs` | public |
| [`docs.search`](#docssearch) | `GET /v1/docs/search` | public |
| [`docs.get`](#docsget) | `GET /v1/docs/{slug}` | public |
| [`docs.getMarkdown`](#docsgetmarkdown) | `GET /v1/docs/{slug}.md` | public |
| [`docs.reference`](#docsreference) | `GET /docs/api` | public |
| [`docs.referenceMarkdown`](#docsreferencemarkdown) | `GET /docs/api.md` | public |
| [`docs.llms`](#docsllms) | `GET /llms.txt` | public |
| [`docs.llmsFull`](#docsllmsfull) | `GET /llms-full.txt` | public |

### docs.openapi

`GET /v1/openapi.json` – OpenAPI 3.1 document.

- Auth: public
- Response: 200 JSON

### docs.list

`GET /v1/docs` – Documentation index.

- Auth: public
- Response: 200 JSON

### docs.search

`GET /v1/docs/search` – Full-text search over guides and the API reference.

- Auth: public
- Response: 200 JSON

Query parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `q` | string | yes |  |
| `limit` | string | no |  |

### docs.get

`GET /v1/docs/{slug}` – One guide as markdown (slug with slashes URL-encoded or as path segments).

- Auth: public
- Response: 200 JSON (`Doc`)

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `slug` | string | yes |  |

### docs.getMarkdown

`GET /v1/docs/{slug}.md` – One guide as raw markdown (links point to other .md guides).

- Auth: public
- Response: 200 markdown

Path parameters:

| Name | Type | Required | Description |
|---|---|---|---|
| `slug` | string | yes |  |

### docs.reference

`GET /docs/api` – Interactive API reference (Scalar) rendered from the OpenAPI document.

- Auth: public
- Response: 200 html

### docs.referenceMarkdown

`GET /docs/api.md` – API reference as markdown (every endpoint with auth, scopes, parameters).

- Auth: public
- Response: 200 markdown

### docs.llms

`GET /llms.txt` – llms.txt index of the documentation (https://llmstxt.org).

- Auth: public
- Response: 200 text

### docs.llmsFull

`GET /llms-full.txt` – Every guide concatenated as markdown, for LLM context.

- Auth: public
- Response: 200 text
