---
title: Reports and dashboards
description: "A definition groups records and measures them; previewing runs it unsaved; a dashboard arranges saved reports as widgets on a twelve-column grid."
---

Every endpoint here needs the `reports` module. A definition is four things, a dashboard is a layout over saved ones.

## A definition is four things

| Part       | What it is                             |
| ---------- | -------------------------------------- |
| `objectId` | Which object's records are counted     |
| `groupBy`  | The field key the rows are grouped by  |
| `measure`  | What is computed per group — see below |
| `filters`  | Up to ten conditions narrowing the set |
| `limit`    | Rows returned, 1 to 100, default 50    |

A `measure` is one of two shapes:

- `{ "operation": "count" }` — no field, because counting rows needs none.
- `{ "operation": "sum" | "average" | "min" | "max", "field": "…" }` — a field key, and **the field has to be numeric**. Anything else is refused with `measure_not_numeric`.

**A report filter is equality, not a view filter.** Each one is `{ field, value }` where the value is a string or a boolean. The thirty-three operators that [a record view](/guides/concepts/views-and-pagination) offers are not available here; a value that does not fit the field is refused with `filter_value_invalid`.

**Two field kinds cannot appear at all.** A `multi_select` or an `address` in a group, a measure or a filter answers `report_field_unavailable`, for the same reason they cannot be sorted on: there is no single value to work with.

## Previewing and saving

[`POST /reports/preview`](/reference/reports/post-api-v1-reports-preview) runs a definition and returns rows without storing anything. It is how a user interface shows a result while someone is still assembling the definition.

[`POST /reports`](/reference/reports/post-api-v1-reports) saves one. [`GET /reports/{id}/results`](/reference/reports/get-api-v1-reports-by-id-results) runs the saved definition **as it stands now** — a saved report is a stored question, not a stored answer, so the numbers move as the records do.

Saving needs `reports.manage`; previewing and running need only `reports.read`. A client that shows reports without letting anyone author them needs the read scope alone.

## Dashboards

A dashboard is a name and up to **twenty widgets**. Each widget points at a saved report, picks a `visualization` from `number`, `table` or `bar`, and carries its own `layout` on a twelve-column grid: `x` from 0 to 11, `width` and `height` from 1 to 12, `y` from 0 to 100.

[`GET /dashboards/{id}/widgets`](/reference/reports/get-api-v1-dashboards-by-id-widgets) answers **only the widgets whose report the caller may read.** A dashboard is therefore not a way around report permissions — two people can open the same dashboard and see different numbers of widgets, and that is the intended behaviour rather than an error to work around.

## What is not here

Drill-down, scheduled delivery and the chart authoring experience belong to the web application, which uses a different transport. This API is the definitions and their rows.
