Plan · 2026-05-19
SFMC is already auto-holding hard-bouncers — sender reputation is safe. But 19,388 non-sendable subscribers (11.9% of the renewal count) still sit on the bill. One DLRS rollup + one formula change stops the bleed and shrinks the count.
SFMC has been writing bounce events into Salesforce for years. The patient Contact record was supposed to receive an aggregated signal — that wiring was never built.
et4ae5__IndividualEmailResult__c
SFMCEmailBounceDate__c populated
Production SOQL run by Kyle 2026-05-19. The destination fields exist
on Contact already (with the description "set via flow based on IER
data"). Nobody ever built the populator. The 2026-05-19 audit of the
entire et4ae5 managed-package namespace (13 flows + 14
Apex triggers) confirmed no built-in mechanism does this work —
nothing existing to step on.
The real story isn't deliverability — SFMC already protects sender reputation by auto-holding hard-bouncers. The real story is billing.
_Subscribers snapshot (run 2026-05-19)
| Subscriber state | Count | % of total | Billable? | Sendable? |
|---|---|---|---|---|
active |
143,915 | 88.1% | Yes | ✓ Yes |
held (SFMC auto-paused) |
7,541 | 4.6% | Yes | ✗ No |
bounced (recent, pre-held) |
9,304 | 5.7% | Yes | ✗ No |
unsubscribed |
2,543 | 1.6% | Yes | ✗ No |
| Total non-sendable | 19,388 | 11.9% | Yes — bill applies | — |
Source: SFMC Query Activity run 2026-05-19 against the
_Subscribers system view. The 19,388 non-sendable
subscribers are counted in renewal pricing but generate zero send
value. This is the value-prop — not deliverability rescue.
_Subscribers check
resolved that concern — SFMC's auto-hold is doing its
job: 7,541 subscribers in held are not being sent to. So
reputation isn't actively eroding from our side. The fix here is
operational/financial, not deliverability rescue.
DLRS counts each hard bounce up to its parent Contact and stamps the
most-recent bounce date onto a custom Contact field. Mirrors the only
existing rollup on the same parent object
(Contact_Most_Recent_TC_Sent).
et4ae5__HardBounce__c = TRUESFMCEmailBounceDate__cIsSFMCEmailBounced__c
Formula(Checkbox) that compares bounce-date to a new
Email_Last_Updated_Date_Time__c field.
Lifecycle-aware — self-clears when Customer Service updates the
email on the next call-in. See §5.3.
Note: the standard Contact.EmailBouncedDate field is
reserved for SF-platform-native bounce tracking (on SF-sent email),
not SFMC bounces. Don't repoint there — these AdvancedRx custom fields
exist specifically to keep the two channels separate.
One new rollup record. Sarah opens the DLRS Lightning app (App Launcher → "Lookup Rollup" → "Manage Lookup Rollup Summaries" → New), gets a form with the fields below, types the values from the right column. The form is what the team uses for all 45 existing rollups — same pattern. Each row is one form field.
Field on dlrs__LookupRollupSummary2__mdt |
Value |
|---|---|
| Label / MasterLabel | Contact Most Recent Hard Bounce |
| DeveloperName | Contact_Most_Recent_Hard_Bounce |
| dlrs__ParentObject__c | Contact |
| dlrs__ChildObject__c | et4ae5__IndividualEmailResult__c |
| dlrs__RelationshipField__c | et4ae5__Contact__c |
| dlrs__FieldToAggregate__c | et4ae5__DateBounced__c |
| dlrs__AggregateOperation__c | Max |
| dlrs__AggregateResultField__c | SFMCEmailBounceDate__c |
| dlrs__RelationshipCriteria__c | et4ae5__HardBounce__c = TRUE |
| dlrs__RelationshipCriteriaFields__c | et4ae5__HardBounce__c |
| dlrs__CalculationMode__c | Realtime |
| dlrs__Active__c | checked |
Three design questions surfaced. All resolved to the simpler option for v1; each can be revisited later.
Contact_Most_Recent_TC_Sent uses a
hardcoded DateSent > 2024-09-08T17:30 lower bound.
Resolved 2026-05-19 by reading the destination field's
description more carefully:
TC = Terms & Conditions, and 2024-09-08T17:30 is the timestamp
the 2024 T&C version went out. The cutoff is a business-policy
boundary ("which patients have seen the current T&C version"),
NOT a data-quality boundary. Pre-cutoff bounces are still
factually true and should count.
SFMCEmailBounceDate__c != null directly, a patient
whose old@email.com bounced 6 months ago stays
dropped from sync forever — even after Customer Service updates
them to a working new@email.com. The flag has no way
to forget.
AND( NOT(ISBLANK(SFMCEmailBounceDate__c)),
SFMCEmailBounceDate__c >= NULLVALUE(
Email_Last_Updated_Date_Time__c, DATETIMEVALUE("2020-01-01
00:00:00") ) )
The NULLVALUE default to a very-old date means
existing bouncers (whose email was never explicitly updated
post-bounce) still flag correctly on day 1. As soon as Customer
Service updates a patient's email on the next call-in, the
formula transitions to comparing against the actual update
timestamp.
Three steps trace a hard-bouncing patient through the lifecycle. Each row is a state snapshot.
| Step | SFMCEmailBounceDate__c |
Email_Last_Updated_Date_Time__c |
IsSFMCEmailBounced__c |
|---|---|---|---|
1. Patient's bob@old.com bounces 2026-04-10 |
2026-04-10 | null (or older) | TRUE |
2. Patient calls in for refill, CS gets new email, updates to
bob@new.com 2026-05-19
|
2026-04-10 (unchanged — factual) | 2026-05-19 14:00 | FALSE |
| 3. New email also bounces 2026-06-01 (worst case) | 2026-06-01 | 2026-05-19 14:00 | TRUE |
The cleanup formula gates on
NOT(IsSFMCEmailBounced__c), so the patient drops out of
sync at step 1, comes back at step 2, drops again at step 3. The Date
field stays factually true throughout — useful for audit / history but
not the right field to gate behavior on.
Declarative. No source files to author by hand unless Sarah prefers metadata-source-based deploy.
Sync_to_Marketing_Cloud__c
Sync_to_Marketing_Cloud__c equal (=) True, poll
schedule every 15 minutes, 177,044 records currently synced. Note
the field name is "to" not
"with" — earlier plan drafts targeted the wrong
field. See §4.4.
Email_Last_Updated_Date_Time__c on Contact
ContactHistory
Email_Last_Updated_Date_Time__c from
ContactHistory.CreatedDate where
Field = 'Email', per-Contact MAX. Closes most of the
false-positive risk within Salesforce's 18–24 month history
retention.
Contact_Insert_Update_Same_Record
(BeforeSave)
ISCHANGED(Email), assign
$Record.Email_Last_Updated_Date_Time__c =
{!$Flow.CurrentDateTime}. No fault connector needed —
no DML to fail.
IsSFMCEmailBounced__c as Formula(Checkbox)
Sync_to_Marketing_Cloud__c to
Formula(Checkbox)
NOT(IsSFMCEmailBounced__c) + deceased +
business-approved staleness criteria. Destructive field-type
change but no logic lost. Remove the now-redundant flow decision
in Contact_Insert_Update_Same_Record.
Contact_Salesforce drop in SFMC
Once the rollup populates,
Contact.Sync_to_Marketing_Cloud__c (the field MC Connect
actually filters on — verified 2026-05-19, see §4.4) gets converted
from a stored Checkbox to a Formula(Checkbox) with all the gates baked
in:
AND( Email <> NULL, // existing behavior preserved NOT(IsSFMCEmailBounced__c), // NEW — drops 10,620 hard-bouncing patients ISBLANK(DeceasedDate), // defensive — currently 0 in prod ISBLANK(HealthCloudGA__DeceasedDate__c), // defensive — currently 0 in prod ISBLANK(EmailBouncedDate) // defensive — currently 0 in prod )
Production sizing (Kyle ran Developer Console SOQL 2026-05-19): 177,051 Contacts currently sync. 10,620 of them are hard-bouncing. 0 are deceased per SF fields. 0 have SF-native bounces. Post-cleanup: ~166,431 (6% reduction). The defensive ISBLANK gates have zero impact today but guard the future case where those fields start being populated.
No staleness gate in v1 — Kyle decision 2026-05-19. Staleness signals are fuzzy and carry real false-positive risk. Bounces are unambiguous. The 6% drop is meaningful and defensible without business politics around "how stale is too stale."
Gates on the lifecycle-aware Boolean, not the raw Date field. The Boolean self-clears when Customer Service updates a patient's email (per §5.3 + §5b) — so a patient whose old email bounced and got a new working address comes back into sync automatically. If we gated on the Date directly, the patient would stay dropped forever.
The numbers throughout this plan are live production data + live schema, not sandbox inference.
FieldDefinition SOQL on ClaudeTest 2026-05-19. Both
custom Contact fields exist with the
"set via flow based on IER data" descriptions. The single
existing DLRS rollup on
et4ae5__IndividualEmailResult__c
(Contact_Most_Recent_TC_Sent) verified to exclude bounces
via
HardBounce__c = FALSE AND SoftBounce__c = FALSE.et4ae5__IndividualEmailResult__c total →
1,635,895
HardBounce__c = TRUE OR SoftBounce__c = TRUE →
43,433
DateSent__c >= LAST_N_DAYS:30 →
75,429
Contact WHERE SFMCEmailBounceDate__c != null →
0
Contact WHERE IsSFMCEmailBounced__c = true →
0
NamespacePrefix = 'et4ae5' on 2026-05-19 surfaced 13
managed flows + 14 managed Apex triggers. All 13 flows fit a
housekeeping/lifecycle pattern (record-internal status updates,
SendDefinition state, deprecated WFR migrations). The suspect
MCC_TrackingAsOfIER is named after the
et4ae5__Tracking_As_Of__c field on IER itself — a
per-record helper, not a Contact-side aggregator. None of the managed
artifacts aggregate to Contact bounce fields._Subscribers on 2026-05-19 — output DE
Subscriber_Status_Check_Output. Query:
SELECT Status, COUNT(*) AS cnt FROM _Subscribers WHERE
SubscriberKey IS NOT NULL GROUP BY Status. Result: 143,915 active / 7,541 held / 9,304 bounced / 2,543
unsubscribed (163,303 total). Resolved the open question about whether
SFMC was auto-holding (it is) and pivoted §2 framing from
sender-reputation to billing-count.