Skip to content

Salesforce Marketing Cloud

Fromenance is a communication provenance platform, and the Salesforce Marketing Cloud adapter turns Event Notification Service (ENS) EmailSent and EmailDelivered events into registrations. Marketing Cloud does not expose the rendered body, so the adapter uses the reserve flow: a pre send call reserves a code, AMPscript places it in the footer, and the event carries it back.

By the end you will have an SFMC source, an ENS callback and subscription pointed at it, the reserve call wired into the send, and events showing on the source’s health panel. This is the longest adapter setup; plan for a Marketing Cloud admin and an email developer.

Terminal window
curl -X POST https://api.fromenance.com/v1/sending-sources \
-H "Authorization: Bearer fr_live_..." \
-H "Content-Type: application/json" \
-d '{"name":"Marketing Cloud journeys","kind":"sfmc","expected_daily_volume":250000,"silence_alert_minutes":360}'

The response includes webhook_url and a secret. Use the secret as the ENS callback signature key in step 2; the callback signs with the key you configure, so the two match by construction.

2. Create the ENS callback and subscription

Section titled “2. Create the ENS callback and subscription”

ENS is configured through the Marketing Cloud REST API (an installed package with the Event Notification Callbacks and Subscriptions scopes).

  1. Create the callback with the source’s webhook_url and the source secret as signatureKey:
POST https://{subdomain}.rest.marketingcloudapis.com/platform/v1/ens-callbacks
Authorization: Bearer <SFMC access token>
Content-Type: application/json
[{ "callbackName": "fromenance", "url": "https://api.fromenance.com/v1/hooks/esp/src_...", "signatureKey": "whsec_..." }]
  1. Marketing Cloud sends a verification request to the URL containing a verificationKey. Fromenance answers 200 to that request. Complete verification by posting the key back:
POST https://{subdomain}.rest.marketingcloudapis.com/platform/v1/ens-verify
{ "callbackId": "<callbackId>", "verificationKey": "<key from the verification request>" }
  1. Subscribe the callback to send events for the sends you register:
POST https://{subdomain}.rest.marketingcloudapis.com/platform/v1/ens-subscriptions
[{
"callbackId": "<callbackId>",
"subscriptionName": "fromenance-sends",
"eventCategoryTypes": ["TransactionalSendEvents.EmailSent", "TransactionalSendEvents.EmailDelivered", "EngagementEvents.EmailSent"],
"filters": [],
"status": "Active"
}]

ENS posts a JSON array of events with the header x-sfmc-ens-signature, a base64 HMAC-SHA256 of the raw body keyed with the signature key. Fromenance recomputes it with the source secret and compares in constant time; a mismatch returns 401 and increments signature_failures. Events whose eventCategoryType does not contain EmailSent or EmailDelivered are ignored.

From each event:

Registration field ENS field
message_id composite.messageKey, else composite.sendId, else eid
recipient (hashed immediately, never stored) info.emailAddress, else info.to, else composite.emailAddress
sent_at info.sendTime, else timestampUTC
verify code info.fromenance_code
template_id info.templateId, else info.emailName, else composite.jobId
campaign_id composite.jobId
content fingerprint not available from Marketing Cloud

The code reaches the event through info, which carries the attributes of the send. Populate a data extension field named fromenance_code on the sendable data extension or the transactional send definition’s attributes, so it is present on the event.

3. Reserve before render and place the code with AMPscript

Section titled “3. Reserve before render and place the code with AMPscript”

For journey and triggered sends, reserve the code when the contact enters the send step and write it to the data extension, then output it in the email:

%%[
/* fromenance_code was written to the sendable DE by the reserve call before this send */
SET @fromenance_code = AttributeValue("fromenance_code")
]%%
Not sure this email is from Northfield Bank? Forward it to verify@northfieldbank.example
or enter code %%=v(@fromenance_code)=%% at northfieldbank.example/verify. Reference: %%=v(@fromenance_code)=%%

For transactional messaging API sends, reserve in your application and pass the code as an attribute:

const reserved = await fr.reserve({ to: customer.email, template_id: "alert-v1", provider: "sfmc" });
await fetch(`https://${subdomain}.rest.marketingcloudapis.com/messaging/v1/email/messages/${messageKey}`, {
method: "POST",
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
body: JSON.stringify({
definitionKey: "alert-v1",
recipient: { contactKey: customer.id, to: customer.email, attributes: { fromenance_code: reserved.verify_code } },
}),
});

Where reservation must happen inside Marketing Cloud (batch journeys with no application in the loop), a Script Activity in Automation Studio can call POST /v1/communications/reserve per row with SSJS HTTP.Post and write the code into the data extension before the send. Because the recipient address is known at that point, hash it with your tenant secret inside the script (Platform.Function.SHA256 does not do HMAC; use the recipient_hash free reservation and let the send event bind the recipient).

  • Send test event on the source runs a synthetic EmailSent event through the adapter.
  • Trigger one send. health.completed increments once the EmailSent event arrives, typically within a minute.

Set silence_alert_minutes to match your send cadence; a journey that sends nightly should not alert every hour. signature_failures after rotating the source secret means the callback still has the old signatureKey; update it with PUT /platform/v1/ens-callbacks/{id}. coverage_gaps counts reservations that expired without an event, which on Marketing Cloud usually means a subscription filter excluded the send.