Advertisement

Integration

Salesforce Platform Events Deep Dive — Publish, Subscribe and Replay (2026)

Published 12 June 2026 · 13 min read · Advanced

Platform Events are Salesforce’s answer to the question “how do two systems talk without knowing about each other.” They underpin event-driven architecture on the platform — decoupling producers from consumers across Apex, Flow, and external systems through a single asynchronous bus. This deep dive covers how the bus actually behaves: publish semantics, every subscriber type, replay and durability, retry, and the delivery allocations that surprise teams at scale. Current to Summer ‘26 and API v67.0.

Platform Events sit alongside the other patterns in the Salesforce integration patterns guide — reach for them when you need fan-out and decoupling, not when you need a synchronous answer.

The event bus model

A Platform Event is a special sObject (suffix __e) representing a message. Publishers place events on the bus; subscribers read them asynchronously. Neither side holds a reference to the other — you can add a third subscriber years later without touching the publisher. That decoupling is the entire value proposition, and also the source of every “but where did my event go” debugging session.

Two volume tiers exist: high-volume events (the default and correct choice) are optimized for throughput and stored efficiently; standard-volume is legacy. Define events in Setup with a clear field schema — the payload is the contract every subscriber depends on, so version it as carefully as any API.

Publishing — and the after-commit decision

You publish with EventBus.publish:

List<Order_Event__e> events = new List<Order_Event__e>();
events.add(new Order_Event__e(
    Order_Id__c = order.Id,
    Status__c   = 'Shipped',
    Amount__c   = order.TotalAmount
));
List<Database.SaveResult> results = EventBus.publish(events);

The pivotal design choice is the publish behavior, set on the event definition:

  • Publish After Commit — the event fires only if the surrounding transaction commits. If the transaction rolls back, the event never reaches the bus. This is the safe default whenever the event asserts something about persisted data (“Order shipped” should not fire if the order save failed).
  • Publish Immediately — the event fires the moment publish runs, independent of the transaction outcome. Correct for audit logs and telemetry that must survive even a failed transaction — you want the “attempt happened” signal regardless.

Choosing After Commit for telemetry loses the events you most wanted on failure; choosing Immediately for a “record created” event leaks events for records that never saved. The wrong default here is a class of bug that only shows up under rollback conditions, which is to say, in production.

Always inspect the SaveResult — publishing can fail, and an unchecked failure is a silently dropped message.

Subscribers — three ways to consume

Apex trigger (after-insert on the event). The in-platform workhorse:

trigger OrderEventTrigger on Order_Event__e (after insert) {
    for (Order_Event__e evt : Trigger.new) {
        // Runs async as Automated Process user.
        // Batch of up to 2,000 events per execution.
    }
}

Two things bite here. First, it runs as the Automated Process user, not the publishing user — so running user context, ownership, and the “Set Created By and Last Modified By” org setting all matter for anything the trigger writes. Second, batches reach 2,000 events, so the same bulkification discipline as any trigger applies, multiplied.

Flow (platform-event-triggered flow). A declarative subscriber, ideal when the reaction is record updates or notifications without code. It processes one event per run and shares the same async, Automated-Process execution model.

Pub/Sub API (external subscribers). The modern external transport: gRPC over HTTP/2 with binary Avro payloads, superseding the older CometD/empApi streaming. External systems subscribe, receive events in near-real-time, and track their position by ReplayId. More efficient and more robust than CometD for anything outside the platform.

Replay and the 72-hour wall

Every event gets a ReplayId marking its position on the bus. External subscribers store the last ReplayId they processed; on reconnect they resume from there, recovering anything missed during a disconnect. The hard constraint: events are retained for 72 hours. Replay works only inside that window — a subscriber down for a long weekend can miss events permanently. Design external consumers to persist their ReplayId durably and to alert when the gap between now and their last ReplayId approaches the retention edge.

This is also why Platform Events are not a database: they are a transient transport. If a consumer needs the full history, it must persist what it consumes; the bus will not hold it.

