> For the complete documentation index, see [llms.txt](https://docs.uxwizz.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.uxwizz.com/server-api/server-api.md).

# Server API

UXWizz v10 exposes a versioned API with read access and optional write scopes. The full base URL is the dashboard origin plus `/server/api/v1/`, for example `https://analytics.example.com/server/api/v1/`. API tokens use the same current role and domain permissions as their owner, so reducing a user's access immediately reduces their tokens' permissions.

## Create a token

Open **Settings → API access**, create an API token, select only the scopes and domains the integration needs, and set an expiry when possible. The secret is shown once. Store it like a password and revoke it when unused.

Read scopes cover domains, domain access grants, analytics, bots, visitors, tags, recordings, heatmaps, events, autocomplete data, A/B tests, goals, segments, and your screen layouts. Each has its own scope; `goals.read` does not grant segment access, and write scopes do not grant read access. Autocomplete data contains visitor tags, IP addresses, referrer domains, and country codes used for suggestions.

Write scopes are unchecked by default. Existing tokens keep their current scopes; use **Edit access** to grant writes. Tokens cannot create/delete users, delete sessions, heatmaps or recordings, change settings, run updates, or edit A/B tests.

## Read core data

These endpoints use GET and require the named scope plus current user and domain access:

| Scope                               | Endpoint                              | Query fields                            | Response                                                                                               |
| ----------------------------------- | ------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `goals.read`                        | `goals/?action=list`                  | `domain`                                | `{items, canShare}` with visible goals.                                                                |
| `goals.read`                        | `goals/?action=get`                   | `domain`, `id`                          | `{item, canShare}` for one visible goal.                                                               |
| `segments.read`                     | `segments/?action=list` or `get`      | `domain`; `id` for get                  | Same shapes as goals, for segments.                                                                    |
| `goals.read` and/or `segments.read` | `saved-filters/?action=list` or `get` | `domain`, optional `kind`; `id` for get | Only the kinds granted to the token.                                                                   |
| `tags.read`                         | `visitors/?action=tags`               | `domain`, `clientID`                    | `{tags: ["checkout"]}` for that visitor, sorted.                                                       |
| `domain-access.read`                | `domains/?action=access`              | `domain`, optional `targetUserId`       | `{domain, items: [{id, name}]}`. Administrators can read other users; other roles see only themselves. |
| `layouts.read`                      | `layouts/?action=list`                | Optional `surface`, `signature`         | `{items}` for your screen layouts.                                                                     |
| `domains.read`                      | `domains/?action=list`                | None                                    | Array of domains allowed by both owner and token.                                                      |

Goal/segment responses retain `id`, `kind`, `name`, `color`, `definition`, `ownerUserId`, `scope`, `domainId`, `domainNames`, `visibility`, `userIds`, and `updatedAt`. Lists use the same private/shared and domain/global/selected visibility as the dashboard. Missing, hidden, wrong-kind, or unavailable records return 404 on single-record reads. Missing scopes or domain access return 403. Reads return saved definitions; use analytics endpoints for counts over a date range.

`tags.read` also permits POST `autocomplete/?action=tags` with `domain` to list distinct tags on the domain. It does not grant IP or other autocomplete reads. Existing visitor/analytics scopes still return the data those endpoints normally contain.

Example: read goals without granting write or segment access:

```bash
curl --fail --get \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --data-urlencode 'action=list' \
  --data-urlencode 'domain=example.com' \
  'https://analytics.example.com/server/api/v1/goals/'
```

Legacy `saved-filters.read` and `saved-filters.write` scopes still grant both goal and segment access. New tokens use the granular choices. Editing a legacy token shows its combined grant as both granular choices; saving keeps equivalent access unless you deselect one. Existing tokens gain no scope automatically.

## Write operations

All writes use POST and require the named scope plus your current role and access rights.

| Scope                             | Actions                                         | Fields                                                                                                                                                        |
| --------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tags.write`                      | `visitors/?action=add-tag` or `remove-tag`      | `domain`, `clientID` as a decimal string, `tagContent` (1–128 characters). Analyst role or higher.                                                            |
| `goals.write` or `segments.write` | `saved-filters/?action=save`                    | `domain`, `kind` (goal/segment), `name`, `definition` (JSON string). Optional `id` updates an owned record. Scope must cover the requested and existing kind. |
| `goals.write` or `segments.write` | `saved-filters/?action=delete`                  | `domain`, `id`, optional `scope` (domain/all). Scope must cover the stored kind.                                                                              |
| `domain-access.write`             | `domains/?action=add-access` or `remove-access` | `domain` and `targetUserId`. One existing user and registered domain. Administrator permission is required for other users.                                   |
| `layouts.write`                   | `layouts/?action=save`                          | `surface`, `signature`, `layout` (responsive JSON string), optional `profileKey` and `profileName`.                                                           |

Saved goals and segments have the same availability options as the dashboard:

* `scope`: `domain` (default), `selected` with `domainNames` (JSON string array), or `global`.
* `visibility`: `private` (default), `selected` with `userIds` (JSON string array), or `all`. Sharing requires administrator permission.
* Optional `color` for display. An empty color uses Auto. Custom theme overrides use `custom:<light>:<dark>`, for example `custom:#ff6600:#ff9933`; an empty slot uses Auto for that mode. Each specified color is a six- or eight-digit hex value. `definition` uses the dashboard filters, such as `tagFilters`, `pagesFilter`, `countries` and excluded filters; dates belong to the active view.

The token and its owner must have access to every selected domain. Updating a record requires access to its existing domains and its new domains. Global changes require an all-domains token. Read existing records and their complete availability configuration with the read endpoints above.

Goal deletion defaults to removal from the requested domain, retaining other domains. Set `scope=all` to delete the whole goal. Segment deletion permanently removes the whole saved segment, as in the saved-segment library. Hiding a segment from your header changes only your header placement. Whole-record deletion requires token access to all affected domains.

Screen layouts use the dashboard's existing per-user screen, content signature and profile model. `layouts.read` and `layouts.write` cover your layouts across domains; they cannot read or write another user's layout. For the Stats screen, the signature input is `stats-v1:` followed by the sorted, comma-separated visible goal IDs. The API accepts this input or its SHA-256 hash. The default profile is `shared` / `Shared`, meaning shared across your devices. Layout JSON retains responsive breakpoint keys such as `lg` and `sm`.

Domain access changes keep recorded data and user accounts intact. They honor agency domain limits and the token's selected domains. They cannot register a new domain, create its database, or grant access beyond the token owner's access.

Example: create a goal shared with selected users on selected domains:

```bash
curl --fail --request POST \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"domain":"example.com","kind":"goal","name":"Checkout","definition":"{\"tagFilters\":[\"checkout\"]}","scope":"selected","domainNames":"[\"example.com\",\"shop.example.com\"]","visibility":"selected","userIds":"[12,18]"}' \
  'https://analytics.example.com/server/api/v1/saved-filters/?action=save'
```

The response contains `item` with the saved ID and configuration. Tag and domain-access writes return `{"ok":true}`, including repeated adds/removals. Bearer writes do not require a browser CSRF token.

## Make a request

Resources use a directory plus an `action` query parameter. No URL rewriting is required. Example:

```bash
curl --fail --get \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --data-urlencode 'action=visitors-count' \
  --data-urlencode 'domain=example.com' \
  'https://analytics.example.com/server/api/v1/stats/'
```

POST requests accept JSON objects or form data; `action` always stays in the query string. Treat database `BIGINT` identifiers as decimal strings in JavaScript to avoid precision loss.

Time-series actions such as `visitors`, `pageviews`, `page-load-time`, `session-length`, and `bounce-rate` accept `dateInterval` as `HOUR`, `DAY`, `WEEK`, or `MONTH`. It defaults to `DAY`. Date filters are inclusive local calendar dates in `YYYY-MM-DD` format; send both `startDate` and `endDate`, and send the local offset from UTC as `dateOffset` in minutes.

```bash
curl --fail --request POST \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"domain":"example.com","startDate":"2026-08-01","endDate":"2026-08-28","dateOffset":120,"dateInterval":"DAY"}' \
  'https://analytics.example.com/server/api/v1/stats/?action=visitors'
```

Time-series rows contain `date` and `value`. Visitors and pageviews are counts, page-load time is seconds, session length is minutes, and bounce rate is percent. The `number` action instead returns `sessionLength` in seconds.

## Authenticated API description

Open `/server/api/v1/` or `/server/api/v1/openapi/` with an authenticated browser session or bearer token. UXWizz returns an OpenAPI 3.1 document filtered to the caller's authentication type, current role, token scopes, and available build features. A browser session can list dashboard-only routes that bearer tokens cannot use. Fetch the document with the intended token to discover its exact integration surface. If an action is absent, that caller cannot use it.

`x-uxwizz-actions` describes each supported action and form field, including allowed values and defaults. `scopeAlternatives` lists other accepted scopes alongside `scope`; one is required, and saved-filter operations further check the actual record kind. Discovery also respects restricted-account route policies. Domain, ownership, sharing and target-user checks run on each request. A `403` response is an access denial, not an empty analytics result.

Cross-origin integrations require a bearer token and an origin listed in `UXWIZZ_API_CORS_ORIGINS`. A separately hosted UXWizz dashboard instead uses `UXWIZZ_DASHBOARD_ORIGINS`; those exact trusted origins may use the HttpOnly session cookie and must be served over HTTPS. Reverse proxies must forward the `Authorization` header to PHP.
