Skip to main content

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: 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: 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. 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: 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, so they inherit your organization’s escalation routing; wire critical-severity rules into whatever paging path your on-call follows.