Stuck Regarding Logic in a Salesforce Marketing Cloud Journey Flow
Summary
A Salesforce Marketing Cloud Journey Builder flow was triggering on every Case record update, even when unrelated fields changed, causing unwanted email sends to contacts. The journey was configured to enter based on Case Status (“New” or “Closed”) and a custom isMarketing__c field being true, but lacked field-change validation that users typically expect from similar Salesforce automation tools.
Root Cause
Journey Builder evaluates record state, not field transitions. Unlike Salesforce Flows or Process Builder, Marketing Cloud’s entry criteria does not include an option for “only when fields change to meet the criteria.” The journey checks the current values of Status and isMarketing__c on every update event, triggering whenever both conditions are met, regardless of which fields actually changed.
Why This Happens in Real Systems
Marketing Cloud’s architecture prioritizes simplicity over granular control:
- Entry sources in Journey Builder are designed to capture records already meeting criteria, not track specific state transitions
- The platform lacks built-in “was field X = Y before update” logic
- Contact-level re-entry settings conflict with the need to send unique communications per record
- Integration between Sales Cloud and Marketing Cloud focuses on data synchronization rather than transactional change detection
Real-World Impact
Business stakeholders experienced:
- Email fatigue: Contacts received duplicate emails for Case updates unrelated to marketing needs
- Operational noise: Marketing teams spent time investigating false-positive journey entries
- User confusion: Sales teams questioned why Case updates triggered marketing communications
- Resource waste: Unnecessary API calls and data processing for non-marketing-relevant updates
Example or Code
Journey Entry Configuration (No Field-Transition Logic Available)
{
"entrySource": {
"object": "Case",
"events": ["created", "updated"],
"filter": {
"conditions": [
{"field": "Status", "operator": "equals", "value": ["New", "Closed"]},
{"field": "isMarketing__c", "operator": "equals", "value": true}
]
}
}
}
Recommended Solution: Contact Data Extension with Timestamp
-- In Salesforce Data Extension
ALTER TABLE Contact_Marketing_Flag ADD Last_Case_Update_Date DATETIME;
-- Update via API or Scheduled Query when Case.isMarketing__c becomes true
UPDATE Contact_Marketing SET
Last_Case_Update_Date = GETDATE(),
Has_Qualified_Case = 1
WHERE ContactId = @ContactId;
Journey Entry Using Contact Data
-- Entry filter using contact-level tracking
SELECT c.Id
FROM Contact c
WHERE c.Has_Qualified_Case = 1
AND c.Last_Case_Update_Date >= @JourneyStartDate
How Senior Engineers Fix It
Implement a layered approach to prevent unwanted triggers:
- Contact-level tracking: Use a Contact data extension to store the last qualified Case update timestamp
- Scheduled evaluation: Run hourly/daily queries to identify newly qualified records rather than real-time triggers
- Secondary validation: Add a DateTime field that gets populated only when the Case first meets marketing criteria
- Decoupled processing: Move email logic to a separate automation that checks both Case and Contact data before sending
Why Juniors Miss It
Common knowledge gaps lead to unexpected behavior:
- Assuming parity with Salesforce: Expecting Journey Builder to work like Process Builder or Flows with field-change conditions
- Overlooking architectural differences: Not understanding that Marketing Cloud is primarily a messaging platform, not a workflow engine
- Missing contact-level context: Focusing only on the triggering object rather than leveraging Contact data extensions for state management
- Re-entry setting confusion: Misunderstanding how re-entry settings interact with different entry source types