Salesforce Marketing Cloud
Datafly Signal delivers events to Salesforce Marketing Cloud Engagement server-to-server, firing entry events into Journey Builder via the Interaction API. This lets you trigger email and SMS journeys directly from behavioural and conversion signals without relying on a separate batch sync or client-side beacon.
This integration is for Marketing Cloud Engagement (formerly ExactTarget). It is distinct from Salesforce Data Cloud / CDP, which uses a different API and is configured separately in Signal.
Prerequisites
Before configuring Marketing Cloud in Signal, you need an Installed Package with API credentials and a configured event in Journey Builder.
Step 1: Identify your tenant subdomain
Marketing Cloud assigns every tenant a unique subdomain prefix that appears in every API URL. For example, if your REST URL is https://mc-abc123xyz.rest.marketingcloudapis.com, your subdomain is mc-abc123xyz.
You can find your subdomain in Setup > Apps > Installed Packages — click any package and look at the REST Base URI field.
Step 2: Create an Installed Package
- In Marketing Cloud, go to Setup > Apps > Installed Packages.
- Click New and give your package a name (e.g.
Datafly Signal). - After creation, click Add Component > API Integration.
- Choose Server-to-Server as the integration type.
- Grant the following scopes:
- Journeys: Read, Write, Execute
- Contacts: Read, Write
- Data Extensions: Read, Write (only required if you intend to write to data extensions in addition to triggering journeys)
- Save the component. Marketing Cloud will generate a Client ID and Client Secret — copy both immediately.
Step 3: Identify your Account ID (MID)
If your Marketing Cloud tenant has multiple business units, you’ll need the numeric MID of the business unit you want events delivered to. Find it under Setup > Business Units — the MID column shows a numeric value like 7281234.
Tenants with a single business unit can omit this field.
Step 4: Create an event in Journey Builder
Signal triggers journeys by firing an Engagement Event by key. Create one in Journey Builder:
- Go to Journey Builder > Events.
- Click Create Event > API Event.
- Give it a name and an Event Definition Key — this is the string Signal will reference, e.g.
datafly_lead_submitted. - Define the schema (the fields the journey expects in the
Datapayload — email, first_name, etc.). - Save the event. You’ll bind it to one or more journeys later.
The Event Definition Key is case-sensitive and must match exactly what Signal sends. Pick a stable naming convention (snake_case is recommended) and use it consistently across events.
Configure in Signal
Configuration fields
| Field | Required | Description |
|---|---|---|
tenant_subdomain | Yes | Tenant-specific subdomain prefix (e.g. mc-abc123xyz). |
client_id | Yes | Installed Package client ID. |
client_secret | Yes | Installed Package client secret. |
account_id | No | Business unit MID. Required only for multi-business-unit tenants. |
event_definition_key | Yes | The Event Definition Key from Journey Builder that this integration should trigger. |
Management UI setup
- Go to Integrations > Add Integration > Salesforce Marketing Cloud.
- Select the Retail preset (or another vertical preset if available).
- Enter your
tenant_subdomain,client_id,client_secret, andevent_definition_key. Addaccount_idif you’re on a multi-business-unit tenant. - Set consent categories — defaults to
marketing. - Click Save.
Management API setup
curl -X POST http://localhost:8084/v1/admin/integrations \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_id": "src_abc123",
"vendor": "salesforce_marketing_cloud",
"name": "Marketing Cloud — Welcome Journey",
"enabled": true,
"config": {
"tenant_subdomain": "mc-abc123xyz",
"client_id": "abc123clientid",
"client_secret": "shh-secret-value",
"event_definition_key": "datafly_lead_submitted"
},
"consent_categories": ["marketing"]
}'API flow
Signal performs a two-stage call per integration:
Stage 1: Auth
POST https://{tenant_subdomain}.auth.marketingcloudapis.com/v2/token
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "{client_id}",
"client_secret": "{client_secret}",
"account_id": "{account_id}"
}Returns a short-lived access_token (typically 20 minutes). Signal caches the token and refreshes it automatically before expiry.
Stage 2: Send event
POST https://{tenant_subdomain}.rest.marketingcloudapis.com/interaction/v1/events
Authorization: Bearer {access_token}
Content-Type: application/json
{
"ContactKey": "jane.doe@example.com",
"EventDefinitionKey": "datafly_lead_submitted",
"Data": {
"email": "jane.doe@example.com",
"first_name": "Jane",
"last_name": "Doe",
"phone": "+447700900123",
"lead_type": "newsletter",
"lead_value": 0
}
}ContactKey is the stable identifier Marketing Cloud uses to find or create a Contact. By default Signal uses the user’s email; if your tenant uses a different ContactKey scheme (e.g. CRM ID), remap ContactKey in the integration’s Field Mappings.
Identity
| Field | Source | Notes |
|---|---|---|
ContactKey | properties.email (default) | Stable Marketing Cloud Contact identifier. Override via Field Mappings to use a different key. |
Data.email | properties.email | Sent into the journey payload so journey activities can reference it. |
Data.first_name | properties.first_name | Optional. |
Data.last_name | properties.last_name | Optional. |
Data.phone | properties.phone | E.164 format recommended for SMS journeys. |
Data.user_id | user_id | Stable user identifier for downstream joins. |
Marketing Cloud accepts ContactKey values as raw strings — hashing is not required and Marketing Cloud will not match a hashed ContactKey against an existing Contact created with a raw email.
Event mapping
Retail preset
| Signal event | Marketing Cloud Event Definition Key |
|---|---|
Lead Submitted | lead_submitted (configurable) |
Identified | identified (configurable) |
Order Completed | order_completed (configurable) |
The EventDefinitionKey sent to Marketing Cloud comes from the integration config (event_definition_key). To trigger different journeys from different Signal events, create one Marketing Cloud integration per journey, each pointing at a different Event Definition Key.
Example: Lead Submitted
Datafly.js call:
datafly.identify("user-123", {
email: "jane.doe@example.com",
firstName: "Jane",
lastName: "Doe",
phone: "+447700900123"
});
datafly.track("Lead Submitted", {
lead_type: "newsletter",
value: 0,
currency: "GBP"
});Marketing Cloud payload sent by Signal:
{
"ContactKey": "jane.doe@example.com",
"EventDefinitionKey": "datafly_lead_submitted",
"Data": {
"email": "jane.doe@example.com",
"first_name": "Jane",
"last_name": "Doe",
"phone": "+447700900123",
"lead_type": "newsletter",
"lead_value": 0,
"currency": "GBP",
"user_id": "user-123"
}
}Order Completed: events vs data extensions
For order data you have two delivery patterns:
- Trigger a journey (default) — use this when an order should kick off a post-purchase email, abandoned-cart recovery, or upsell journey. Configure an Event Definition Key for orders and route
Order Completedthrough it. - Insert into a Data Extension — use this when you want order rows persisted in Marketing Cloud for reporting or audience building without journey entry. The Data Extension Async API (
/data/v1/async/dataextensions/key:{key}/rows) is preferred for this; we plan to ship a separatesalesforce_marketing_cloud_data_extensionsspec to cover that flow declaratively.
Until the data-extension spec lands, use the journey-trigger flow and pair it with a “no-op” journey that simply records the contact + payload to a Sendable Data Extension.
Testing
- In Marketing Cloud, open Journey Builder > Events and click the event you bound to Signal.
- The right-hand panel shows recent fires — Signal events appear within a few seconds of delivery.
- To trace a specific event, copy the
event_idfrom the Signal event log and search for it in Marketing Cloud’s Tracking view. - For end-to-end validation, build a simple test journey with a single “Wait 1 minute > Send Email” path and bind it to your Event Definition Key. Trigger
datafly.track(...)and confirm the email arrives.
Rate limits
Marketing Cloud applies tenant-level rate limits to the Interaction API — typically 2,000 events per minute per business unit on standard editions, higher on Enterprise. Signal does not pre-throttle by default; if you expect bursts above this, contact your account team to enable per-integration rate limiting on the integration.
Troubleshooting
| Problem | Solution |
|---|---|
401 Unauthorized on auth | Confirm client_id/client_secret match the Installed Package. Confirm the package’s API component is Server-to-Server, not Web App. |
401 after a successful auth | The access token expired — Signal refreshes automatically, but if you’ve revoked the package’s permissions, the next refresh will fail. Re-grant scopes. |
404 on the events endpoint | Wrong tenant_subdomain. Confirm the REST Base URI in Setup > Apps > Installed Packages. |
| Journey is not triggered | Confirm the event_definition_key in Signal exactly matches the key in Journey Builder (case-sensitive). Confirm the event is bound to an active journey. |
| Contact is not created | Ensure the Installed Package has the Contacts: Write scope. Confirm ContactKey is populated — it cannot be null. |
account_id mismatch errors | On multi-business-unit tenants the package must have access to the target MID. Re-create the package inside the target business unit or grant cross-BU access. |