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

# Alerts

> Configure metric-based alert rules that fire notifications to configured Slack, Teams, webhook, or ops-email destinations when thresholds are breached.

Alert rules monitor platform metrics and trigger notifications when thresholds
are breached. Each rule defines a metric, comparison operator, threshold,
time window, and destinations.

See [Platform — Alert Rules](../operations-reporting/alert-rules) for the operator guide
and [Notifications API](./notifications) for configuring destinations.

All endpoints require tenant context and RBAC:

* **viewer / editor / admin** can list and read
* **editor / admin** can create, update, and delete

## GET /api/v1/alerts

List all alert rules for the tenant.

### Response

```json theme={null}
[
  {
    "id": "clx...",
    "tenantId": "my-tenant",
    "name": "High Error Rate",
    "metric": "degraded_scoring_rate",
    "operator": "gt",
    "threshold": 0.05,
    "windowMinutes": 5,
    "channels": [
      "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
    ],
    "cooldownMinutes": 60,
    "enabled": true,
    "lastFiredAt": null,
    "status": "ok",
    "createdAt": "2026-04-17T12:00:00.000Z",
    "updatedAt": "2026-04-17T12:00:00.000Z"
  }
]
```

***

## POST /api/v1/alerts

Create a new alert rule. **Editor or Admin.**

### Request Body

| Field             | Type    | Required | Description                                          |
| ----------------- | ------- | -------- | ---------------------------------------------------- |
| `name`            | string  | Yes      | Alert rule name                                      |
| `metric`          | string  | Yes      | See [Supported metrics](#supported-metrics)          |
| `operator`        | string  | Yes      | `gt`, `lt`, `gte`, `lte`, `eq`                       |
| `threshold`       | number  | Yes      | Threshold value to compare against                   |
| `windowMinutes`   | number  | No       | Observation window (default: 5)                      |
| `channels`        | array   | Yes      | Destinations — see [Channel shapes](#channel-shapes) |
| `cooldownMinutes` | number  | No       | Min time between consecutive fires (default: 60)     |
| `enabled`         | boolean | No       | Whether the rule is active (default: true)           |

### Supported metrics

| Metric                  | Unit           | Description                                                  |
| ----------------------- | -------------- | ------------------------------------------------------------ |
| `acceptance_rate`       | 0–1            | Positive outcomes / impressions                              |
| `ctr`                   | 0–1            | Clicks / impressions                                         |
| `revenue`               | currency units | Sum of conversion values                                     |
| `selection_frequency`   | 0–1            | Decisions with ≥1 selected offer / total traces              |
| `latency_p99`           | milliseconds   | p99 of decision latency                                      |
| `degraded_scoring_rate` | 0–1            | Traces with degradedScoring=true / total                     |
| `http_5xx_count`        | count          | Number of 5xx API responses in the window                    |
| `suppression_rate`      | 0–1            | Qualified candidates removed by suppression / contact policy |
| `empty_candidate_rate`  | 0–1            | Decisions that returned zero offers / total decisions        |

<Note>
  The `metric` field is a free-form string at the API layer — the evaluator recognizes the
  names above (`SUPPORTED_METRICS` in `lib/analytics/metrics.ts`). A rule whose `metric` is
  not in this list is stored, but every evaluation resolves to `status: "unsupported_metric"`
  and never fires. `suppression_rate` and `empty_candidate_rate` are also the two signals the
  [Decision Sentinel](./cron#get-apiv1cronai-sentinel) cron watches.
</Note>

### Channel shapes

The `channels` array accepts three shapes (mix and match allowed):

```json theme={null}
// 1. Provider UUID (preferred — references a NotificationProvider)
"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"

// 2. { providerId } — same semantics, explicit key
{ "providerId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" }

// 3. Legacy { type, target } — direct webhook / email dispatch
{ "type": "webhook", "target": "https://hooks.slack.com/services/..." }
{ "type": "email",   "target": "ops@example.com" }
```

Configure destinations under **Settings → Integrations → Notifications** or
via `POST /api/v1/notifications/providers`.

### Example

```bash theme={null}
curl -X POST https://playground.kaireonai.com/api/v1/alerts \
  -H "Content-Type: application/json" \
  -H "X-Requested-With: XMLHttpRequest" \
  -H "X-Tenant-Id: my-tenant" \
  -d '{
    "name": "p99 Latency Spike",
    "metric": "latency_p99",
    "operator": "gt",
    "threshold": 500,
    "windowMinutes": 10,
    "cooldownMinutes": 30,
    "channels": [
      "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
    ]
  }'
```

### Response (201)

Returns the created alert rule.

***

## GET /api/v1/alerts/:id

Get a single alert rule by ID.

***

## PUT /api/v1/alerts/:id

Update an existing alert rule. All fields are optional. **Editor or Admin.**

***

## DELETE /api/v1/alerts/:id

Delete an alert rule. **Editor or Admin.** Returns `204 No Content`.

***

## Rule lifecycle status

The `status` field reflects the outcome of the most recent evaluation:

| Status               | Meaning                                                                  |
| -------------------- | ------------------------------------------------------------------------ |
| `ok`                 | Last evaluation did not trigger                                          |
| `fired`              | Last evaluation triggered and at least one destination accepted dispatch |
| `cooldown`           | Triggered, but still inside `cooldownMinutes` since `lastFiredAt`        |
| `delivery_failed`    | Triggered, but every destination returned a failure                      |
| `unsupported_metric` | `metric` is not recognized by the evaluator                              |

The evaluator runs on every cron tick — see [Cron](./cron) for the `/api/cron/tick` entry point.
