Sales teams rely on Salesforce CRM to tell them whether a Contact or Lead is reachable. If someone’s email has permanently bounced in SFMC, the CRM record should reflect that — so sales reps are not wasting calls following up on “did you get my email” with someone whose inbox has been dead for months.
The integration is straightforward in concept but has a nuance that catches most teams out when they first build it. Simply querying the _Bounce Data View for hard bounces and syncing everything you find is not the correct approach. The result will be a lot of noise, false flags, and contacts marked as unreachable in CRM who are actually perfectly fine.
_Bounce WHERE BounceCategory = 'Hard bounce' and treating all results as actionable is wrong. Hard bounces from non-trusted ISPs are handled by SFMC as soft bounces — the subscriber stays Active and continues receiving emails. Only hard bounces from trusted ISPs result in the subscriber being moved to Held status immediately. If you sync every hard bounce event to CRM, you will be flagging contacts in CRM as unreachable when SFMC itself is still happily sending to them.
How SFMC Actually Handles Bounces
Before building the sync, understanding the subscriber status model matters. SFMC does not treat all hard bounces equally, and the path a subscriber takes depends on whether the bounce came from a trusted ISP.
| Bounce Source | SFMC Treatment | Subscriber Status After | Should Sync to CRM? |
|---|---|---|---|
| Hard bounce — trusted ISP | Immediate suppression | Held / Undeliverable | Yes — permanent failure confirmed |
| Hard bounce — non-trusted ISP | Treated as soft bounce, retried | Bounced (temporary) | No — SFMC still sending to this address |
| 3+ bounces in 15 days (any ISP) | Automatic suppression after threshold | Held | Yes — threshold reached, permanently suppressed |
| Subscriber opens/clicks after bounce | Status reset to Active | Active | No — email was deliverable despite bounce record |
The correct signal to sync to CRM is not a bounce event in _Bounce. It is a subscriber reaching Held status in _Subscribers. That is the moment SFMC itself has decided the address is permanently undeliverable and stopped all future sends. That is what your CRM should know about.
The Full Architecture
The SQL Query
This is the exact query for the daily Automation Studio step. It joins _Bounce with _Subscribers to confirm Held status, applies a 24-hour incremental filter, and brings in the email address and bounce category for context in the staging DE:
Automation Studio — Daily Hard Bounce Staging SQL
/* Pulls confirmed hard bounces — only where SFMC has also moved the subscriber to Held status. This is the CRM-worthy signal. Run daily. Action: Overwrite on Hard_Bounce_Staging_DE. */ SELECT DISTINCT b.SubscriberKey, b.EmailAddress, b.BounceCategory, b.SMTPCode, LEFT(b.SMTPBounceReason, 500) AS BounceReason, b.EventDate AS BounceDate FROM _Bounce b JOIN _Subscribers s ON b.SubscriberKey = s.SubscriberKey WHERE b.BounceCategory = 'Hard bounce' AND s.Status = 'Held' AND b.EventDate >= DATEADD(day, -1, GETDATE()) AND b.EventDate < GETDATE()
SMTPBounceReason field in _Bounce is nvarchar(max) — it has no length limit in the data view. When writing to a Data Extension, always wrap it in LEFT(SMTPBounceReason, 500) or similar to stay within the DE field character limit, otherwise the query will fail silently on records with long bounce reason strings.
Setting Up the Salesforce CRM Fields
Before configuring the Journey, create two custom fields on both the Contact and Lead objects in Salesforce CRM. These are simple to set up via Setup in the CRM:
- Hard_Bounce__c — Checkbox field. Ticked when the Journey Object Activity runs. Sales reps can filter their views on this field immediately.
- Hard_Bounce_Date__c — Date/DateTime field. Set to the
BounceDatevalue from the staging DE. Gives visibility into when the address became undeliverable.
In the Journey Builder Object Activity, configure it as Find and Update. Match on SubscriberKey to ContactID and LeadID respectively. Map Hard_Bounce__c to true and Hard_Bounce_Date__c to the BounceDate field from the journey DE. Set the Journey to Re-entry Always so that if the same contact bounces again at a later date, the BounceDate in CRM is updated to the most recent event.
CRM Fields to Add to Your Page Layouts
Once the fields exist and data starts populating, add them to the Contact and Lead page layouts in Salesforce so sales reps can see email reachability at a glance. A simple section called “Email Deliverability” with the two fields is enough. You can also add a CRM report or list view filtered on Hard_Bounce__c = TRUE so the sales team has a single place to review and action these records periodically.
Setup Checklist
- Create
Hard_Bounce__c(Checkbox) andHard_Bounce_Date__c(DateTime) on both Contact and Lead objects in Salesforce CRM. - Create the staging DE as sendable, with SubscriberKey as the primary key and a send relationship linked to the CRM ContactID or LeadID field.
- The SQL query must JOIN
_Subscribersand filter ons.Status = 'Held'. Filtering on_Bouncealone is not sufficient. - Wrap
SMTPBounceReasoninLEFT(..., 500)to prevent silent query failures on records with long bounce reason strings. - Configure the Journey with Re-entry Always so BounceDate stays current if the same address bounces across multiple campaigns.
- Chain the SQL activity and Journey in the same Automation Studio automation. Do not schedule them independently.
- After the first run, spot-check five records in CRM to confirm
Hard_Bounce__c = TRUEand verify the BounceDate matches the SFMC bounce event. - Add a CRM list view or report filtered on
Hard_Bounce__c = TRUEso the sales team can action these records without needing to query the data themselves.
Frequently Asked Questions
The Object Activity in Journey Builder can only target one object type per activity. For orgs where the same SubscriberKey might exist as both a Contact and a Lead (common in pre-conversion pipelines), add two separate Object Activities in the Journey — one targeting Contact, one targeting Lead. The Find logic will return no results and skip cleanly for the object type where the record does not exist, so it is safe to run both.
We recommend against it for bounce data. HasOptedOutOfEmail represents a deliberate opt-out, not a deliverability failure. Mixing the two signals in one field makes reporting unclear and can cause problems if a subscriber’s email address is updated and the record should become reachable again. Keep bounce data in its own dedicated fields so you can distinguish “this person asked not to be emailed” from “this email address does not work”.
SFMC will reset a subscriber’s status from Held back to Active if they open or click a link in a forwarded version of an email, or if a Salesforce Support case is raised to manually release the Held status. In those cases, you would want to set Hard_Bounce__c = FALSE in CRM. The cleanest way to handle this is a second daily query against _Subscribers for any subscriber where status has returned to Active and Hard_Bounce__c is currently TRUE in CRM, and run a corresponding Journey Object Activity to clear the flag.
Yes, but the mechanics differ slightly. With Marketing Cloud Connect in place, the Journey Object Activity handles the CRM update natively — it uses the MC Connect bridge to write directly to Salesforce objects. If you are using a custom integration without MC Connect, you need to use SSJS with the UpdateSingleSalesforceObject AMPscript function wrapped in TreatAsContent, or export the staging DE via SFTP and have your integration layer pick it up and process the CRM updates.
SFMC and Salesforce CRM Not Talking to Each Other Properly?
Bounce data, unsubscribe signals, engagement scores — keeping SFMC and Salesforce CRM in sync requires deliberate architecture. Genetrix designs and implements these integrations for clients who need their CRM to reflect the real state of their email deliverability.