# Monitors

## Monitor Model

### Fields

- **`object`** `"monitor"`

- **`id`** `string`

   Unique identifier, prefixed with `mon_`.

- **`slug`** `string`

   URL-safe identifier for the monitor.

- **`team_id`** `string`

- **`project_id`** `string`

- **`metric_id`** `string`

   The metric this monitor watches. Fixed for the life of the monitor.

- **`created_at`** [`ISODateString`](/api/monitors#iso-date-string)

- **`updated_at`** [`ISODateString`](/api/monitors#iso-date-string)

- **`name`** `string`

- **`description`** `string | null`

   What this monitor watches for.

- **`status`** [`MonitorStatus`](/api/monitors#monitor-status)

   Whether the monitor evaluates on its schedule or is paused.

- **`chart_value`** [`MetricChartValue`](/api/monitors#metric-chart-value)

   Which value of the metric is compared against the threshold.

- **`scope`** [`MonitorScope`](/api/monitors#monitor-scope)

   Whether the monitor fires once for the metric, or tracks each series separately.

- **`time_window_minutes`** `number | null`

   How far back each evaluation looks when it reads the metric.

- **`condition_type`** [`MonitorConditionType`](/api/monitors#monitor-condition-type)

   Threshold compares a value; sustained adds a duration; existence means value > 0; absence means
   no observations in a complete window.

- **`operator`** [`MonitorOperator`](/api/monitors#monitor-operator)

   How the metric value is compared against the threshold.

- **`threshold_value`** `number | null`

   The value the metric is compared against.

- **`sustained_minutes`** `number | null`

   How long the condition must hold before the monitor fires.

- **`schedule_cron`** `string`

   Cron expression, in UTC, setting how often the monitor evaluates.

- **`alert_mode`** [`MonitorAlertMode`](/api/monitors#monitor-alert-mode)

   Whether an alert stays open until the condition clears, or opens and ends at once.

- **`expires_at`** [`ISODateString | null`](/api/monitors#iso-date-string)

   When the monitor stops evaluating; null when it does not expire.

- **`series_filters`** `{ include?: Record<string, string>[] | undefined; exclude?: Record<string, string>[] | undefined; } | null`

   Narrows which of the metric's series this monitor watches.

- **`min_occurrences`** `number | null`

   How many times the condition must be met before the monitor fires.

- **`occurrence_window_minutes`** `number | null`

   The window those occurrences have to fall within.

- **`last_evaluated_at`** [`ISODateString | null`](/api/monitors#iso-date-string)

   When the monitor last evaluated.

- **`next_evaluation_at`** [`ISODateString | null`](/api/monitors#iso-date-string)

   When the monitor evaluates next.

- **`recent_alerts`** [`Alert[]`](/api/alerts#model)

   The most recent alerts this monitor raised.

- **`series_states`** [`MonitorSeriesState[]`](/api/monitors#series-state-model)

   Current state of each series the monitor watches.

### Referenced Types

#### ISODateString

`ISODateString`

An ISO 8601 date-time string returned at the JSON API boundary.

#### MonitorStatus

`"active" | "paused" | "disabled"`

#### MetricChartValue

`"count" | "average" | "sum" | "min" | "max" | "last" | "cumulative_sum" | "cumulative_count" | "p50" | "p95" | "p99" | "count_unique"`

Aggregation applied to a metric's values. Use `last` when records are snapshots of a persistent
thing such as a deal, user, or inventory item: each time bucket contains the latest reading for
that series in the bucket. Within the requested range, the last observed value carries forward
across complete empty intervals instead of reading them as zero.

#### MonitorScope

`"total" | "any_series" | "each_series"`

#### MonitorConditionType

`"threshold" | "sustained" | "existence" | "absence"`

Threshold compares values; sustained adds duration; existence fires for value > 0; absence
detects a complete window with no observations, including chart gaps.

#### MonitorOperator

`"gt" | "gte" | "lt" | "lte" | "eq" | "neq"`

#### MonitorAlertMode

`"spanning" | "instant"`

## Monitor Preview Model

### Fields

- **`object`** `"monitor_preview"`

- **`start_at`** [`ISODateString`](/api/monitors#iso-date-string)

- **`end_at`** [`ISODateString`](/api/monitors#iso-date-string)

- **`complete_through_at`** [`ISODateString | null`](/api/monitors#iso-date-string)

- **`ready`** `boolean`

   False means the window is not complete yet, so no condition verdict is available.

- **`verdicts`** [`MonitorVerdict[]`](/api/monitors#verdict-model)

### Referenced Types

#### ISODateString

`ISODateString`

An ISO 8601 date-time string returned at the JSON API boundary.

## Monitor Verdict Model

### Fields

- **`series_hash`** `string`

- **`series_label`** `string | null`

- **`value`** `number | null`

- **`is_met`** `boolean | null`

### Referenced Types

#### ISODateString

`ISODateString`

An ISO 8601 date-time string returned at the JSON API boundary.

## Monitor Series State Model

### Fields

- **`monitor_id`** `string`

- **`series_hash`** `string`

   Identifies which series of the metric this state tracks.

- **`is_firing`** `boolean`

   Whether this series currently meets the monitor's condition.

- **`last_triggered_at`** [`ISODateString | null`](/api/monitors#iso-date-string)

   When this series last met the condition.

- **`last_value`** `number | null`

   The value this series had at the last evaluation.

- **`started_firing_at`** [`ISODateString | null`](/api/monitors#iso-date-string)

   When the current firing period began; null when the series is not firing.

- **`occurrence_timestamps`** `string[] | null`

   When the condition was met, for a monitor that counts occurrences.

### Referenced Types

#### ISODateString

`ISODateString`

An ISO 8601 date-time string returned at the JSON API boundary.

## List Monitors

### Endpoint

List the monitors watching a project's metrics.

```http
GET /v1/projects/:project_id/monitors
GET /v1/projects/:project_id/metrics/:metric_id/monitors
```

**Scope:** `monitors:read`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string`
  Unique identifier of the metric. Only used by `/v1/projects/:project_id/metrics/:metric_id/monitors`.

### Query Parameters

- **`order_by`** `string`
  Field the results are sorted by. Optional. Defaults to `"name"`. Allowed values: `"name"`, `"created_at"`.

- **`metric_id`** `string`
  Return only monitors watching this metric. Optional.

- **`status`** `string`
  Return only monitors in this state. Optional. Allowed values: `"active"`, `"paused"`, `"disabled"`.

- **`limit`** `number`
  Optional. Defaults to `25`. Minimum: `1`. Maximum: `200`.

- **`after`** `string`
  Cursor from `pagination.next_cursor` of a previous response. Returns the resources after that page. Optional.

- **`before`** `string`
  Cursor from `pagination.prev_cursor` of a previous response. Returns the resources before that page. Optional.

- **`sort`** `string`
  Optional. Defaults to `"asc"`. Allowed values: `"asc"`, `"desc"`.

### Response

```ts
{
  message: string;
  data: Monitor[];
  status: 200;
  error: null;
  pagination: Pagination;
  endpoint: string;
}
```

### Comments

- Call the nested path to scope the results to one metric, or the flat path with an optional `metric_id` filter.
- `after` and `before` are mutually exclusive.

## Retrieve Monitor

### Endpoint

Retrieve a single monitor.

```http
GET /v1/projects/:project_id/metrics/:metric_id/monitors/:monitor_id
```

**Scope:** `monitors:read`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string` -- **Required**
  Unique identifier of the metric.

- **`monitor_id`** `string` -- **Required**
  Unique identifier of the monitor.

### Response

Monitor retrieved

```ts
{
  message: string;
  data: Monitor;
  status: 200;
  error: null;
  pagination: null;
  endpoint: string;
}
```

### Comments

- Includes the monitor's recent alerts and the current state of every series it watches.

## Create Monitor

### Endpoint

Create a monitor that raises alerts when a metric meets a condition.

```http
POST /v1/projects/:project_id/metrics/:metric_id/monitors
```

**Scope:** `monitors:write`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string` -- **Required**
  Unique identifier of the metric.

### Request Body

- **`name`** `string`
  Name shown for this monitor. Optional. Minimum length: `2`. Maximum length: `64`.

- **`description`** `string | null`
  What this monitor watches for. Optional.

- **`status`** `string`
  Whether the monitor starts evaluating immediately or switched off. `disabled` is not accepted: the system uses it to record that it stopped the monitor itself. Optional. Defaults to `"active"`. Allowed values: `"active"`, `"paused"`.

- **`chart_value`** [`MetricChartValue`](/api/monitors#metric-chart-value)
  Which value of the metric is compared against the threshold. Optional. Defaults to `"sum"`.

- **`scope`** [`MonitorScope`](/api/monitors#monitor-scope)
  Whether the monitor fires once for the metric as a whole, or tracks each series separately. Optional. Defaults to `"total"`.

- **`time_window_minutes`** `integer | null`
  Evaluation lookback in whole minutes (1 to 525600). Null means since the metric was created. No-data conditions require a finite window. Optional.

- **`condition_type`** [`MonitorConditionType`](/api/monitors#monitor-condition-type)
  threshold compares a value, sustained requires that comparison to hold, existence fires when the selected value is greater than zero, and absence fires when no observations exist in the complete evaluation window, regardless of chart gap handling. Optional. Defaults to `"threshold"`.

- **`operator`** [`MonitorOperator`](/api/monitors#monitor-operator)
  How the metric value is compared against the threshold. Optional. Defaults to `"gt"`.

- **`threshold_value`** `number | null`
  The value the metric is compared against. Optional.

- **`sustained_minutes`** `number | null`
  How long the condition must hold before the monitor fires. Optional.

- **`schedule_cron`** `string`
  Cron expression, in UTC, setting how often the monitor evaluates. Optional. Defaults to `"* * * * *"`. Minimum length: `1`. Maximum length: `128`.

- **`alert_mode`** [`MonitorAlertMode`](/api/monitors#monitor-alert-mode)
  Whether an alert stays open until the condition clears, or opens and ends in one moment. Optional. Defaults to `"spanning"`.

- **`expires_at`** [`ISODateString | null`](/api/monitors#iso-date-string)
  When the monitor stops evaluating. Leave null for no expiry. Optional.

- **`series_filters`** `object | null`
  Narrows which of the metric's series this monitor watches. Optional.

- **`min_occurrences`** `integer | null`
  How many times the condition must be met before the monitor fires. Optional.

- **`occurrence_window_minutes`** `integer | null`
  The window those occurrences have to fall within. Optional.

- **`metric_id`** `string`
  Metric to watch. Optional when using the /metrics/:metric/monitors route. Optional.

### Response

Your monitor has been created.

```ts
{
  message: string;
  data: Monitor;
  status: 201;
  error: null;
  pagination: null;
  endpoint: string;
}
```

### Comments

- The monitor starts evaluating on its schedule as soon as it is created.
- `scope` decides how many alerts a monitor can raise at once: `any_series` fires once for the metric as a whole, `each_series` tracks every series separately.
- No-data (`absence`) conditions require `chart_value=count` and a finite `time_window_minutes`.
- `sustained_minutes` is required when `condition_type` is `sustained`, and `threshold_value` is required when it is `threshold`.
- `min_occurrences` and `occurrence_window_minutes` must both be set or both be null, and occurrence-based triggering cannot be combined with a sustained condition.
- `schedule_cron` must be a valid cron expression. It is evaluated in UTC, and the finest granularity is one minute.
- A monitor cannot be created active with an `expires_at` already in the past.

## Preview Monitor

### Endpoint

Preview a monitor condition over its current evaluation window without saving or raising alerts.

```http
POST /v1/projects/:project_id/metrics/:metric_id/monitors/preview
```

**Scope:** `monitors:read`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string` -- **Required**
  Unique identifier of the metric.

### Request Body

- **`name`** `string`
  Name shown for this monitor. Optional. Minimum length: `2`. Maximum length: `64`.

- **`description`** `string | null`
  What this monitor watches for. Optional.

- **`status`** `string`
  Whether the monitor starts evaluating immediately or switched off. `disabled` is not accepted: the system uses it to record that it stopped the monitor itself. Optional. Defaults to `"active"`. Allowed values: `"active"`, `"paused"`.

- **`chart_value`** [`MetricChartValue`](/api/monitors#metric-chart-value)
  Which value of the metric is compared against the threshold. Optional. Defaults to `"sum"`.

- **`scope`** [`MonitorScope`](/api/monitors#monitor-scope)
  Whether the monitor fires once for the metric as a whole, or tracks each series separately. Optional. Defaults to `"total"`.

- **`time_window_minutes`** `integer | null`
  Evaluation lookback in whole minutes (1 to 525600). Null means since the metric was created. No-data conditions require a finite window. Optional.

- **`condition_type`** [`MonitorConditionType`](/api/monitors#monitor-condition-type)
  threshold compares a value, sustained requires that comparison to hold, existence fires when the selected value is greater than zero, and absence fires when no observations exist in the complete evaluation window, regardless of chart gap handling. Optional. Defaults to `"threshold"`.

- **`operator`** [`MonitorOperator`](/api/monitors#monitor-operator)
  How the metric value is compared against the threshold. Optional. Defaults to `"gt"`.

- **`threshold_value`** `number | null`
  The value the metric is compared against. Optional.

- **`sustained_minutes`** `number | null`
  How long the condition must hold before the monitor fires. Optional.

- **`schedule_cron`** `string`
  Cron expression, in UTC, setting how often the monitor evaluates. Optional. Defaults to `"* * * * *"`. Minimum length: `1`. Maximum length: `128`.

- **`alert_mode`** [`MonitorAlertMode`](/api/monitors#monitor-alert-mode)
  Whether an alert stays open until the condition clears, or opens and ends in one moment. Optional. Defaults to `"spanning"`.

- **`expires_at`** [`ISODateString | null`](/api/monitors#iso-date-string)
  When the monitor stops evaluating. Leave null for no expiry. Optional.

- **`series_filters`** `object | null`
  Narrows which of the metric's series this monitor watches. Optional.

- **`min_occurrences`** `integer | null`
  How many times the condition must be met before the monitor fires. Optional.

- **`occurrence_window_minutes`** `integer | null`
  The window those occurrences have to fall within. Optional.

- **`metric_id`** `string`
  Metric to watch. Optional when using the /metrics/:metric/monitors route. Optional.

### Response

Monitor preview retrieved

```ts
{
  message: string;
  data: MonitorPreview;
  status: 200;
  error: null;
  pagination: null;
  endpoint: string;
}
```

### Comments

- Uses the same series filtering and condition evaluation as scheduled monitors. This is a condition preview, not a simulation of cron timing, sustained duration, or occurrence counting.
- An incomplete window returns ready=false with no verdicts. No-data conditions count observations across the whole window, including when the chart displays missing buckets as gaps.
- No-data (`absence`) conditions require `chart_value=count` and a finite `time_window_minutes`.
- `sustained_minutes` is required when `condition_type` is `sustained`, and `threshold_value` is required when it is `threshold`.
- `min_occurrences` and `occurrence_window_minutes` must both be set or both be null, and occurrence-based triggering cannot be combined with a sustained condition.
- `schedule_cron` must be a valid cron expression. It is evaluated in UTC, and the finest granularity is one minute.
- A monitor cannot be created active with an `expires_at` already in the past.

## Update Monitor

### Endpoint

Change a monitor's condition, schedule, or status.

```http
POST /v1/projects/:project_id/metrics/:metric_id/monitors/:monitor_id
```

**Scope:** `monitors:write`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string` -- **Required**
  Unique identifier of the metric.

- **`monitor_id`** `string` -- **Required**
  Unique identifier of the monitor.

### Request Body

- **`name`** `string`
  Name shown for this monitor. Optional. Minimum length: `2`. Maximum length: `64`.

- **`description`** `string | null`
  What this monitor watches for. Optional.

- **`chart_value`** [`MetricChartValue`](/api/monitors#metric-chart-value)
  Which value of the metric is compared against the threshold. Optional.

- **`scope`** [`MonitorScope`](/api/monitors#monitor-scope)
  Whether the monitor fires once for the metric as a whole, or tracks each series separately. Optional.

- **`time_window_minutes`** `integer | null`
  Evaluation lookback in whole minutes (1 to 525600). Null means since the metric was created. No-data conditions require a finite window. Optional.

- **`condition_type`** [`MonitorConditionType`](/api/monitors#monitor-condition-type)
  threshold compares a value, sustained requires that comparison to hold, existence fires when the selected value is greater than zero, and absence fires when no observations exist in the complete evaluation window, regardless of chart gap handling. Optional.

- **`operator`** [`MonitorOperator`](/api/monitors#monitor-operator)
  How the metric value is compared against the threshold. Optional.

- **`threshold_value`** `number | null`
  The value the metric is compared against. Optional.

- **`sustained_minutes`** `number | null`
  How long the condition must hold before the monitor fires. Optional.

- **`schedule_cron`** `string`
  Cron expression, in UTC, setting how often the monitor evaluates. Optional. Minimum length: `1`. Maximum length: `128`.

- **`alert_mode`** [`MonitorAlertMode`](/api/monitors#monitor-alert-mode)
  Whether an alert stays open until the condition clears, or opens and ends in one moment. Optional.

- **`expires_at`** [`ISODateString | null`](/api/monitors#iso-date-string)
  When the monitor stops evaluating. Leave null for no expiry. Optional.

- **`series_filters`** `object | null`
  Narrows which of the metric's series this monitor watches. Optional.

- **`min_occurrences`** `integer | null`
  How many times the condition must be met before the monitor fires. Optional.

- **`occurrence_window_minutes`** `integer | null`
  The window those occurrences have to fall within. Optional.

### Response

Your monitor has been updated.

```ts
{
  message: string;
  data: Monitor;
  status: 200;
  error: null;
  pagination: null;
  endpoint: string;
}
```

### Comments

- Send only the fields you are changing. Anything omitted keeps its current value.
- Changing what the monitor MEANS (its condition, scope, series filters or alert mode) ends any period currently firing, because the old alert no longer describes the new rule.
- `metric_id` cannot be changed. Create a new monitor against the other metric instead.
- The metric a monitor watches cannot be changed. Create a second monitor instead.
- No-data (`absence`) conditions require `chart_value=count` and a finite `time_window_minutes`.
- `sustained_minutes` is required when `condition_type` is `sustained`, and `threshold_value` is required when it is `threshold`.
- `min_occurrences` and `occurrence_window_minutes` must both be set or both be null, and occurrence-based triggering cannot be combined with a sustained condition.
- `schedule_cron` must be a valid cron expression. It is evaluated in UTC, and the finest granularity is one minute.
- `status` is not editable here. Use the pause and resume operations, which is also what keeps a customer from writing the `disabled` the system uses for expiry.
- Moving `expires_at` is rejected when that would leave an active monitor with an expiry already in the past. Editing a monitor that has already expired is allowed, including the update that extends its expiry.
- Conditions are checked against the monitor as it will be stored, not against the fields you send, so a change that would leave the monitor in an invalid combination is rejected even when the conflicting value is one you did not send.

## Pause Monitor

### Endpoint

Pause a monitor so it stops evaluating.

```http
POST /v1/projects/:project_id/metrics/:metric_id/monitors/:monitor_id/pause
```

**Scope:** `monitors:write`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string` -- **Required**
  Unique identifier of the metric.

- **`monitor_id`** `string` -- **Required**
  Unique identifier of the monitor.

### Response

Monitor paused

```ts
{
  message: string;
  data: Monitor;
  status: 200;
  error: null;
  pagination: null;
  endpoint: string;
}
```

### Comments

- A paused monitor raises no alerts and closes nothing that is already firing. Resuming evaluates it again on its next scheduled tick.

## Resume Monitor

### Endpoint

Resume a paused monitor so it evaluates on its schedule again.

```http
POST /v1/projects/:project_id/metrics/:metric_id/monitors/:monitor_id/resume
```

**Scope:** `monitors:write`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string` -- **Required**
  Unique identifier of the metric.

- **`monitor_id`** `string` -- **Required**
  Unique identifier of the monitor.

### Response

Monitor resumed

```ts
{
  message: string;
  data: Monitor;
  status: 200;
  error: null;
  pagination: null;
  endpoint: string;
}
```

### Comments

- A monitor the system stopped because its expiry passed can be resumed once the expiry is moved into the future. Resuming one whose expiry is still in the past is rejected.

## Delete Monitor

### Endpoint

Delete a monitor.

```http
DELETE /v1/projects/:project_id/metrics/:metric_id/monitors/:monitor_id
```

**Scope:** `monitors:delete`

### Path Parameters

- **`project_id`** `string` -- **Required**
  Unique identifier of the project.

- **`metric_id`** `string` -- **Required**
  Unique identifier of the metric.

- **`monitor_id`** `string` -- **Required**
  Unique identifier of the monitor.

### Response

Your monitor has been deleted.

```ts
{
  message: string;
  data: null;
  status: 200;
  error: null;
  pagination: null;
  endpoint: string;
}
```

### Comments

- Alerts the monitor already raised are deleted with it.

