A triggered send is an application telling an ESP which message to send to which person. It worked when the ESP was the only consumer and email was the only channel. Marketing Eventing flips the direction: systems publish facts about what happened, against a contract, and the marketing layer decides what to do with them. This is the companion to Object Oriented Marketing: objects describe what is true, events describe what happened.
1. What a triggered send really is
Every platform has one. In SFMC it is a triggered send definition or the Transactional Messaging API. In Braze it is an API-triggered campaign or Canvas. The shape is the same: the calling system names the message and the recipient.
SFMC: Transactional Messaging API
POST https://{subdomain}.rest.marketingcloudapis.com/messaging/v1/email/messages/{messageKey}
{
"definitionKey": "noshow-reminder-email",
"recipient": {
"contactKey": "u_1042",
"to": "ana@example.com",
"attributes": { "AppointmentId": "apt_5521", "ClinicName": "Downtown" }
}
}
Braze: API-triggered campaign
POST https://rest.iad-0X.braze.com/campaigns/trigger/send
{
"campaign_id": "<noshow_reminder_campaign_id>",
"recipients": [
{ "external_user_id": "u_1042",
"trigger_properties": { "appointment_id": "apt_5521", "clinic_name": "Downtown" } }
]
}
Both calls hardcode a decision (“send the no-show reminder”) and a vendor identifier (definitionKey, campaign_id) inside application code. That is where the trouble starts.
- Every new message is an engineering ticket. Want a push before the email, or an SMS for high-value patients? Someone has to change the app.
- The decision lives in the wrong place. Suppression, frequency caps, quiet hours and channel preference all belong to marketing, but the app already pulled the trigger.
- One consumer. The fact that an appointment was missed is useful to analytics, CX, ads suppression and ops. A send call tells only the ESP, and only as a side effect.
- Vendor lock-in in your codebase. Switching platforms means finding every hardcoded message ID across every service.
- Silent drift. Nobody can list which services trigger which messages without reading the code.
2. Marketing Eventing, defined
Marketing Eventing is the practice of having product and business systems publish customer-relevant facts as versioned events, and having the marketing layer subscribe to those events and decide what to do. The app says what happened. Marketing decides what it means.
Where “the marketing layer” lives matters. Moving the decision out of the app and into Braze or SFMC is better than a triggered send, but it is still suite thinking. In a composable stack the decision belongs in the CDP or orchestration layer, and the ESP is close to a dumb pipe that renders and delivers. The examples below use a Braze Canvas because it is familiar; the same pattern applies, and is stronger, when the router hands events to an orchestration layer first.
| Triggered send (command) | Marketing event (fact) |
|---|---|
send_noshow_email(u_1042) | appointment.no_show.v1 |
| App decides the message | Marketing decides the response, including doing nothing |
| One consumer: the ESP | Many consumers: engagement, warehouse, CX, ads, ops |
| Vendor ID in code | Business vocabulary in code; vendor mapping in config |
| New message = deploy | New message = new Canvas listening to an existing event |
| Undocumented | Listed in an event catalog with owner, schema and version |
3. The event contract
An event is only as useful as its contract. Use a standard envelope (the CloudEvents spec works well) and keep the business payload in data.
{
"specversion": "1.0",
"id": "evt_01JC8Z6Q4M2T7",
"source": "scheduling-service",
"type": "appointment.no_show.v1",
"time": "2026-10-30T15:00:00Z",
"subject": "u_1042",
"datacontenttype": "application/json",
"dataschema": "https://schemas.example.com/appointment/no_show/v1.json",
"data": {
"appointment_id": "apt_5521",
"clinic_id": "downtown",
"scheduled_for": "2026-10-30T14:30:00Z",
"visit_type": "follow_up",
"reschedule_url": "https://example.com/r/apt_5521"
}
}
And the contract that governs it, kept in the repo next to the code that emits it:
event: appointment.no_show
version: 1
owner: scheduling-team
description: Patient did not attend a scheduled appointment within the grace window.
subject: external_id # the customer ID shared by every downstream system
pii: none # no email, phone or clinical notes in the payload
idempotency_key: id
fields:
appointment_id: { type: string, required: true }
clinic_id: { type: string, required: true, lookup: catalog:clinics }
scheduled_for: { type: timestamp, required: true }
visit_type: { type: enum, values: [initial, follow_up, refill] }
reschedule_url: { type: url }
consumers: [braze, warehouse, cx_alerts, ads_suppression]
Rules that keep it clean
- Name facts, not intentions.
domain.entity.action.vN, past tense.appointment.no_show, neversend_reminder. - IDs, not PII. Carry the customer ID and let each consumer resolve contact data from its own consented profile.
- Lookups, not copies. Send
clinic_id, not the clinic's address. The catalog holds the thing. - Version on breaking change. Add fields freely; rename or remove only in a new version, and run both until consumers move.
- Idempotent by default. Every consumer de-duplicates on
id, so retries never double-send. - Event time is truth. Use
time, not arrival time, for windows and delays.
4. Routing: vendor mapping lives in config
A thin router (a CDP, an event pipeline, or a small service) subscribes to the bus and translates events into each destination's API. This is the only place vendor-specific shapes exist.
routes:
appointment.no_show.v1:
braze:
endpoint: /users/track
map:
events[0].external_id: subject
events[0].name: appointment_no_show
events[0].time: time
events[0].properties: data
sfmc:
endpoint: /interaction/v1/events
map:
ContactKey: subject
EventDefinitionKey: APIEvent-appointment-no-show
Data: data
warehouse:
table: events.appointment_no_show
cx_alerts:
when: data.visit_type == "initial"
slack_channel: "#cx-noshows"
What reaches Braze is a plain event on the user object, not a send instruction:
POST https://rest.iad-0X.braze.com/users/track
{
"events": [
{ "external_id": "u_1042", "name": "appointment_no_show",
"time": "2026-10-30T15:00:00Z",
"properties": { "appointment_id": "apt_5521", "clinic_id": "downtown",
"visit_type": "follow_up", "reschedule_url": "https://example.com/r/apt_5521" } }
]
}
A Canvas with an action-based entry on appointment_no_show takes it from there: wait 30 minutes, check whether the patient has already rebooked, respect quiet hours, choose push or SMS based on engagement, look up the clinic in a catalog, and exit on appointment.booked. None of that required a deploy. Swapping Braze for another engine means changing one block of router config.
5. Transactional sends: intent, not mechanism
Not every message should pass through marketing's decision layer. Password resets, one-time codes, receipts and legally required notices are transactional sends. They are still triggered, and they can and should be triggered by events: password.reset_requested and order.placed are facts like any other. What sets them apart is not how they start but their intent: the customer asked for the message or is owed it, so it must go out exactly once, immediately, regardless of marketing preferences.
- Password resets, one-time codes and security alerts.
- Order and payment receipts, shipping updates and legally required notices.
- Anything where the user just clicked a button and is waiting for the message.
Route those events to a transactional path, not a marketing one: no frequency caps, no quiet hours, no holdouts, and ideally a separate sending domain or IP pool so a marketing reputation problem never blocks a password reset. In SFMC that is the Transactional Messaging API; in Braze, a transactional email campaign. The test: if marketing should ever be allowed to delay, suppress or change the channel, it is marketing. If not, it is transactional.
A Venn diagram of terms
Part of the confusion is vocabulary. Marketing versus transactional describes intent. Triggered, batch, real time, near real time and ad-hoc describe something else, and teams mix them freely. “Triggered” can mean an API call or an event. “Real time” gets used for anything faster than tonight's batch. A triggered send in SFMC is a product feature, a trigger in Braze is an action-based entry, and a transactional email can be triggered, real time and event-driven all at once. Every term overlaps another until two people can describe the same program with no words in common.
Three separate questions untangle it. The tags are colored by who tends to say them, from engineering to marketing:
| Question | Answers | Terms people use for it |
|---|---|---|
| Intent: why are we sending? | Marketing, transactional | promotionalcommerciallifecycle servicerelationshipoperational |
| Trigger: what starts it? | A schedule, a person, an event, a command, a model | batchrecurringscheduled ad-hocblastone-off event-drivenaction-basedbehavioral API-triggeredtriggered send AI decisioningnext-best-actionsend-time optimization |
| Latency: how fast after the trigger? | Real time (seconds), near real time (minutes), batch (hours) | real timeinstantstreaming near real timemicro-batch nightlydaily |
Answer all three and the ambiguity goes away: “a transactional, event-triggered, real-time receipt,” or “a marketing, event-triggered, near-real-time no-show recovery with a 30-minute delay.” The matrix below crosses intent with trigger; latency is the badge in each cell.
Only one cell is a problem: marketing by command, where an application names a marketing message in code. That is what this article means by moving beyond triggered sends. Every other combination is legitimate. Transactional by command is fine when the user is waiting on the other end; still publish the underlying event so analytics and lifecycle can use the fact.
6. Next: AI decisioning and the campaignless program
The matrix has a fifth trigger that is quickly becoming the most important one: a model. Vendors call it AI decisioning. Today it is mostly machine learning, usually contextual bandits or reinforcement learning, that the vendor trains and runs for you, with AI in the tooling around it. It is not autonomous yet, but it is close, and it changes the job.
In a decisioning program there is no campaign and no journey to build. You define an objective, the guardrails and a library of actions. The model decides, per person, who gets a message, what it says, when it arrives, on which channel and how often, and it keeps experimenting to learn what works. Skipping someone today is a decision too.
What the job becomes
- Marketers supply variety, not calendars. The model can only test what exists: offers, creative, subject lines, tones and formats, tagged so the model and the analyst can tell them apart.
- Martech keeps the signals flowing. Events and profile features must be complete, defined and fresh in minutes, because they are both the model's inputs and its rewards. This is where Marketing Eventing pays off: a model cannot learn from a send call buried in app code.
- Content has to be fetchable at scale. Variants live in catalogs or a content API that delivery tools read at send time, not pasted into hundreds of templates.
- Guardrails and measurement stay human. Consent, caps, brand and legal rules, offer budgets and a permanent random holdout are set by people and enforced outside the model.
- Transactional stays out. A model can personalize a block inside a receipt, but never whether or when the receipt sends.
Campaigns and journeys will not disappear overnight. Most programs will run both: decisioning for always-on lifecycle, hand-built journeys for launches, regulated flows and anything with a fixed sequence. The foundations are the same either way: events, profiles and content a machine can use.
7. Migrating off triggered sends
- Inventory. Search the codebase for every send call and vendor message ID. List caller, message, channel and owner.
- Find the fact. For each one, name the business event behind it. Several sends usually collapse into one event.
- Write the contract. Schema, owner, PII class, consumers. Put it in the event catalog.
- Publish alongside. Emit the event next to the existing send call. Nothing changes for customers yet.
- Rebuild the response. Build the Canvas or journey on the event, with suppression and caps, or hand the event to a decisioning model as a signal and a reward and let it choose from your content library. Either way, run it in a holdout or shadow mode against the old send.
- Cut over and delete. Switch traffic, remove the send call and the hardcoded ID. The app is now vendor-neutral for that flow.
8. Why it matters now
With Salesforce acquiring Braze, a lot of teams are about to re-evaluate their engagement platform whether they planned to or not. Teams whose applications call campaign_ids directly will face a code migration. Teams that publish marketing events will face a config change.
It is also a good moment to ask what you are paying for. If orchestration lives in a CDP and decisions increasingly come from a model, the engagement platform's job shrinks to rendering and delivery. Braze and SFMC are priced as the brain: MAU tiers, message volume and add-on modules. Paying premium-engine prices for a dumb pipe is hard to justify, and a renewal under a new owner, with bundling and repricing likely, is the natural time to price out alternatives. That can mean delivery APIs (Amazon SES, SendGrid or Postmark for email, Twilio for SMS, Firebase Cloud Messaging for push) or a lighter engagement platform. Count what you would give up, such as deliverability tooling, template management, in-app messaging, SDKs and a preference center, and the engineering time to replace it. If your stack is event-driven, the evaluation is cheap: route one event to a second destination, run both in parallel against a holdout, and compare cost and results before you sign anything.
Objects tell the engine what is true. Events tell it what happened. Neither should tell it what to send. That decision belongs to marketing, ideally in a CDP or orchestration layer that sits above any single ESP, where it can be tested, capped, measured and changed without a deploy or a vendor migration. Increasingly that decision will be made by a model, and the teams ready for it will be the ones whose events, profiles and content a machine can already use.
Related: Object Oriented Marketing and Let Data Run the Lifecycle.