# Reporting

Where each module's charts and counts live, and the query shape they share.

There is no single analytics API. Reporting is **per module**: each one exposes
its own chart and count endpoints, and the console composes them into
dashboards. This page is the map.

```bash
curl -G https://api.console.buildbase.app/api/users/chart \
  -H "Authorization: $BUILDBASE_TOKEN" \
  --data-urlencode 'groupBy=day' \
  --data-urlencode 'filter={"from":"2026-07-01T00:00:00Z","to":"2026-08-01T00:00:00Z"}'
```

> **Note:**
  `/api/analytics` is not a reporting endpoint. It mounts third-party
  integrations only. Use the per-module endpoints below.


## The shared query shape

Every chart endpoint validates the same two parameters, and **both are
required**:

| Parameter | Type   | Required | Notes                                    |
| --------- | ------ | -------- | ---------------------------------------- |
| `groupBy` | string | Yes      | One of `day`, `week`, `month`, `year`    |
| `filter`  | object | Yes      | `{ from, to }`, both ISO 8601 date-times |

`from` and `to` are required inside `filter`. No other properties are accepted —
the schema sets `additionalProperties: false`, so an extra key is a `400` rather
than being ignored.

Buckets are computed in the **organization's timezone**, not UTC, so a `day`
bucket lines up with the calendar day your users see.

## Where to find each

| Module             | Endpoints                                                                          |
| ------------------ | ---------------------------------------------------------------------------------- |
| **Users**          | `/api/users/chart`, `/comparison-chart`, `/counts`, `/heatmap`, `/world-map-data`  |
| **Audience**       | `/api/audience/chart`, `/counts`, `/heatmap`, `/world-map-data`, `/:id/tab-counts` |
| **Audience lists** | `/api/audience-lists/chart`, `/:id/world-map-data`                                 |
| **Email**          | `/api/emails/chart`, `/counts`, `/stacked-chart`, `/history/stacked-chart`         |
| **Short links**    | `/api/links-analytics/chart`, `/counts`                                            |
| **Collections**    | `/api/collections/chart`                                                           |
| **Forms**          | `/api/forms/:id/submissions/chart`                                                 |
| **Workflows**      | `/api/workflows/:id/metrics`, `/api/workflows/instances/counts`                    |
| **Workspaces**     | `/api/workspaces/chart`, `/counts`, `/:id/users/world-map-data`                    |
| **Subscriptions**  | `/api/subscriptions/chart`                                                         |
| **Organizations**  | `/api/organizations/chart`                                                         |

## Chart shapes

Three response shapes, depending on which handler backs the endpoint:

| Shape          | Used for                                                    |
| -------------- | ----------------------------------------------------------- |
| **Line**       | One series over time — signups per day                      |
| **Stacked**    | Several series over the same buckets — email sent vs opened |
| **Comparison** | The current period against the preceding one                |

`counts` endpoints are not charts. They return totals for the current state and
take no `groupBy`.

`heatmap` and `world-map-data` are geographic, derived from the IP recorded on
the underlying event.

## Freshness

Some figures are served from rollups computed by a background job that runs
**every 10 minutes**, rather than aggregated per request.

> **Note:**
  Rolled-up numbers are eventually consistent. A signup from two minutes ago may
  not appear in a chart yet, and a chart can disagree with a live list endpoint
  for up to ten minutes.

For "is this exactly right now", read the underlying records. Charts are for
trends.



## Building your own

The chart endpoints are shaped for the console's widgets. For anything else,
read the records and aggregate yourself — every list endpoint supports `filter`,
`sort`, and `pagination=false`, which is usually enough:

```bash
curl -G https://api.console.buildbase.app/api/audience \
  -H "Authorization: $BUILDBASE_TOKEN" \
  --data-urlencode 'filter={"unsubscribed":false}' \
  --data-urlencode 'pagination=false'
```

Watch the response size — `pagination=false` returns everything matching. See
the [admin API](/reference/admin-api) for the full list-query contract.

## Next Steps

- [Admin API](/reference/admin-api) — tokens, pagination, and error shapes.
- [Workflows monitoring](/workflows/monitoring) — instance counts and health.
