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.
1. Create the source
Section titled “1. Create the source”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).
- Create the callback with the source’s
webhook_urland the source secret assignatureKey:
POST https://{subdomain}.rest.marketingcloudapis.com/platform/v1/ens-callbacksAuthorization: Bearer <SFMC access token>Content-Type: application/json
[{ "callbackName": "fromenance", "url": "https://api.fromenance.com/v1/hooks/esp/src_...", "signatureKey": "whsec_..." }]- Marketing Cloud sends a verification request to the URL containing a
verificationKey. Fromenance answers200to 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>" }- 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"}]What ENS sends and how it is verified
Section titled “What ENS sends and how it is verified”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.exampleor 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).
4. Confirm
Section titled “4. Confirm”- Send test event on the source runs a synthetic EmailSent event through the adapter.
- Trigger one send.
health.completedincrements once the EmailSent event arrives, typically within a minute.
Source health
Section titled “Source health”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.