# Agents and the API

Everything in Dashboards can be built by an AI agent, through the MCP server
(Claude Desktop, Claude.ai connectors) or the REST API. The agent works with
exactly your account's rights: it sees the dashboards and datasets you can see
and edits what you can edit.

A metric's `source` is `dataset` (the default), `manual` or `rate`. For `manual`, set values with
`metric_record_value {"key", "date", "value", "note"}` or `POST
/smallstack/dashboards/api/metrics/<key>/entries/`. For `rate`, set `numerator` and `denominator`
to metric keys.

To track a goal list over time, set its cadences with `goal_list_tracking`, read them back with
`goal_list_history`, and capture on demand with `goal_list_capture`.

## Asking an agent

With the SmallStack connector added, ask in plain language:

> Build a "Support health" dashboard: open tickets, tickets opened this month,
> average first-response time with a warning over 4 hours, and open tickets
> by queue.

A well-behaved agent will:

1. **Look** at the datasets — `list_datasets`, then `describe_dataset` for the
   fields, the operators each takes and the period names.
2. **Dry-run** each metric with `metric_preview`. That returns the value, how
   many rows match and every problem, without saving anything. It repeats
   until the problems are gone and the number is plausible.
3. **Save** with `create_metric`, then `create_dashboard`, then
   `dashboard_add_card` per card. Personal is the default: a staff agent
   passes `"visibility": "shared"` for the team's copy, on every piece.
4. **Check** with `dashboard_snapshot`, which evaluates every card.

## The MCP tools

| Tool | Does |
|---|---|
| `list_datasets`, `describe_dataset` | what can be measured, and how it can be filtered |
| `metric_preview` | evaluate an unsaved metric (dry run) |
| `list_metrics`, `get_metric`, `create_metric`, `update_metric`, `delete_metric` | metric CRUD. The CRUD families here share these verbs |
| `metric_value` | a saved metric's value, optionally for a period, with the change from the previous period |
| `metric_trend` | a saved metric per day, week, month, quarter or year across a period, which is what a chart draws |
| `metric_rows` | the rows behind a number, to explain it |
| `metric_usage` | where a metric is used, before you change or delete it |
| `list_dashboard_components` | the card types and their options |
| `card_preview`, `*_component` | try a card without saving; saved cards you can place by key |
| `get_dashboard`, `create_dashboard`, `update_dashboard`, … | dashboard CRUD (the whole layout at once) |
| `dashboard_add_card`, `dashboard_remove_card` | one card at a time |
| `dashboard_snapshot` | every card on a dashboard with its live value. Nothing is stored, and a chart or calendar card has no one number |
| `create_goal_list`, `goal_list_add_goal` | goal lists and their targets. To change or remove a goal, read the list with `get_goal_list`, edit its `sections` and send them all to `update_goal_list` |
| `goal_list_values`, `goal_list_weeks` | a list judged now; a list week over week |
| `goal_list_save_week` | save one named week as it stands (a list tracking weeks is also saved each Monday) |
| `goal_list_tracking`, `goal_list_capture`, `goal_list_history` | the cadences a list is captured on; capture the current period or fill past ones; read captures back |
| `metric_record_value`, `metric_delete_value` | a hand-entered metric's value for a date |
| `create_report`, `report_values`, … | KPI reports; `report_values` gives the CSV's link too |
| `create_playlist`, `playlist_slides` | slides for a meeting: dashboards and goal lists |
| `list_calendars`, `calendar_events`, `create_event`, `create_calendar_view` | the calendar. Give an event a `start_time` and it's timed, and a `repeat` and it's a series |
| `event_skip_date` | take one date out of a series, or put it back |
| `query_dataset_<key>` | raw rows from a dataset, when the site publishes that tool. Bundled datasets don't by default (`SMALLSTACK_FRAMEWORK_DATASETS_MCP`); `list_datasets` says which do |

