Next
Custom provider
Bring your own analytics backend.
On this page
source: 'custom' plugs any data source into the full dashboard experience — an internal data warehouse, an analytics product Systhema doesn't ship an adapter for, or a proxy in front of one that it does. You implement the same four data methods the built-in adapters do; the module supplies everything around them (range resolution, payload.kv caching with stale-serving, capability gating, widget seeding, the settings endpoint, access control).
Implementing an adapterLink to this section
import type { CustomAnalyticsAdapter } from '@systhemaui/payload'
const warehouse: CustomAnalyticsAdapter = {
label: 'Internal warehouse', // shown where a built-in provider's name would be
dashboardUrl: 'https://warehouse.example.com/analytics', // optional — powers the widgets' external-link icon
capabilities: {
breakdowns: ['page', 'source', 'country', 'device', 'browser', 'os'],
hourly: false, // drops the Today/Yesterday presets, exactly like Matomo
},
liveWindowMinutes: 5, // the "last N min" caption on the live widget; default 30
async timeseries({ range, compare }) {
// range is fully resolved: { from, to, bucket, previous, tz } with
// inclusive ISO dates — return zero-filled chronological points
// (plus previousPoints when compare is true).
return { bucket: range.bucket, points: [] }
},
async aggregate({ range }) {
// All five metrics; use null for a metric you cannot supply.
return {
current: { pageviews: 0, visitors: 0, sessions: null, bounceRate: null, avgDuration: null },
previous: null,
}
},
async breakdown({ range, property, limit }) {
return { property, metric: 'visitors', rows: [] }
},
async live() {
return { visitors: 0 }
},
}
export default withSysthema(buildConfig({ /* … */ }), {
analytics: { provider: { source: 'custom', adapter: warehouse } },
})NotesLink to this section
- Capabilities are per-instance. Built-in providers use the static capability matrix below; a custom adapter declares its own
capabilities, and every consumer of the matrix (endpoint validation, the settings vocabularies, the breakdown drawer options, default widget seeding — including the otherwise GA4-only age/gender widgets) honors the declared set. Omitted fields are permissive: all eleven breakdown properties, hourly supported. dashboardUrlis optional. Declare it to power the "Provided by <label>" attribution's external-link icon on every widget (opens your backend's own dashboard in a new tab); omit it and the icon simply doesn't render.- Methods may be sync or async — return the value or a promise.
- Throw
AnalyticsProviderError(exported from@systhemaui/payload) when your backend fails, so the cache layer can serve stale data and the endpoints answer with an opaque 502; any other thrown error is treated the same way defensively. - Caching applies as usual under the
custom:cache-key scope; tune or disable it via thecacheoption like any other provider.