# Metrics

> Read what your components did with GET /v1/metrics, one window of loader deliveries, browser events and conversions per site, component and version, with the source and trust of every number. Windows up to 7 days of hours or 90 days of days; everything derived from browser events (impressions, interactions, custom events and the rates) is forgeable.

`GET /v1/metrics` answers what your workspace's components did in a window, bucket by bucket. This page
says what each number is and how far to trust it, so that nobody reads a delivery count, or a count
of browser events, as a verified audience.

## What is measured

| Metric              | Source             | Trust       | What it is                                                                                                                                                                |
| ------------------- | ------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `loader_deliveries` | `loader_hits`      | `forgeable` | Times the edge delivered the loader to a page of a site of yours: human traffic, on a hostname the site does not block. It is **not** a verified impression.              |
| `conversions`       | `submissions`      | `counted`   | Accepted submissions (not spam) of a collection of the component, counted from the rows Taggor stores. This is the default definition of a conversion.                    |
| `impressions`       | `component_events` | `forgeable` | Components rendered in a visitor's browser: the loader reports each mounted component. Human traffic only.                                                                |
| `interactions`      | `component_events` | `forgeable` | Events a component declared in its `events` and emitted, reported by visitors' browsers: at most 30 per component per page, sampled by `eventSampleRate`, so an estimate. |
| `custom_events`     | `component_events` | `forgeable` | Events a component declared in `data.track` and sent with `track`, reported, capped and sampled the same way.                                                             |
| `interaction_rate`  | `component_events` | `forgeable` | `interactions / impressions`, as a fraction. `null` when there are no impressions.                                                                                        |
| `conversion_rate`   | `component_events` | `forgeable` | `conversions / impressions`, as a fraction (`0.0734` is 7.34 %). `null` when there are no impressions.                                                                    |

`forgeable` means anyone who knows a site key (it is public, it goes in your HTML) can add to the count by
requesting the loader, so Taggor never cuts or bills from it alone. `counted` means rows in our database.

Everything derived from browser events is `forgeable` too, the conversion rate included: a rate is never `counted`, even
when its numerator is (`conversion_rate` divides stored submissions by forgeable impressions). Do not
present it as a verified rate. A metric with no data is `null` and never `0`. The `metrics` array of every
answer repeats this: each entry has `source`, `trust`, `available` and, when it is not available, a `reason`
(`no_event_source`, `reader_unavailable` or `not_attributable`).

## Ask for a window

```bash
curl -s -H "Authorization: Bearer $TAGGOR_API_KEY" \
  "$TAGGOR_API_URL/v1/metrics?granularity=day&group_by=version"
```

| Query                                   | Meaning                                                                                                            |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `from`, `to`                            | The window `[from, to)`, RFC 3339. Default: `to` is now, `from` is 7 days (`day`) or 24 hours (`hour`) before it.  |
| `granularity`                           | `hour` (a window of at most 7 days) or `day` (at most 90 days, which is how long the data is kept). Default `day`. |
| `group_by`                              | `site`, `component`, `version`, `country`, `device`, `hostname` or `event` (see "Breakdowns" below).               |
| `site_id`, `component_id`, `version_id` | Narrow to one of yours. A site that is not yours is a `404`.                                                       |
| `environment`                           | `production` (default) or `staging`: test sites are kept apart, as in your usage.                                  |

The answer has `totals`, a `series` with one point per bucket (zero-filled where a source answered and found
nothing) and, with `group_by`, `groups`, each with its own `totals` and the buckets that have data. Grouped
by site, component or version, `conversions` add up to the total.

## Breakdowns

| `group_by`             | What is cut                                                                                                     |
| ---------------------- | --------------------------------------------------------------------------------------------------------------- |
| `site`                 | Every metric.                                                                                                   |
| `component`, `version` | `conversions`. The loader deliveries and the browser metrics are `null` per group (the total still has them).   |
| `country`              | The browser metrics, by the two-letter country the network reports for the request. No IP address is stored.    |
| `device`               | The browser metrics, by one of four classes: `mobile`, `desktop`, `tablet` or `other`. No user agent is stored. |
| `hostname`             | The browser metrics, by the hostname of the page the component was on.                                          |
| `event`                | `interactions` and `custom_events`, by the name of the event the component reported.                            |

A metric a breakdown cannot cut is `null` per group, never `0`, and its `breakdown_available` is `false`.
A group of the last four has `totals` and an empty `series`. The group `unknown` holds the events that
carried no value (a request the network gave no country for). A `component_id` or `version_id` filter
makes the loader deliveries and the browser metrics `null`, so the last four breakdowns answer no groups
under it. If the Analytics Engine reader is down, those metrics are `null` with
`reader_unavailable` and the conversions still answer.

## Sampling and limits

At high volume the Analytics Engine stores one point for several events and records the weight in
`_sample_interval`. Every delivery count here is a **sum of weights**, never a row count, so a sampled
dataset still gives an estimate of the real total. Conversions come from a database and are not sampled.

- At most 10,000 cells (groups times buckets) per answer: a `group_by=site` that would pass it is a `400`
  that asks for a shorter window, days instead of hours or a `site_id`. `truncated: true` means a source cut
  its answer, so read the numbers as a lower bound.
- At most 200 groups are listed.
- 30 requests a minute per credential (the `metrics` rate limit class).
- Needs the `metrics:read` scope; every role holds it, a viewer included.
- The numbers are a little behind (seconds) and approximate; the same window read again within a minute
  may come from a cache.

## Privacy

Everything above is a count: no visitor identifier, no IP address and no user agent is stored or returned.
See [Forms and data](https://taggor.dev/docs/forms-and-data.md) for what a submission keeps, which belongs to you.

## Coming later

Your own conversion definitions are not available yet in these numbers; this page will say so when they
are. Until then, `conversions` is the one measure of results.

## From the terminal and from an agent

`taggor metrics` prints the same numbers as this route: one line per metric with its source and trust, and
`--json` prints the route's answer unchanged. Flags: `--from`, `--to`, `--granularity`, `--group-by`, `--site`,
`--component`, `--version`, `--environment`. An agent connected to the MCP server has the `get_metrics` tool
(scope `metrics:read`), which says the same: browser-event numbers are `forgeable`.