Rows can be named as well as numbered: `get_metric`, `update_metric` and `delete_metric`
take `key` in place of `id`; dashboards, reports, goal lists and playlists take `slug`.
A `slug` is optional when creating one: leave it out and it's made from the name (`site-health`,
then `site-health-2`), and the answer says what it is. An existing one's slug doesn't change
with its name. Only an administrator (a superuser, or a member of the `admin` group) may change it;
anyone else's change, over REST and MCP too, is refused. A changed slug breaks the old address: there
is no redirect, so bookmarks, embeds and playlists that used it stop working.

**Periods** are `today`, `yesterday`, `wtd` (this week so far), `pwtd` (last week), `mtd`, `pmtd`,
`qtd`, `pqtr`, `ytd`, `prev_year`, `this_month` and `this_year` (the whole month or year, days to come
included), `last_<N>`, `next_<N>` and `all`. A name that isn't one is refused,
never answered with an all-time number (a stored `default_period` takes only the fixed names, not
`all` or an arbitrary `last_14`). **A metric with no `period_field`** can't be narrowed, so it gives
its all-time value and a `note` saying so, and a goal on one can't have its own period.

**Counts.** A list tool's `count` is the page and `total` is how many match. Tool links are relative
to the site. `dashboard_snapshot` gives metric cards' values, grouped cards' groups and notes' text;
for a calendar card, ask `calendar_events`.

## REST

With a token (`Authorization: Bearer …`), the CRUD routes are
`/smallstack/api/dashboards/metrics/`, `…/boards/`, `…/reports/`, `…/goals/`,
`…/components/` and `…/playlists/`. These routes evaluate:

```
GET  /smallstack/dashboards/api/datasets/
GET  /smallstack/dashboards/api/datasets/<key>/schema/
POST /smallstack/dashboards/api/preview/            {"dataset": …, "filter_tree": {…}, "agg": …}
POST /smallstack/dashboards/api/report-preview/     {"items": [{"metric": …}], "periods": ["mtd", "pmtd"]}
POST /smallstack/dashboards/api/card-preview/       {"type": …, "metric": …, "options": {…}}
GET  /smallstack/dashboards/api/metrics/<key>/value/?period=mtd
GET  /smallstack/dashboards/api/metrics/<key>/trend/?period=&grain=
GET  /smallstack/dashboards/api/metrics/<key>/rows/?period=&order=&limit=
GET  /smallstack/dashboards/api/boards/<slug>/values/
GET  /smallstack/dashboards/api/reports/<slug>/values/    (?format=csv for the file)
GET  /smallstack/dashboards/api/goals/<slug>/values/?period=
GET  /smallstack/dashboards/api/goals/<slug>/weeks/?week=&weeks=
POST /smallstack/dashboards/api/goals/<slug>/weeks/<week>/save/
GET  /smallstack/dashboards/api/goals/<slug>/tracking/
POST /smallstack/dashboards/api/goals/<slug>/tracking/    {"cadences": ["weekly", "monthly"]}
GET  /smallstack/dashboards/api/goals/<slug>/snapshots/?cadence=weekly
POST /smallstack/dashboards/api/goals/<slug>/snapshots/   {"cadence": "weekly", "fill": 4}
GET  /smallstack/dashboards/api/metrics/<key>/entries/
POST /smallstack/dashboards/api/metrics/<key>/entries/    {"date": …, "value": …, "note": …}
GET  /smallstack/dashboards/api/playlists/<slug>/slides/
GET  /smallstack/dashboards/api/components/
```

All of them are in the OpenAPI schema (`/api/schema/openapi.json`, browsable at `/api/docs/`).

## Safety

- A non-staff account's agent can't share; only a staff account's can mark something shared.
- A metric can only use datasets its user may read. A card on a shared
  dashboard shows *restricted* to anyone whose account can't read its data.
- A bad filter is refused with a precise reason. It is never applied as
  "no filter". So is a misspelt argument, period or week.
- `delete_metric` also removes the metric's cards, report rows, goals and saved cards in
  everything the account can edit, and lists them under `also_removed`, with any rates it
  leaves showing an error under `also_removed.rates_left_without_it`. It isn't a dry run:
  `metric_usage` says where a metric is used, first.
