Skip to main content

Operating Compliance-Health Scores as an Early-Warning Signal

The compliance-health score is only useful if someone watches it, and the dashboard panel is only open when a human happens to look. This runbook is the operator cadence that turns a read-only 0–100 score into an early-warning signal: poll the endpoints on a fixed schedule, alert on band transitions rather than raw numbers, and route every warnings[] entry to the deep page that actually remediates its factor.
The health surface is read-only advisory — nothing below blocks a send, suppresses a contact, or gates traffic. Every remediation step here is a tenant-owned control you configure and own; Orbit provides the surfaces and defaults them open. This page is an operational runbook, not legal advice — confirm which obligations apply to your traffic with qualified counsel.

1. The alert cadence

Poll the three read endpoints hourly and alert on band transitions, not absolute scores. GET /compliance/health/campaigns and GET /compliance/health/numbers return worst-first rows; a single hourly call to each typically surfaces every scope that moved. A once an hour cadence catches drift while it is still a watch-band trend — the stage where remediation is a targeting fix, not an incident. A minimal poller (any cron host, CI schedule, or monitoring runner):
Wire it into your existing alerting tool the same way you would any synthetic check, or adapt the request-signing pattern from API integration. Two rules keep the alert useful:
  • Alert on the transition, not the value. Store the previous band per scope (a file, a monitoring label, a small KV) and notify only when it moves. A score that lives at watch for a quarter is background; a score that enters watch is the page.
  • Alert on warning severity, too. An at_risk band with no change is routine follow-up; a warning whose severity reaches critical — especially carrier_rejection — deserves the runbook below even when the band label stubbornly reads the same.

2. The score-drop runbook

Map each band transition to a fixed response. The point of a runbook is that the response is decided before the alert fires.
  1. Open the organization scope GET /compliance/health and read the consent_coverage factor value — share of contacted recipients holding a current granted consent.
  2. Trace the gap with the Consent API: look up a sample of recently-contacted recipients and find the capture path (import, form, inbound keyword) that never wrote a consent record. Fix the capture path, not the score.
  3. Review suppression in Opt-Out & Suppression: confirm the opt-out path is enforced end to end and that recent STOP replies were actually suppressed — a broken suppression path shows up as rising opt-out and STOP factors before it shows up as a complaint.

at_risk → critical — lifecycle errors + emergency-stop rehearsal

  1. Re-read with a short window (window_days=7) to confirm the drop is current drift and not a trailing-window artifact.
  2. Resolve the worst warnings[] factor through its deep page (Section 3). Only one factor is usually acute; fix it before touching anything else.
  3. If carrier_rejection is the acute factor, work the compliance-profile lifecycle errors checklist: a 422 REQUIRED/NOT_APPROVED means the sender’s profile packet never cleared, and a 409 LOCKED/IN_USE blocks the fix until you clone-and-fix. A sender with an unapproved profile draws carrier rejections until the packet clears.
  4. Rehearse the emergency stop once — an owner/admin activates, verifies the blocked-send 403 code, deactivates, and confirms the status read. The emergency-stop runbook has the drill procedure. Rehearsing it in a critical band is how the control stays tenant-owned and operable: an operator who has never flipped the switch in peacetime will fumble it in an incident. Trigger it for real only when the cost of one more send outweighs halting everything — suspected list contamination, an active abuse signal, a carrier complaint wave.

3. Map warnings[] to deep pages

Each warnings[] entry names exactly one factor. Route it to the page whose control moves that factor — the recommendation text in the entry usually points at the right one; this table is the fixed mapping. There is no reset button for any of these — the score recovers when the underlying metric recovers, and a fresh-window re-read is how you confirm the fix landed.

4. Tenant-owned framing

Health scores never block. The whole point of the cadence above is that you decide what a watch trend, an at_risk band, or a critical warning means for your traffic — Orbit computes the signal and defaults every gate open. Nothing in this runbook is mandatory, and no step here modifies your traffic unless you run it. Treat the score the way you treat a carrier’s complaint desk: the early read is only as useful as the runbook it feeds, and the runbook is yours.

5. Worked example: one band transition

A spring-promo sender (+14155550101) sits at healthy for weeks. Your hourly poller logs:
  1. Tue 12:00, healthy → watch: the alert fires once. The sender row shows a consent_coverage warning — a weekend import of 12,000 recipients dropped coverage from 98% to 81%. The owning team gets a ticket; no traffic change.
  2. Tue 15:00, runbook runs: a sample lookup through the Consent API confirms the import path never wrote consent records. The team re-permissions the list through the import consent flow and verifies records exist for sampled recipients.
  3. Wed 00:00, next hourly poll: watch holds — expected, the 30-day window still holds the gap.
  4. Next day, band returns to healthy: the import flow now writes consent at capture, and a window_days=7 re-read confirms coverage at 97%. The poller’s single alert was the entire cost — one page, one transition, one fix.
Contrast with the un-runbooked path: the same import unpolled reaches at_risk within a week, and the first critical carrier-rejection warning arrives at campaign launch — the moment volume spikes and the throttle costs the most.