---
title: WatchFor alert rules API
description: List, create, update, enable/disable and delete a monitor's alert rules — the conditions that open incidents — validated against the type's metric catalog.
canonical: https://watchfor.io/docs/api/alert-rules
---

# WatchFor alert rules API

List, create, update, enable/disable and delete a monitor's alert rules — the conditions that open incidents — validated against the type's metric catalog.

An **alert rule** is a condition on one monitor — a metric, an operator
and a threshold value — that opens an incident when it holds across the
configured number of checks and locations. Every new monitor gets the
[default rules](/docs/api/monitor-types) for its type; these endpoints
let you read, tune and extend them.

| Method | Path | Scope | operationId |
| --- | --- | --- | --- |
| GET | `/v1/monitors/{id}/alert-rules` | read | `listAlertRules` |
| POST | `/v1/monitors/{id}/alert-rules` | write | `createAlertRule` |
| GET | `/v1/monitors/{id}/alert-rules/{ruleId}` | read | `getAlertRule` |
| PATCH | `/v1/monitors/{id}/alert-rules/{ruleId}` | write | `updateAlertRule` |
| DELETE | `/v1/monitors/{id}/alert-rules/{ruleId}` | write | `deleteAlertRule` |

The alert rule object:

```json
{
  "id": "a16c3a38-…",
  "object": "alert_rule",
  "monitor_id": "0b0f6a41-…",
  "name": "Availability Check",
  "metric": "success",
  "operator": "==",
  "value": "false",
  "severity": "critical",
  "failure_threshold": 3,
  "min_failed_probes": 1,
  "duration_window_seconds": 0,
  "enabled": true,
  "is_system": true,
  "contact_group": null,
  "created_at": "2026-08-01T09:12:00.000Z",
  "updated_at": "2026-08-01T09:12:00.000Z"
}
```

| Field | Meaning |
| --- | --- |
| `metric` | Exact metric string from the monitor type's catalog (`success`, `status_code`, `response_time`, `metrics['ssl.days_until_expiry']`, …) |
| `operator` | `>`, `<`, `>=`, `<=`, `==`, `!=` for numbers; `==`, `!=` for the boolean `success`; `in_groups`, `not_in_groups` for status codes |
| `value` | Always a string: a number (`"2000"`), `"true"`/`"false"`, or a JSON array of status codes/groups (`'["2xx","3xx"]'`, `'[200,301]'`) |
| `severity` | `critical` or `warning` — see [alerting severity](/docs/alerting#severity) |
| `failure_threshold` | Consecutive failing checks before the rule fires (1–100) |
| `min_failed_probes` | Minimum number of locations that must report the failure in the same evaluation before it counts (1–100, default 1). It can never exceed the monitor's number of locations (`1` for heartbeat): a higher value is `400 minFailedProbes: must be at most N — this monitor checks from N location(s)`, and if the monitor later loses locations, rules above the new count are clamped (the clamp is written to the activity log). Multi-location confirmation of **Down** comes from `failure_threshold` plus the accelerated re-checks from other locations — see [Locations & scheduling](/docs/monitors/scheduling) |
| `duration_window_seconds` | Optional time window the condition must persist for (0 = off; up to 86400) |
| `enabled` | Disabled rules never fire; disabling resolves any incident the rule left open |
| `is_system` | `true` for the auto-created defaults — editable and toggleable, not deletable |
| `contact_group` | Optional contact group this rule notifies instead of the monitor's default routing |

## List rules

`GET /v1/monitors/{id}/alert-rules` — all of a monitor's rules (system
defaults + custom), newest first. Not paginated (`has_more` is always
`false`).

```bash
curl https://watchfor.io/api/v1/monitors/0b0f6a41-…/alert-rules \
  -H "Authorization: Bearer wf_live_YOUR_KEY"
```

## Create a rule

`POST /v1/monitors/{id}/alert-rules` — requires `write` scope. The rule
is pushed to the alerting engine immediately.