Retry — bounded, and you own the final attempt

When an Apex subscriber hits a transient failure, signal a retry:

trigger OrderEventTrigger on Order_Event__e (after insert) {
    try {
        processEvents(Trigger.new);
    } catch (CalloutException e) {
        if (EventBus.TriggerContext.currentContext().retries < 4) {
            throw new EventBus.RetryableException('Transient — retry');
        } else {
            logFailure(Trigger.new, e);  // final attempt: persist, don't lose
        }
    }
}

EventBus.RetryableException asks the platform to redeliver the batch. Retries are capped — by a retry count and by roughly a 10-minute elapsed ceiling. After that the platform gives up, so the trigger must check EventBus.TriggerContext...retries and, on the last attempt, fall back to an error log rather than throwing into the void. A retry strategy without a terminal branch is just delayed data loss.

Delivery allocations — the limits that bite at scale

Platform Events draw on daily allocations separate from API limits, and they are easy to blow through under fan-out:

  • Published events per 24 hours — bounded by your org’s allocation and add-on licenses
  • Delivered events to CometD/empApi and Pub/Sub subscribers, counted per subscriber — three subscribers to one event is three deliveries against the allocation
  • Apex-trigger and flow delivery do not count against the same external delivery allocation, but do consume async processing capacity

The fan-out arithmetic is the trap: one published event delivered to four external subscribers is four counts. Architectures that look fine at 1,000 events/day quietly fail at 100,000 because the delivery multiplier was never modeled. Estimate published × subscriber-count before committing to an event-driven design, not after the allocation error fires.

When Platform Events are the wrong tool

  • You need a synchronous answer — events are fire-and-forget; use a REST callout.
  • You need guaranteed long-term durability — 72 hours is a transport window, not storage. Persist on consumption.
  • Strict ordering across partitions matters — ordering holds within a publish but is not a cross-stream guarantee; design idempotent consumers instead of assuming order.
  • Tiny, tightly-coupled, same-transaction logic — if a trigger can just call a handler synchronously, an event adds latency and an allocation cost for no decoupling benefit.

Used where decoupling and fan-out genuinely pay off — and modeled honestly for the 72-hour window and the per-subscriber delivery math — Platform Events are the backbone of event-driven Salesforce. Used as a generic message queue or a database, they become an expensive way to lose data 72 hours at a time.

Test your knowledge — Integration

10 questions · Basic to Advanced

0 / 10 correct

Advertisement

Frequently asked questions

What is a Platform Event in Salesforce?

A Platform Event is a special sObject that represents a message on Salesforce's event bus. Publishers put events on the bus and subscribers consume them asynchronously, decoupling the two so that producers and consumers never call each other directly.

What is the difference between Publish After Commit and Publish Immediately?

Publish After Commit fires the event only if the surrounding transaction commits successfully — the safe default for events that must reflect saved data. Publish Immediately fires the moment EventBus.publish runs, even if the transaction later rolls back, which suits logging and telemetry that should survive a failure.

How do I subscribe to a Platform Event in Apex?

Create an after-insert Apex trigger on the event sObject. The trigger runs asynchronously as the Automated Process user and receives events in batches of up to 2,000, with Trigger.new holding the event payloads and their ReplayIds.

What is a ReplayId in Platform Events?

A ReplayId is the position of an event in the bus. Subscribers using the Pub/Sub API can resume from a stored ReplayId to recover missed events, but only within the retention window — Platform Events are retained for 72 hours.

How long are Platform Events stored in Salesforce?

Standard-volume and high-volume Platform Events are retained on the event bus for 72 hours. After that, the events and their ReplayIds are no longer available for replay.

How do I retry a failed Platform Event trigger in Apex?

Throw EventBus.RetryableException to ask the platform to redeliver the batch. Retries are capped — after the limit (or 10 minutes of elapsed retries) the platform stops, so the trigger must check EventBus.TriggerContext.retries and fall back to an error log on the final attempt.

Advertisement