Skip to main content

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: bounced rows 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 / rejected minus 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.
Every component and its weight travels with the response, so you always answer “why a 0.42?” from the 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

Unknown contact ids return 404. Possible 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

Readings for the segment rollup:
  • sendableHealth — mean raw health over the population you may actually send to. null when 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.
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’s consent_records ledger UNION the legacy channel_preferences[channel].opted_out flag.
When either is true, 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_records at 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.
A contact with no identifier on the channel (no 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 set truncated: 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 across sms, 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 scores insufficientData: 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 reachableShare under your floor means re-clean the segment or accept the suppression numbers before you spend; a low sendableHealth means 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 — route at_risk contacts 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.
Related: Number health, warming, and carrier reputation for sender-side scores, and Insights dashboards for the aggregate /insights/deliverability surface.