| Field | Required | Notes |
| --- | --- | --- |
| `name` | yes | 1–200 characters |
| `metric` | yes | From the type's catalog (`available_metrics` on `GET /v1/monitors/{id}` or the [reference](/docs/api/monitor-types)); `success` is valid on every type |
| `operator` | yes | Must fit the metric's value type (see above) |
| `value` | yes | String; must fit the metric's value type and unit range |
| `severity` | no | `critical` (default) or `warning` |
| `failureThreshold` | no | 1–100, default 1 |
| `minFailedProbes` | no | 1–100, default 1; at most the monitor's location count |
| `durationWindow` | no | Seconds, 0–86400, default 0 |
| `contactGroupId` | no | A contact group id in your organization, or `null` |

```bash
curl -X POST https://watchfor.io/api/v1/monitors/0b0f6a41-…/alert-rules \
  -H "Authorization: Bearer wf_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Slow response",
    "metric": "response_time",
    "operator": ">",
    "value": "2000",
    "severity": "warning",
    "failureThreshold": 3
  }'
```

Returns `201` with the rule. Supports
[`Idempotency-Key`](/docs/api/errors#idempotency--safe-retries).

**Validation** is catalog-driven so you can only create rules the
dashboard could: an unknown `metric` returns `400` listing the valid
metrics for that type; an operator that does not fit the metric's value
type returns `400` listing the valid operators; a numeric `value`
outside the metric's sensible range (days, milliseconds, percent, 0/1
flags, scores) returns `400` with the range and unit. Status-code values
must be a non-empty JSON array of codes 100–599 or groups `"1xx"`–`"5xx"`.

**One rule per metric per severity.** A duplicate returns
`409 conflict` with the existing rule's id — `PATCH` that rule instead.
(A warning + critical pair on the same metric is the normal way to get
two thresholds.)

## Get a rule

`GET /v1/monitors/{id}/alert-rules/{ruleId}`

## Update or enable/disable a rule

`PATCH /v1/monitors/{id}/alert-rules/{ruleId}` — requires `write` scope.
Partial: send any of `name`, `metric`, `operator`, `value`, `severity`,
`failureThreshold`, `minFailedProbes`, `durationWindow`, `contactGroupId`
and `enabled`.

```bash
curl -X PATCH https://watchfor.io/api/v1/monitors/0b0f6a41-…/alert-rules/a16c3a38-… \
  -H "Authorization: Bearer wf_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "value": "5000", "enabled": false }'
```

- Changing `metric`, `operator` or `value` re-validates the effective
  triple against the catalog; changing `metric` or `severity` re-checks
  the one-rule-per-metric-per-severity invariant (`409` on collision).
- `enabled: false` disables the rule **and resolves any incident it left
  firing**; `enabled: true` re-arms it. System rules can be toggled and
  edited like any other.
- Field changes and toggles are recorded separately in the
  [activity log](/docs/api/activity) (`alert_rule.updated`,
  `alert_rule.enabled` / `alert_rule.disabled`).

## Delete a rule

`DELETE /v1/monitors/{id}/alert-rules/{ruleId}` — requires `write`
scope. Returns `200` with `{ "object": "deleted", "id": "…", "deleted": true }`.
**System rules** (`is_system: true`) cannot be deleted — the request
returns `403 forbidden`; disable them instead.

## How rules become incidents

A rule fires only after `failure_threshold` consecutive failing checks
(default 3). After the first failure the monitor shows **Degraded** and
WatchFor re-checks it from other locations on an accelerated schedule, so
the threshold is normally reached by several locations within minutes —
that confirmation is what separates **Down** (incident, notifications,
status page) from **Degraded** (one failed location, no page).
`min_failed_probes` additionally requires that many locations to report the
failure in the same evaluation (default 1). A few monitor types that observe
a single global fact (for example domain expiry via RDAP, heartbeat
check-ins, MCP tool inventory drift) are confirmation-exempt. Details:
[how incidents work](/docs/alerting/incidents) and
[locations & scheduling](/docs/monitors/scheduling).

## Also via

MCP tools `list_alert_rules`, `create_alert_rule`, `update_alert_rule`,
`delete_alert_rule`; A2A skill `explain-monitor` (reads rules). The
[monitor types reference](/docs/api/monitor-types) lists every metric,
operator and example value per type.

---

Canonical page: https://watchfor.io/docs/api/alert-rules · All docs: https://watchfor.io/docs · Site guide: https://watchfor.io/llms.txt
