---
title: "Analytics reference"
description: "Configuration, endpoints and caching."
requested_language: ar
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/ar/next/payload/analytics/reference
version: unreleased (main)
docs_index: https://docs.systhema.app/ar/next/llms.txt
---
> This page isn't translated yet. Showing English.


The full `analytics` option, the endpoints the widgets call, and how responses are cached.

## Configuration reference

```ts
analytics?: false | {
  provider:                    // discriminated union on `source`
    | {
        source: 'ga4'
        propertyId: string     // the numeric GA4 property id
        clientEmail: string    // service account client_email from its JSON key file
        privateKey: string     // service account private_key; literal \n sequences are normalized
      }
    | {
        source: 'plausible'
        apiKey: string         // Stats API key (sent as a Bearer token)
        siteId: string         // the site's domain exactly as configured in Plausible
        host?: string          // self-hosted instance base URL; default: plausible.io
      }
    | {
        source: 'umami'
        websiteId: string      // the website id (UUID) from the Umami dashboard
        apiKey?: string        // Umami Cloud API key — when set, username/password are not needed
        host?: string          // self-hosted instance base URL; required for username/password auth
        username?: string      // self-hosted login (paired with password)
        password?: string
      }
    | {
        source: 'matomo'
        host: string           // Matomo instance base URL
        siteId: number | string // the numeric site id (idSite)
        tokenAuth: string      // API token — only ever sent in POST bodies, never in URLs
      }
    | {
        source: 'fathom'
        apiKey: string         // Fathom API key (sent as a Bearer token)
        siteId: string         // the Fathom site id (e.g. 'ABCDEFGH')
      }
    | {
        source: 'custom'
        adapter: CustomAnalyticsAdapter // bring your own backend — see "Custom provider" below
      }
  cache?: false | {            // false disables kv persistence (request dedup stays active)
    ttlSeconds?: number        // TTL for timeseries/aggregate/breakdown responses; default 300
    liveTtlSeconds?: number    // TTL for the live-visitors response; default 30
  }
}
```

All credentials are **server-only**: they live in the server-side config store and never reach the admin client — the settings endpoint exposes the provider _name_ (plus a custom adapter's display `label`) and never exposes a host, id, or key **on its own**. A `dashboardUrl` deep-link is the one exception: when the provider can build one, it may embed a destination identifier (e.g. a Plausible site domain, a GA4 property id) baked into the URL the widgets link out to.

## Endpoints

Registered on the Payload config only when analytics is enabled. The data endpoints require authentication + the `analytics.read` capability (401 unauthenticated → 404 disabled → 403 missing capability → 400 invalid query params → 502 provider failure); `/settings` requires only authentication — it carries no secrets and returns a `canRead` flag the widgets use:

| Endpoint                             | Method | Purpose                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/api/systhema/analytics/timeseries` | GET    | Zero-filled pageviews + visitors series — `?preset=28d` or `?from=&to=`, plus `&tz=` (the viewer's IANA timezone) and optionally `&compare=true` for a previous-period overlay. Hourly buckets for a single-day span when the provider supports it.                                                                                                                                                        |
| `/api/systhema/analytics/aggregate`  | GET    | All five metrics (pageviews, visitors, sessions, bounceRate, avgDuration) for the current period plus the previous period for deltas — same range params (no `compare`; the previous period is always included).                                                                                                                                                                                           |
| `/api/systhema/analytics/breakdown`  | GET    | Top-N rows for one property — `?property=&limit=` (limit 1–50, default 10) plus range params. `property` values: `page`, `source`, `country`, `region`, `city`, `device`, `browser`, `os`, `language`, `age`, `gender`. Requesting a property the configured provider can't serve (see the capability matrix below) returns 400.                                                                           |
| `/api/systhema/analytics/live`       | GET    | Current live-visitor count (no range params — it always reflects right now).                                                                                                                                                                                                                                                                                                                               |
| `/api/systhema/analytics/settings`   | GET    | Safe client settings — enabled/canRead, the provider name, the provider-filtered `presets` and `breakdownProperties` vocabularies, the provider's realtime window, and a `dashboardUrl` deep-link to the provider's own dashboard (`null` when not resolvable). Never exposes keys, hosts or ids on their own — only baked into `dashboardUrl` where the link itself needs them (e.g. a Plausible domain). |

Successful data responses are `{ success: true, data, stale? }` — `stale: true` rides along only when an expired cache entry was served because the provider refresh failed (see Caching). Provider error details are logged server-side only; the client sees a generic 502 message, since provider errors can carry hosts, URLs or ids.

## Caching

Provider responses are cached server-side in **`payload.kv`** (Payload's native database-backed key-value store) under `systhema-analytics:*` keys. Defaults: 300 seconds for timeseries/aggregate/breakdown, 30 seconds for the live count — tune via `cache.ttlSeconds` / `cache.liveTtlSeconds`.

- **Serve-stale-on-error**: when a cached entry has expired and the provider refresh fails, the stale data is returned (flagged `stale: true` in the response and shown as a "stale" hint in the widgets) instead of an error. A failed fetch is never persisted.
- **Request deduplication**: concurrent requests for the same data share one in-flight provider call — several editors opening the dashboard at once cost one provider request per widget, not one per user. This matters for rate-limited APIs (Umami Cloud allows 50 calls per 15 seconds; Fathom's API calls count against your pageview quota).
- `cache: false` disables kv persistence entirely (the in-flight deduplication stays active). Only do this for debugging — caching is what protects the provider's rate limits.
