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

# Messaging deliverability alerting: thresholds, evaluation, and notification fan-out

> How Orbit pages you when a messaging channel's delivery rate drops or its failure/bounce rate spikes — the metric definitions, the rule model, the evaluation endpoints, and what a breach response carries.

# Messaging deliverability alerting model

Deliverability alerting closes the loop that spend alerting cannot: a balance alert tells you money is leaving, a deliverability alert tells you messages are not landing. Orbit exposes four tenant-facing endpoints for this — `GET /v1/messages/deliverability/alert-status`, `GET`/`PUT /v1/messages/deliverability/alert-rules`, and `POST /v1/messages/deliverability/alert-check` — working together as one model: compute per-channel deliverability metrics over a rolling window, compare them against your thresholds, and notify you when a threshold is breached.

## Why alert on deliverability, not just spend

A mature messaging operation pages on more than budget. Before this model, Orbit alerted on spend and balance thresholds, while a channel silently degrading — an SMS route grey-routed, a WhatsApp template rejected, an email domain losing sender reputation — only showed up as a falling number on the stats card, which nobody watches at 3 a.m. Peer CPaaS platforms expose per-channel deliverability thresholds for exactly this reason.

This alerting is the messaging sibling of two existing alert engines: the voice-queue rule alerts that page supervisors on queue health, and the AI-agent KPI alerts that fire when an agent's rated quality crosses a floor. All three share the same shape — a ruleset you own, an explicit check that evaluates it, and the same notification fan-out — so operators learn one model, not three.

## Metrics you can threshold

Three metrics are alertable, computed per channel and for the cross-channel aggregate:

| Metric          | Direction        | Meaning                                                                                                        |
| --------------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| `delivery_rate` | higher is better | Delivered messages as a percentage of all messages that reached a terminal status in the window.               |
| `failure_rate`  | lower is better  | Terminal non-delivered messages (failed, undelivered, rejected, bounced) as a percentage of terminal messages. |
| `bounce_rate`   | lower is better  | Bounced messages only (a subset of failures) as a percentage of terminal messages.                             |

Each metric runs over the same canonical status sets that power the `GET /v1/messages/stats` KPI card: the denominator is messages in a terminal status, and the delivered numerator is channel-aware (for example, the "submitted without receipt" outcome counts as delivered only on Meta DM channels, where no delivery receipt is expected). The denominator is exactly the terminal-status set — nothing queued or still in flight washes the rate. The consequence is deliberate: an alarm and the dashboard report the same number, so you never argue about which figure is real.

Outbound messages only, per your tenant, over a configurable window (1–90 days, default 7).

## The rule model

A rule is one threshold on one metric:

| Field        | Purpose                                                                                                                                                                 |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `metric`     | `delivery_rate`, `failure_rate`, or `bounce_rate`.                                                                                                                      |
| `comparator` | `lt` (breach when the value drops below the threshold) or `gt` (breach when it rises above). The natural default: delivery rate uses `lt`; failure and bounce use `gt`. |
| `threshold`  | Percentage, 0–100.                                                                                                                                                      |
| `channel`    | A specific channel (`sms`, `whatsapp`, `email`, …), or `null` for the cross-channel aggregate.                                                                          |
| `min_volume` | Minimum terminal messages in the window before the rule can fire (default 50). Keeps a tiny sample — 1 failure of 2 messages is a 50% failure rate — from paging you.   |
| `severity`   | `warning` or `critical`.                                                                                                                                                |
| `enabled`    | Set to `false` to mute a rule without deleting it.                                                                                                                      |

Rules are org-level configuration, stored with the rest of your organization's messaging settings rather than in a separate system. You can hold up to 50 rules; `PUT` replaces the full set in one write, so reads and writes against sibling settings never clobber each other.

Reading the rules (`GET`) requires the `messages:read` or `messages:write` scope. Writing them (`PUT`) additionally requires an owner, admin, or developer role — the same bar as every other messaging write. Every rules update appends an audit entry naming the actor.

