> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usecroma.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Monitors

> Watch any Croma source on a schedule and get what is new by email: how monitors run after each source refresh, what they charge, the relevance filter, and the full API.

A **monitor** is a saved query on one Croma endpoint that Croma evaluates on
a schedule and emails to up to three addresses. You describe what to watch the
same way you would query the endpoint; Croma keeps the schedule, remembers what
it already reported, and sends only what appeared since the last run.

<Note>
  Monitors are a feature of the API. Create them with `POST /monitors`, by
  asking Claude or any MCP client ([Monitors from Claude](/monitors-mcp)) or
  from the console at
  [platform.usecroma.com/monitors](https://platform.usecroma.com/monitors),
  where you describe the monitor in plain words or fill in a form. All three
  manage the same monitors.
</Note>

## Which endpoints can be monitored?

Every data endpoint, whichever way it is served: see
[Monitorable sources](/monitors-sources) for the list, with what each one
watches, what a run reports and when it is checked. `GET /catalog` marks the
same endpoints with `monitorable: true`. Only the web search, extraction,
generation and research tools, the batch endpoints and retired versions are
left out. A new source is monitorable the day it ships.

What a run reports depends on the endpoint:

* **A search** (it pages its results) reports the results that were not there
  before.
* **A lookup of one record** (a case by radicado, a company by NIT, a vehicle
  by plate, a person's records) reports what changed in that record. See
  [Watching one record](#watching-one-record).

And when it runs depends on how it is served, which its `served_from` says:

* **`served_from: "dataset"`**: Croma refreshes these on a schedule of their
  own, so a monitor can follow that refresh (`cadence: "source"`) and each run
  spends one credit.
* **`served_from: "live"`**: these query the source when asked, so a monitor
  runs them on the clock, `daily` or `weekly`, and each run spends the credits
  of one live request.

## Create one

```bash theme={"dark"}
curl -X POST https://api.croma.run/monitors \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Contrato AD9-111",
    "endpoint": "/co/anm/notices-search/v1",
    "query": { "title_number": "AD9-111" },
    "schedule": { "cadence": "source" },
    "recipients": ["legal@example.com"],
    "notify": "always",
    "language": "es"
  }'
```

| Field | Meaning |
| - | - |
| `endpoint` | The endpoint to watch, by path, catalog id or MCP tool name (`anm_notices_search`). |
| `query` | The request body the monitor sends on every run, validated exactly as a direct request would be. `page` and `per_page` are not allowed. |
| `schedule` | When to run. See below. Default `{ "cadence": "source" }`. |
| `recipients` | One to three email addresses. |
| `notify` | `always` (default) sends an email on every run, including one that found nothing new; `on_new` only when there is something to report. |
| `language` | `es` (default) or `en`, for the emails. |
| `relevance` | Optional. A plain-language instruction that filters new results before they are sent. See below. |

The response is the monitor with its compiled schedule:

```json theme={"dark"}
{
  "data": {
    "id": "mon_5f1c2c0a-9d1e-4a3b-8c7d-1e2f3a4b5c6d",
    "name": "Contrato AD9-111",
    "endpoint": { "id": "anm-notices-search", "path": "/co/anm/notices-search/v1", "source": "ANM", "name": "Notices Search" },
    "query": { "title_number": "AD9-111" },
    "relevance": null,
    "schedule": { "cadence": "source", "timezone": "America/Bogota", "cron": "30 7 * * *", "next_run_at": "2026-09-22T12:30:00.000Z" },
    "recipients": ["legal@example.com"],
    "notify": "always",
    "language": "es",
    "status": "active",
    "created_at": "2026-09-21T15:04:05.000Z",
    "updated_at": "2026-09-21T15:04:05.000Z",
    "last_run": null
  }
}
```

## The first run

Right after creation the monitor runs once to record what already matches
today (up to 500 rows, or the record as it stands) and sends a welcome email
with that count and the next run time. From then on, every run reports only
what was not seen before.

## Watching one record

A monitor on a lookup reads the whole record on every run and compares it with
the last reading. Two things can come out of a run:

* **The record moved.** The match carries `changes`: each value that differs,
  by its path in the record, with what it was and what it is now.
* **A list in the record grew.** Each new item (a new action on a case, a new
  fine on a plate, a new filing of a company) is a match of its own, reported
  like a new search result.

```json theme={"dark"}
{
  "record_key": "record@8d0c1e5a-…",
  "record": { "found": true, "registration_number": "05001310300120240012300", "primary_case": { "court": "Juzgado 2 Civil del Circuito de Medellín" } },
  "changes": [
    { "path": "primary_case.court", "before": "Juzgado 1 Civil del Circuito de Medellín", "after": "Juzgado 2 Civil del Circuito de Medellín" }
  ]
}
```

Values that change on every read without the record changing (the time the
source was consulted, a certificate issued for that request) are left out of
the comparison, so a monitor never reports a new timestamp as news.

## Schedules

| `cadence` | Runs |
| - | - |
| `source` | Right after the endpoint's own refresh completes. For a daily source that is once a day, as soon as new data has landed. Dataset-served endpoints only. |
| `hourly` | Every hour, on the hour. |
| `daily` | Every day at `at` (HH:MM, default 08:00) in `timezone` (default the source's). |
| `weekly` | Every `weekday` (default monday) at `at`. |

A monitor can never check a source more often than the source refreshes: a
`daily` dataset refuses `hourly` with `schedule_faster_than_source`. A
`served_from: "live"` endpoint has no refresh of its own to follow, so it takes
`daily` or `weekly` and refuses `cadence: "source"` with `invalid_param`. When a
`source` run has to go ahead without the day's refresh (the source did not
publish), the run and its email say so: `source_stale` is `true` and the email
reads "the source has not published new data since…", never a plain "nothing
new".

## The relevance filter

With `relevance` set, every new row is judged against your instruction before
the email goes out. Rows the filter discards are never lost: they stay in the
monitor with `relevant: false` and the email says how many were left out. If
the filter is unavailable, the rows are sent flagged as unreviewed rather than
dropped.

```json theme={"dark"}
{ "relevance": "solo avisos sobre suspensión o caducidad del título" }
```

`GET /monitors/{id}/matches?relevant=false` lists what was discarded, with the
reason for each row.

## Manage it

| Call | What it does |
| - | - |
| `GET /monitors` | Your monitors, with each one's last run. |
| `GET /monitors/{id}` | One monitor. |
| `PATCH /monitors/{id}` | Change anything but the endpoint. `{ "status": "paused" }` pauses, `{ "status": "active" }` resumes. |
| `POST /monitors/{id}/run` | Run now, outside the schedule. |
| `GET /monitors/{id}/runs` | Every run: when, what it found, whether the email went out. |
| `GET /monitors/{id}/matches` | Every reported row, with the relevance verdict. |
| `DELETE /monitors/{id}` | Removes the monitor, its schedule, runs and matches. |

Every email carries an unsubscribe link for its recipient. A monitor left with
no recipients is paused.

## Credits and limits

Each run spends the same credits as one request to the endpoint: one for a
dataset-served search, the live rate for one that queries the source.
Creating and managing monitors is free. When the organization has
no credits left, the run is recorded as `skipped_plan_limit`, the recipients
are told once a day, and the schedule stays in place.

An organization can have up to 20 monitors, each with up to 3 recipients. A
run reports up to 500 new rows; beyond that it is flagged `truncated`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.