Contact & segment deliverability health
The number-level health scores in Number health tell you how healthy your senders are. The per-contact deliverability health score answers the complementary question: how healthy is it to send to this contact, on this channel — and separately, whether consent lets you send at all. The dashboard surfaces it as a health card on the contact detail page, and two API endpoints expose the same verdict per contact and rolled up across a segment.1. What the score blends
The score reads the last 90 days of message traffic between you and the contact on one channel (sms, whatsapp, email, or voice), and blends
five components into a raw value in [0, 1] — 1 is healthiest:
- Hard bounce share (weight 0.35) — terminal failures that look
permanent:
bouncedrows plus the soft-NACK error-code families (unreachable,invalid number,no route). Hard bounces drag the score to 0 once they reach 20% of attempted sends. - Soft bounce share (weight 0.15) — other terminal failures
(
failed/undelivered/rejectedminus the hard-bounce branches). Transient, clears on retry, so the blend forgives them up to a 50% share. - Complaint share (weight 0.25) — reserved for per-contact complaint feeds. A complaint still blocks the send here, but as a consent overlay (section 3), not a raw-score penalty.
- Engagement share (weight 0.15) — inbound replies on the same channel, saturating once replies reach 10% of attempted sends.
- Failure recency (weight 0.1) — days since the newest terminal failure, decaying on a 14-day half-life. A fresh failure drags the score; a failure 60 days behind clean traffic barely matters.
components map — no black box.
2. The two endpoints
Both endpoints take an optional?channel= query param and default to
sms when omitted. Both are pure reads of persisted traffic — no live
provider call and nothing on the outbound path.
Per contact
label values: healthy
(sendable ≥ 0.8), watch (≥ 0.5), at_risk (< 0.5), consent_blocked
(suppressed or opted out), insufficient_data (no send signal yet).
Per segment
sendableHealth— mean raw health over the population you may actually send to.nullwhen every member is consent-blocked.overallHealth— mean raw health across all members, consent-blocked or not.suppressedCount/optedOutCount— the consent split (section 3).insufficientDataCount— members with no send signal (section 6).reachableShare— share of the segment sendable on this channel, in [0, 1]. A perfectly healthy segment can still be unreachable.
3. The consent-suppression overlay
The raw score answers “is this contact deliverable?”; the overlay answers “may we send?”. Conflating the two lies either to the operator (a healthy-but-blocked contact shows as healthy) or to a compliance reviewer (an opted-out send pretends to be a healthy send). The overlay folds three sources into two booleans per (contact, channel):suppressed— the address is on the tenant’s external suppression list (suppression_list,channel IN ('all', <channel>), not revoked): a global unsubscribe, hard-bounce suppression, or complaint suppression. Overrides per-channel opt-in, and outranks opt-out when both exist.optedOut— an explicit opt-out on this channel, from the contact’sconsent_recordsledger UNION the legacychannel_preferences[channel].opted_outflag.
sendableScore is 0 and blockReason is
"suppressed" or "opted_out"; the raw score and components stay visible
for audit.
Two sources are deliberately not part of this overlay:
- The hot-path STOP-response Redis fence — a cached fast-reject over
recent STOP traffic. Redundant with
consent_recordsat read time and still checked authoritatively at dispatch; including it here would make an honest rollup depend on a cache’s warm state. - The DNC import list — unattached address rows the overlay cannot join to a stored contact; the send-time gate applies it on the destination address instead.
email, no phone) skips
the suppression probe and keeps its raw score — the send-time 422 on a
missing identifier is the authoritative block there.
4. Segment rollup cost
The segment endpoint serves up to 5,000 materialised members per read in a constant number of grouped round-trips — the signal rollup plus one batched consent-overlay query — regardless of cohort size. A 5,000-member segment costs the same query shape as a 1-member one; members past the 5,000 cap settruncated: true in the response, and the member list the
endpoint scans comes from the materialised contact_segment_members
surface, not a re-run of the segment filter.
5. Where it appears in the dashboard
Open Audience → Contacts, pick a contact, and land on the Overview tab. The Deliverability health card sits between the engagement panel and the reachability card, with a channel selector acrosssms,
whatsapp, email, and voice. It renders the { label, score } pair
plus the same consent-suppression overlay and insufficient-data handling
the API returns above.
6. Zero-state semantics
A contact with no outbound traffic on the channel scoresinsufficientData: true
and renders No send signal yet — the first outbound traffic starts the
health record. It never shows as a healthy 1.0: the raw score reads
perfect only because nothing has been observed yet, and the honesty
contract keeps that distinct from a proven-healthy contact. Segment
rollups count these contacts in insufficientDataCount.
7. Put it to work
- Throttle a segment pre-campaign. Pull the segment rollup before a
send: a
reachableShareunder your floor means re-clean the segment or accept the suppression numbers before you spend; a lowsendableHealthmeans tune targeting before a real blast damages sender reputation. - Gate a flow on a health threshold. Poll the per-contact endpoint at
a flow step and branch on
sendableScore— routeat_riskcontacts to email or a re-permission segment rather than hammering a failing channel. Because scores are pure per-channel reads, a flow can probe each channel and pick the healthiest.
/insights/deliverability surface.