## How evaluation runs

Four endpoints form the loop:

* **`GET /v1/messages/deliverability/alert-rules`** returns your configured rule set.
* **`PUT /v1/messages/deliverability/alert-rules`** replaces the rule set (validated body; a malformed rule is rejected with a 422).
* **`GET /v1/messages/deliverability/alert-status?days=N`** returns the current metrics, your rules, and their breach evaluation — read-only, so a dashboard banner can render the current alert state without paging anyone.
* **`POST /v1/messages/deliverability/alert-check?days=N`** runs the same evaluation and dispatches a notification for each breach — the in-app bell plus the [notification fan-out](/concepts/notification-model). The dashboard's "Check now" control calls this, and a recurring scheduler or external cron can call the same endpoint on a cadence.

A rule is skipped — never fired — when its channel had no terminal traffic in the window (no coverage is not a degradation), when the sample is below `min_volume`, or when the metric has no rate at all (an empty window evaluates to null, not 0%, so a quiet tenant never receives a "delivery rate dropped to 0" page).

Repeat checks against a persisting breach notify at most once per 30-minute dedupe window, keyed on channel, metric, and severity — so a dashboard on a refresh loop cannot spam you, and the same breach re-pages only half-hourly while it persists.

## Reading a breach response

`alert-status` and `alert-check` both return an `alerts` block alongside `metrics` and `alert_rules`:

| Field                      | Content                                                                                                                                                                                                                                                  |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alerts.breached`          | Whether any rule is currently in breach.                                                                                                                                                                                                                 |
| `alerts.severity`          | Worst severity across breaches (`critical` beats `warning`), or `null`.                                                                                                                                                                                  |
| `alerts.breaches[]`        | One entry per breached rule: `metric` (`delivery_rate` / `failure_rate` / `bounce_rate`), `channel` (`*` for the aggregate), measured `value`, `threshold`, `comparator`, `severity`, and `volume` — the terminal-message count backing the measurement. |
| `notifications_dispatched` | (`alert-check` only) the subset of breaches that actually paged, after dedupe.                                                                                                                                                                           |

The `metrics` array leads with the cross-channel aggregate (channel `*`), followed by one entry per channel, each carrying `terminal`, `delivered`, `failed`, `bounced`, and the three rates (null when the channel had no terminal traffic). The notification body mirrors these fields — for example, "Delivery rate is 61.2% on sms — below your 90% threshold — over the last 7 day(s) (12,400 messages)" — and links through to the Messages surface.

## Posture checklist

* **Pick thresholds per channel.** A sensible starting set: page when `delivery_rate` drops below your floor, when `failure_rate` rises above your ceiling, and when `bounce_rate` spikes — scoped to each channel you operate, plus a cross-channel rule as the catch-all.
* **Size `min_volume` to your traffic.** Default 50; raise it for noisy low-stakes channels, lower it only where a small sample is still an event.
* **Run `alert-check` on a cadence.** Evaluation only happens when something calls it — the dashboard's check control, a scheduled job, or your own cron. Status polling alone never notifies.
* **Route the bell into escalation.** Breaches arrive through the standard [notification model](/concepts/notification-model), so they inherit your organization's escalation routing; wire critical-severity rules into whatever paging path your on-call follows.

## Related pages

* [Notification model](/concepts/notification-model) — the fan-out a breach travels on (in-app bell, plus your organization's other notification channels).
* [Analytics pipeline](/concepts/analytics-pipeline) — where the terminal-status aggregates behind these metrics also feed rollups.
* [Messaging endpoints](/api-reference/endpoints/messaging) — the `/messages` surface these alerting endpoints hang off.
* [Deliverability Lab pre-send scoring](/concepts/deliverability-lab-pre-send-scoring) — the complement: scoring a message's deliverability risk before you send it.
