
Trigger SFMC Journeys from real-time MailOdds intent
Use MailOdds intent.hot_lead webhooks to insert rows into a Marketing Cloud Data Extension, then trigger a Journey Builder flow with AMPscript-personalised email and an optional MobileConnect SMS step.
Prerequisites
- MailOdds account (free tier works)
- API key from dashboard
Why webhook + Data Extension instead of Journey Builder Trigger Event
SFMC's proprietary Journey Builder Trigger Event API requires a Marketing Cloud package install per source system. For most teams it is faster to point the MailOdds webhook at a small middleware (Cloud Function, AWS Lambda, Heroku dyno) that authenticates against the Marketing Cloud REST API and inserts a row into a Data Extension. Journey Builder polls the Data Extension every 5 minutes, picks up new rows, and starts the configured journey.
That trade-off costs you 0-5 minutes of latency vs. the proprietary trigger, and saves you a multi-week package-install conversation with your Salesforce Admin.
MailOdds payload your middleware receives
Same payload as every MailOdds intent webhook. Verify the X-Signature-SHA256 header (HMAC-SHA256 of the raw body, keyed with your webhook secret) before
forwarding into Marketing Cloud.
POST your-middleware-url
JSON{
"event": "intent.hot_lead",
"request_id": "evt_b7f3e8a1c9d24f06",
"timestamp": 1714670400,
"data": {
"base_id": "msg_a3f8c2",
"heat_score": 0.87,
"dwell_seconds": 142,
"storefront_context": {
"last_product_viewed": {
"id": "sku-1042",
"title": "Alpaca Runner",
"price": "129.00",
"currency": "EUR"
},
"cart_value": "129.00",
"page_type": "product",
"utm_source": "klaviyo"
},
"sms_eligible": true,
"cross_device": false,
"device_count": 1,
"device_types": ["desktop"],
"most_active_device": "desktop",
"first_device_seen_at": "2026-04-29T10:14:02Z",
"cluster_confidence": 1.0
}
} REST API call your middleware makes
Two HTTP calls: get an OAuth token, then async-insert one row into the MAILODDS_HOT_LEADS Data Extension. Cache the token until 1 minute before expires_in; tokens are valid for 18 minutes.
forward-to-sfmc.sh
BASH# Step 1: get a Marketing Cloud OAuth token (Server-to-Server, JWT bearer
# is preferred for production; client_credentials shown here for clarity).
curl -sf -X POST "https://YOUR-SUBDOMAIN.auth.marketingcloudapis.com/v2/token" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "<MC_CLIENT_ID>",
"client_secret": "<MC_CLIENT_SECRET>",
"scope": "data_extensions_write events_write"
}'
# Returns: { "access_token": "<TOKEN>", "expires_in": 1080, "rest_instance_url": "..." }
# Step 2: insert the MailOdds event into a Data Extension.
# The Data Extension acts as the queue Journey Builder polls.
curl -sf -X POST "${REST_INSTANCE_URL}data/v1/async/dataextensions/key:MAILODDS_HOT_LEADS/rows" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"items": [{
"request_id": "evt_b7f3e8a1c9d24f06",
"subscriber_email": "visitor@example.com",
"heat_score": 0.87,
"product_id": "sku-1042",
"product_title": "Alpaca Runner",
"product_price": "129.00",
"currency": "EUR",
"cart_value": "129.00",
"page_type": "product",
"utm_source": "klaviyo",
"cross_device": false,
"device_count": 1,
"most_active_device": "desktop",
"captured_at": "2026-05-02T18:00:00Z"
}]
}' AMPscript inside the Journey email
Pull the MailOdds fields out of the Journey's entry data and personalise the email body. The RedirectTo() wrapper preserves SFMC click tracking on the deep link back to the product page.
hot-lead-email.html
HTML%%[
/* AMPscript inside the Journey email step. Pull the MailOdds context out
of the Journey's Entry Data and personalise the message body. */
VAR @productTitle, @productPrice, @currency, @heatScore, @cta
SET @productTitle = AttributeValue("product_title")
SET @productPrice = AttributeValue("product_price")
SET @currency = AttributeValue("currency")
SET @heatScore = AttributeValue("heat_score")
IF @heatScore >= 0.9 THEN
SET @cta = "Reserve yours - free EU shipping"
ELSE
SET @cta = "Take another look"
ENDIF
]%%
<h1>Still thinking about %%=v(@productTitle)=%%?</h1>
<p>It's still in stock at %%=v(@currency)=%% %%=v(@productPrice)=%%.</p>
<a href="%%=RedirectTo(Concat('https://shop.alpacanica.com/p/', AttributeValue('product_id'), '?utm_source=sfmc&utm_campaign=hot_lead'))=%%">
%%=v(@cta)=%%
</a> Journey Builder configuration
Build this in Marketing Cloud under Journey Builder -> Journeys -> New Journey -> Multi-Step Journey. The checklist below maps each MailOdds field to a Journey activity.
journey-builder-checklist.txt
BASHJourney Builder UI checklist
============================
1. Journeys -> New Journey -> Multi-Step Journey
2. Entry Source: Data Extension
-> Choose Data Extension MAILODDS_HOT_LEADS (created via SOAP or REST,
primary key: request_id, dedup on insert)
-> Evaluate every: 5 minutes
-> Subscriber Source: subscriber_email column, link to Sendable
Subscriber List
3. Entry Filter
-> heat_score >= 0.8
-> cart_value > 0
-> product_id IS NOT NULL
4. Activity 1: Wait 15 minutes
-> Avoids interrupting the live session
5. Activity 2: Email
-> Pick the email containing the AMPscript snippet above
-> Subject line: %%product_title%% is still in your cart
6. Activity 3: Decision Split
-> sms_eligible == true -> SMS path
-> sms_eligible == false -> End
7. Activity 4 (SMS path): Wait 60 minutes, then MobileConnect SMS step
-> Body: "Still thinking about %%product_title%%? Free shipping today."
8. Activity 5: Goal
-> Definition: contact has fired the "checkout_completed" tracking
event from MailOdds. Wire this with a second webhook listener that
inserts into a Goal Data Extension.
9. Activate the Journey. Test inserts: drop a row into MAILODDS_HOT_LEADS
via the REST call shown above; the contact should land in Activity 1
within 5 minutes. Operational notes
- Dedup: set the Data Extension primary key to
request_id. MailOdds includes a uniquerequest_idper webhook delivery so REST inserts are idempotent. - Auth caching: cache the OAuth token in memory or in a small Redis key. SFMC issues a hard quota of 50 token requests per minute per package.
- Journey polling: 5-minute polling is the SFMC default. Journey Builder will not pick up rows faster than that, no matter how often MailOdds fires the webhook.
- SMS via MobileConnect: requires a separate provisioning flow with Salesforce. The Decision Split shown above gracefully ends the Journey if MobileConnect is not provisioned.
- Package types: use a Server-to-Server (S2S) installed package, not a Web App package. The webhook listener never represents a user.