Briefly: Change Data Capture is for “tell me when a record changed”, and you get no say in the payload. Platform Events are for “tell me when this business thing happened”, and you define the payload. Outbound messages are the declarative option from an earlier era, still working, still SOAP. Pick CDC when the other system wants your data, Platform Events when it wants your meaning, and do not build either until you know what happens when nobody is listening for three days.
They are not three versions of the same thing
All three push something out of Salesforce when something changes, which is why they land in the same design conversation. What separates them is who decides the message.
Change Data Capture publishes a change event whenever a record is created, updated, deleted or undeleted. The message is a header plus record fields, and the header says what kind of change it was and where it came from. You choose which objects are enabled. You do not choose the shape.
Platform Events are a message you design. You define the fields, you decide when to publish, and you publish from Flow, Apex or an API call. Nothing is automatic, which is the point — the event says “Order Fulfilled”, not “these 14 fields changed on this row”.
Outbound messages are configuration: a SOAP message sent to an endpoint you nominate when a rule fires. No code, and no subscriber to build on the Salesforce side. They retry on failure, which is more than a bare callout gives you — check the current documented retry window in Help rather than trusting anyone’s memory of it, including mine.
The choice usually comes down to one question

Does the receiving system want to mirror your data, or react to your business?
A data warehouse, a search index, a reporting database, a downstream copy of Accounts — those want to mirror. CDC is built for exactly that, and choosing Platform Events instead means writing publish logic for every object and every field you later add, forever.
An ERP that needs to know an order is ready to ship wants to react. It does not care that Description__c changed. Publishing a Platform Event at the moment the business decides the order is ready gives the receiver one clear signal, and gives you a payload that does not break when someone renames a field.
The failure I see most: CDC wired into a system that only cares about one transition, and a subscriber full of filtering logic trying to work out whether this particular update was the one that mattered. That logic belongs in Salesforce, and its output is a Platform Event.
The 72-hour window is the real design constraint
Both CDC and Platform Events are high-volume events on the same bus, and Salesforce stores them for 72 hours. Not longer. Each event carries a replay ID, and a subscriber that stores the last replay ID it processed can reconnect and pick up where it stopped. As long as it comes back inside the window.
Three days sounds generous until you map it against real outages. A subscriber that dies on Friday evening and gets attention on Monday morning is at roughly 60 hours. A long weekend puts you past it. When you go past it, the events are gone, and nothing tells the receiving system that its picture is now wrong.
So every event-based integration needs a second mechanism, and it needs designing on day one rather than after the first gap:
- Monitor the subscriber, not just the publisher. The Salesforce side will look perfectly healthy while nobody is listening.
- Keep a reconciliation path. A scheduled query on
SystemModstampthat catches anything the stream missed is unglamorous and has saved more integrations than any amount of event design. - Decide what “resync” means before you need it. If the answer is “reload everything”, know how long that takes while you are calm, not while the warehouse is three days stale.
The limits worth knowing before you design the payload

From the platform event and Pub/Sub documentation:
- 1 MB maximum per event message. A batch may hold several, and Salesforce recommends keeping a batch under 3 MB, comfortably below the 4 MB gRPC ceiling.
- 2,000 concurrent subscribers across the org. High for most estates, and low if every integration you own opens its own connection per environment.
- Event delivery is allocated per 24 hours, and the allocation depends on your licences. This is the one that fails quietly: publishing keeps working, and deliveries stop being counted in your favour.
Two consequences for payload design. Keep events small and specific: an event carrying an Id and a status is cheap, resilient and easy to version. And do not publish one event per field change if a single event per business transition will do, because the allocation counts messages, not usefulness.
One more piece of housekeeping: standard-volume platform events have been retired. If you inherit an org with them, that migration is already on your list whether or not anyone has written it down.
What I would choose, and when I would not use events at all
For a warehouse or a downstream mirror: CDC, with a scheduled reconciliation query, and monitoring on the subscriber.
For “system B must do something when business event X happens”: a Platform Event published at the decision point, with a payload of Ids and the few fields the receiver genuinely needs. Not the whole record. The whole record is a contract you did not mean to sign.
For a small, declarative, low-volume notification where nobody wants to own a subscriber: an outbound message is still a reasonable answer, and dismissing it because it is old costs you a build you did not need.
And the honest case for none of the above: if the receiving system can only accept a nightly file, events buy you nothing except a queue that waits for the file anyway. Batch synchronisation is not a lesser pattern. It is the right pattern when the other end is batch-shaped.
Frequently asked
Can I subscribe to CDC inside Salesforce? Yes, with an Apex trigger on the change event or a flow, and it is a useful way to react to changes made by an integration. Watch for loops: your reaction is a change, and a change publishes an event.
Does CDC fire for every field? The event tells you which fields changed, so a subscriber can ignore updates it does not care about. It still costs you a delivery to find that out, which is the argument for a Platform Event when only one transition matters.
Do events respect sharing? Subscribers see event messages, not records, so record-level access does not filter the stream the way it filters a query. Design payloads on the assumption that anyone subscribed can read every field you include.
Are events guaranteed to arrive once? Design for at-least-once and you will never be surprised. Carry a key in the payload and make the receiver’s handling idempotent. The same discipline any asynchronous integration needs.
