Introduction
Salesforce's modern event bus gives you two very different ways to tell the outside world that something happened: Platform Events and Change Data Capture (CDC). Both are durable for replay within the retention window, both can be consumed through Pub/Sub API, and both can drive Apex triggers, Flow, and external integrations. But they are not interchangeable.
The cleanest way to choose is to ask one question first: are you publishing a business fact, or are you exposing a record mutation?
Order_Submitted__e. Use CDC when subscribers need to know that Salesforce data itself changed, such as an Account, Opportunity, or supported custom object being created, updated, deleted, or undeleted.Another important framing from Salesforce documentation: Pub/Sub API is not a third event type. It is a transport and subscription interface that works with both Platform Events and CDC.
What each event type means
Platform Events
- You decide when the event is emitted.
- You own the event contract and field design.
- Best for business milestones, orchestration, and decoupled domain messaging.
- Typical channel shape is
/event/EventName__e.
Change Data Capture
- Salesforce emits the event automatically when tracked records change.
- The payload shape follows the underlying object plus
ChangeEventHeader. - Best for sync, replication, audit-style consumers, and downstream data pipelines.
- Typical channel shape is
/data/AccountChangeEventor/data/MyObject__ChangeEvent.
That difference in publisher intent is the whole architecture story. Platform Events express meaning at the business layer. CDC expresses meaning at the data-change layer.
Side-by-side comparison
| Area | Platform Events | Change Data Capture |
|---|---|---|
| Who emits the message | Your app, Flow, Apex, or an API client chooses when to publish. | Salesforce publishes automatically when a tracked record changes. |
| What the payload means | A business contract such as order submitted, invoice approved, shipment delayed. | A change notification for a Salesforce record operation. |
| Schema ownership | You define custom event fields and keep the contract stable for subscribers. | Salesforce defines the event shape from the source object and the change header. |
| Can you publish it manually | Yes. Apex, Flow, data APIs, and Pub/Sub API can publish supported platform events. | No. You enable CDC for supported objects and Salesforce produces the events. |
| Best-fit consumer | Apps that care about business outcomes and process steps. | Apps that care about record deltas, synchronization, or data movement. |
| Typical topic name | /event/Order_Submitted__e | /data/AccountChangeEvent |
| Retention and replay | Stored on the event bus for 72 hours and replayable within that window. | Stored on the event bus for 72 hours and replayable within that window. |
| Good fit | You want a stable, intentional message that survives source-object refactors. | You want downstream systems to track what changed on Salesforce records. |
| Poor fit | You are trying to mirror every row-level mutation for replication. | You need a curated business event rather than every qualifying CRUD mutation. |
A lot of confusion comes from treating CDC as if it were a business API. It is not. CDC tells subscribers that Salesforce data changed. If subscribers should react to a meaningful business step, define that step explicitly as a Platform Event.
Code examples
These examples are intentionally simple and map to the documented event model. The Platform Event examples use a custom event contract. The CDC example shows how change subscribers work with ChangeEventHeader.
Platform Event publisher in Apex
Use Platform Events when you want to publish a business fact that is independent from the internal shape of the source record.
public with sharing class OrderEventPublisher {
public static void publishSubmitted(Order orderRecord) {
Order_Submitted__e eventRecord = new Order_Submitted__e(
Order_Id__c = String.valueOf(orderRecord.Id),
Account_Id__c = String.valueOf(orderRecord.AccountId),
Order_Number__c = orderRecord.OrderNumber,
Status__c = 'Submitted',
Submitted_At__c = System.now()
);
Database.SaveResult publishResult = EventBus.publish(eventRecord);
if (!publishResult.isSuccess()) {
for (Database.Error err : publishResult.getErrors()) {
System.debug(LoggingLevel.ERROR,
err.getStatusCode() + ': ' + err.getMessage());
}
}
}
}
The event schema is yours. That is why Platform Events are a better contract when subscribers should not care whether the source process touched one object, three objects, or a dozen fields behind the scenes.
Platform Event subscriber with an Apex trigger
Platform Event triggers are always after insert because the event has already been published onto the bus.
trigger OrderSubmittedSubscriber on Order_Submitted__e (after insert) {
List<Task> followUps = new List<Task>();
for (Order_Submitted__e eventRecord : Trigger.New) {
followUps.add(new Task(
Subject = 'Process submitted order ' + eventRecord.Order_Number__c,
Status = 'Not Started',
Priority = 'Normal',
Description =
'Order Id: ' + eventRecord.Order_Id__c +
'\nAccount Id: ' + eventRecord.Account_Id__c +
'\nSubmitted at: ' + String.valueOf(eventRecord.Submitted_At__c)
));
}
if (!followUps.isEmpty()) {
insert followUps;
}
}
CDC subscriber with an Apex trigger
CDC is different. You do not publish the change event yourself. You enable the object for CDC in Setup, and Salesforce inserts change events when the tracked record changes.
trigger AccountChangeSubscriber on AccountChangeEvent (after insert) {
for (AccountChangeEvent changeEvent : Trigger.New) {
EventBus.ChangeEventHeader header = changeEvent.ChangeEventHeader;
if (header.changeType == 'UPDATE') {
System.debug(LoggingLevel.INFO,
'Record ids: ' + header.recordIds +
', transaction key: ' + header.transactionKey +
', changed fields payload: ' + header.changedFields +
', current Name value: ' + changeEvent.Name);
}
}
}
Notice the contract difference. In CDC, the subscriber reasons about changeType, recordIds, and the source-object fields carried on the event. That is great for data synchronization and change processing, but it is a different abstraction than a business-domain event.
External topic naming with Pub/Sub API
Salesforce's Pub/Sub quick starts document the topic naming rules clearly, which is helpful when comparing the two models from an integration-client perspective.
Custom Platform Event: /event/Order_Submitted__e
Standard-object CDC: /data/AccountChangeEvent
Custom-object CDC: /data/Shipment__ChangeEvent
All tracked CDC: /data/ChangeEvents
Delivery, replay, and payload details
Salesforce documents the event bus as a time-ordered event log. Platform Events and CDC events are stored on that bus for 72 hours. A subscriber can persist the last processed replay position and ask for missed events again, as long as that replay point is still inside the retention window.
- Store replay positions durably: never keep them only in memory if you care about reconnect recovery.
- Separate event meaning from transport: Platform Events vs CDC is an architecture decision; Pub/Sub API vs Streaming API is a transport decision.
- Decode CDC deltas correctly: Salesforce documents
changedFields,diffFields, andnulledFieldson CDC headers. - Know the Pub/Sub payload shape: Pub/Sub API CDC messages include unchanged fields with empty values, while
changedFieldstells you what actually changed.
That last point is easy to miss and matters a lot. If you inspect a Pub/Sub CDC message and assume every field shown in the payload changed, your subscriber logic will be wrong. The documented delta indicators in ChangeEventHeader are the reliable guide.
Decision framework
| If your scenario is... | Start with... | Why |
|---|---|---|
| "Notify downstream systems that an order was submitted." | Platform Events | You want a business milestone with a publisher-owned contract. |
| "Keep a warehouse or MDM system in sync with Salesforce record changes." | CDC | You want automatic notifications tied to actual CRUD mutations. |
| "Expose only selected record changes, not every update." | Platform Events | CDC publishes tracked changes automatically; Platform Events let you filter meaning before publication. |
| "Let subscribers understand exactly which fields changed on a record." | CDC | The change-event model and header semantics are built for that. |
| "Keep external contracts stable even if the internal data model changes." | Platform Events | The event schema is decoupled from the underlying object design. |
In mature orgs, the answer is often both. CDC can feed operational sync and analytics, while Platform Events express curated business milestones such as Customer_Tier_Changed__e or Invoice_Approved__e.
Common mistakes
- Using CDC as a business-domain API: that couples subscribers to your object model and CRUD behavior instead of a stable process contract.
- Using Platform Events as a raw replication stream: that usually recreates CDC badly and forces you to hand-maintain an object-shaped contract.
- Ignoring replay storage: both event types are replayable only if your subscriber persists replay IDs and reconnect logic correctly.
- Forgetting CDC enablement: no object selection in Setup means no CDC events, even if the subscriber code is ready.
- Assuming one event per trigger execution: Salesforce event triggers still need bulk-safe logic, and the documented Apex trigger batch size for Platform Events and CDC is larger than standard object-trigger batches.
- Misreading CDC payloads in Pub/Sub API: use
changedFields,nulledFields, anddiffFieldsinstead of assuming every field shown changed.
Recommendation
Use Platform Events when you want Salesforce or an external publisher to announce a business event intentionally, with a clean contract that can survive internal data-model changes. Use CDC when you want subscribers to react to Salesforce record mutations themselves.
If you are still unsure, choose based on what the subscriber is supposed to understand. If the subscriber should understand business intent, publish a Platform Event. If the subscriber should understand which Salesforce record changed and how, use CDC. Then consume either one through Pub/Sub API when you want a single external interface and replay-aware subscriber design.
References
- Get Started with Pub/Sub API
- Pub/Sub API and the Expanded Event Bus
- Event Types
- Event Message Durability
- Step 3: Set Up Events
- Step 4: Write Code That Subscribes to an Event Channel
- Step 5: Subscribe to Change Events
- Event Deserialization Considerations
- Create and Configure the Custom Platform Event
- Apex Governor Limits
