The contract

Where analytics.json lives, the public JSON Schema to validate it against, how it relates to schema.json and manifest.json, and what happens when you deploy.

analytics.json sits next to your manifest.json and schema.json. One App ships one analytics.json, declaring one or more flat views over the entities in its schema.json.

App layout
my-app/
  manifest.json      identity, vendor and app name, capabilities
  schema.json        the entities your app owns
  analytics.json     which of those entities are analysable, and how

Validate as you write

The contract is published as JSON Schema:

Schema URL
https://schemas.revenexx.com/analytics.schema.json

Point your editor at it with $schema and you get completion and validation as you type — which matters here, because most of the file is string locators whose exact form is checked by a pattern:

analytics.json
{
  "$schema": "https://schemas.revenexx.com/analytics.schema.json",
  "version": "1",
  "views": []
}

Two fields are required at the top level: version, which is the contract major version and is currently "1", and views, which needs at least one view. Nothing else is allowed at the top level.

Its relationship to schema.json

schema.json is the source of truth; analytics.json is a projection of it.

schema.json saysanalytics.json says
Which entities your app owns, and their fields, attributes and relationsWhich of those entities are analysable, flattened into which columns

Every view's source names an entity from your schema.json, and every column's from locator resolves against that entity — its scalar columns, its attributes, the relations it can follow. So a view can never expose something the schema does not describe, and a rename in schema.json is a change you have to make in both files.

Two practical consequences:

  • Write analytics.json after the schema settles. Declaring analytics over an entity whose fields are still moving is churn for no benefit.
  • depends_on documents the entities a view reads through relations. It is not a join specification — the locators do that — but it records the dependency, and it is the field a reviewer reads to understand what a view touches.

Where the two files come apart: analytics.json is one row per key, not one row per locale. A localised attribute is collapsed to a single scalar at the view's default_locale, so per-locale breakouts have to be explicit extra columns. See Column locators.

Its relationship to manifest.json

The manifest supplies the identity that names the emitted dataset. Your vendor and app name plus the view name produce a fixed pattern:

Emitted dataset name
analytics.<vendor>__<app>__<view>

So a view named locations in the acme/warehouse App is registered as analytics.acme__warehouse__locations. You do not choose the name; you choose the view name, and the rest comes from the manifest.

What happens at deploy time

analytics.json is picked up as part of the normal App deploy, alongside the manifest and schema:

Deploy
revenexx deploy app

At deploy, the views in analytics.json are compiled and registered as datasets for the tenant, scoped so each one only ever returns that tenant's rows. They then appear in that tenant's Analytics Studio — in the dataset picker, in cohorts, and in the chart builder — labelled with your title and description.

See Deploy with the CLI for the full loop.

There is nothing to call and nothing to enable afterwards. Deploying is the whole of publishing analytics, and re-deploying with a changed analytics.json is how you change them.

What it is not

  • It is not a query API for you. Declaring a view does not give your own code a way to query it. It puts your data in front of the tenant's operator, in Cockpit.
  • It is not a place for SQL. You declare what is analysable and where each column comes from. The platform builds the flat view.
  • It is not where isolation or storage is configured. Both are platform decisions you cannot opt out of — see Tenant isolation.

Sizing the opportunity

Only a handful of the platform's own apps ship an analytics.json today — notably neither the orders app nor the customers app does — so views you declare genuinely add to what the tenant can analyse rather than duplicating it.

Where to go next

Was this page helpful?