---
title: 'Analytics Building Guide'
description: 'The recommended way to build an Analytics page through the API, start to finish, and the details that trip people and agents up.'
---

This guide is the short path to a working Analytics page. [Analytics](/docs/api/rest/introduction/analytics) explains the parts, and every endpoint name below links to its reference page.

## The recommended path

1. **Check where you are acting.** [Who Am I?](/docs/api/rest/actions/get-me) answers the organization your requests act on and the permissions your key has there.
2. **Find the data.** [List Analytics Data Sources](/docs/api/rest/actions/list-analytics-data-sources) first: reuse a source that already fetches what you need. Otherwise find the app with [List Universal Apps](/docs/api/rest/actions/list-universal-apps), its read action with [List Universal Actions](/docs/api/rest/actions/list-universal-actions), and a connection with [List Connections](/docs/api/rest/actions/list-connections).
3. **Create the source** with [Create Analytics Data Source](/docs/api/rest/actions/create-analytics-data-source). Its first fetch starts at once; [Get Analytics Data Source Result](/docs/api/rest/actions/get-analytics-data-source-result) answers its status and rows.
4. **Look at the rows** with [Query Analytics Data Source](/docs/api/rest/actions/query-analytics-data-source): run the SQL a panel will run before you build the panel.
5. **Create the page** with [Create Analytics Page](/docs/api/rest/actions/create-analytics-page).
6. **Build it in one call** with [Set Analytics Page Definition](/docs/api/rest/actions/set-analytics-page-definition): its variables, tabs and every panel, with their SQL, display and clicks. Give each new panel a `ref` and name it by that ref anywhere in the same body. It is all or nothing, and a refusal lists every problem with its JSON Pointer into your body. Check a body first, without saving it, with [Validate Analytics Page Definition](/docs/api/rest/actions/validate-analytics-page-definition).
7. **Check each panel** with [Get Analytics Panel Output](/docs/api/rest/actions/get-analytics-panel-output): its status, rows and any failure.
8. **Publish** with [Publish Analytics Page](/docs/api/rest/actions/publish-analytics-page), then share it with [Create Analytics Page Share](/docs/api/rest/actions/create-analytics-page-share) or send it on a schedule with [Create Analytics Publication](/docs/api/rest/actions/create-analytics-publication).

## What to know

### Revisions

Every change to a page's draft needs the revision you last read in the `If-Match` header, sent as `"r_..."`, as workflow drafts do: page settings, variables, tabs, layout, panels, undo and redo, publish, discard and restore. [Get Analytics Page](/docs/api/rest/actions/get-analytics-page) with `version=draft` and [Get Analytics Page Definition](/docs/api/rest/actions/get-analytics-page-definition) answer it as `revision`, and in the `ETag` header. A change answers the new one in `meta.revision`, so you can send it straight back with the next change. A `412` means the draft changed since you read it, in the app, through Cirra or through the API: read it again and retry; `error.details.revision` is the current one.

A definition you read can be sent back as it is, and it changes nothing; within a panel, only what differs from the draft is written, and `null` on a field that holds a value clears it. A panel you leave out is deleted.

### Panel SQL

A panel's `dataSources` entry runs DuckDB SQL over the source's rows, with its `alias` as the table name: `SELECT region, sum(total) AS total FROM orders GROUP BY 1`. Filter, group and sort in SQL; use `transformCode` only for what SQL cannot do. Then map the columns in the display type's settings: [Analytics Display Types](/docs/api/rest/introduction/analytics-display-types) lists every type and what it takes. A bar chart, for example, is `{ "displayType": "bar", "settings": { "chart": { "xKey": "month", "series": [{ "key": "orders" }] } } }`. A page variable is `$name` in the SQL, and a date variable filters each source's date column unless the entry sets `followPageDate: false`.

### Drill-downs and subpages

A click opens a drill-down panel on the same page: give the target `placement: "drill"` and wire the click with `on`, such as `{ "rowClick": { "action": "drillDown", "panelId": "<id or ref>" } }`. Every `$name` the target's SQL reads takes the clicked row's field of the same name; rename a field in the clicking panel's SQL (`SELECT owner_id AS userId`) when the names differ. To open a whole page instead, use [Create Analytics Subpage](/docs/api/rest/actions/create-analytics-subpage), which makes the page and wires the click together.

### Incremental refreshes

A sync source made through the API refreshes in full on its schedule. Fetching only changed records stays off unless you turn it on with [Update Analytics Data Source](/docs/api/rest/actions/update-analytics-data-source): send `incrementalDetails` (the fields that identify a record and the arguments that ask the app for changes since the last check) with `isIncrementalEnabled: true`.

### Real side effects

[Send Analytics Publication](/docs/api/rest/actions/send-analytics-publication) sends for real. Sharing with a guest, and publication recipients outside your organization, email people outside it. Fetching a source calls the app with its connection.

### Published embeds

A change that deletes a panel or tab that a published embed shows needs confirming, as in the app. The first request answers `409` with the affected embeds and a `confirmationReceipt` in `error.details`; send the same request again with that receipt to confirm.
