Skip to content

Replay, Retention, Order

Module 4 of 8 in the chapter Events, chapter 11 of the Integration Architect course. Each module stands on its own. Previous: Platform Events and Change Data Capture · Next: Allocations

The question this module answers: When a subscriber’s line drops and comes back, what has it missed, and how does it catch up?

Module 1 said it: the subscriber called once and stays on the line, and lines drop. A server is patched, a network blips, a certificate expires. Meanwhile Salesforce keeps publishing. So when the subscriber calls back, it has to answer a question: where do I pick up?

Here’s how I put it in class. I’ve been getting messages, numbered. The last one I handled was number 11. Then I was disconnected. While I was gone, more came in, and the newest is now 51. When I call back, I could say “just give me new ones.” But what if the missed messages were important? There might be one. There might be a hundred. So instead I say “I last had 11,” and I get 12 to 51, in order, and then whatever’s new.

That’s the fundamental idea of eventing on Salesforce. The events wait on the bus, each with its position, and the subscriber says where to start.

Salesforce keeps platform events and change events on the bus for 72 hours (Event Message Durability (opens in a new tab), Pub/Sub API Developer Guide, checked 2026-11-07). It’s a rolling window: at any moment, the last 72 hours are there to replay. Older events are purged. Sometimes the purge runs late and older events are still there, but Salesforce’s guide is clear: it “doesn’t guarantee the storage of events beyond the retention period of 72 hours.” Design for 72.

If you meet the older kinds, PushTopic and generic events, they’re kept 24 hours (Message Durability (opens in a new tab), Streaming API Developer Guide, checked 2026-11-07). That’s chapter 17.

Every event has a replay ID, its position in the stream. When the subscriber calls, it says where to start, in one of three ways. The Pub/Sub API names them; the older Streaming API uses numbers. They mean the same thing.

Where the subscriber picks up Where the subscriber picks up. On the right, the event bus, kept 72 hours, with three events: number 3, the oldest still kept, almost 72 hours ago; number 11, the last one this subscriber handled before the line dropped; number 51, the newest, sent while the line was down. On the left, the subscriber chooses one of three ways to pick up, each pointing at where it starts. Everything kept, EARLIEST or −2, starts at the oldest and gets 3 to 51, then new ones. After my last one, highlighted, CUSTOM with the stored ID, or the ID itself in the Streaming API, starts after 11 and gets 12 to 51, then new ones. New only, LATEST or −1, starts after the newest: quiet until 52, and 12 to 51 are missed. The legend: the usual choice is to store the ID of the last one handled, and pick up after it. Underneath: the numbers are for the picture; real replay IDs aren't counting numbers, so store them as Salesforce sends them. {"author": "Steve Simpson", "blueprint": "1.0.0", "builder": "standard 1.58.0", "built": "2026-11-03", "id": "DGM-INT-EV-07", "rights": "\u00a9 2026 Twinshark LLC. All rights reserved.", "template": "cells 0.5.0", "theme": "stevetecharc 1.4.0"} THE SUBSCRIBER CHOOSES THE EVENT BUS, KEPT 72 HOURS Everything kept EARLIEST, or -2: gets 3 to 51, then new ones Number 3 the oldest still kept, almost 72 hours ago After my last one CUSTOM with the stored ID, or the ID itself: gets 12 to 51, then new ones Number 11 the last one this subscriber handled before the line dropped New only LATEST, or -1: quiet until 52; 12 to 51 are missed Number 51 the newest, sent while the line was down starts at the oldest starts after 11 starts after the newest The numbers are for the picture. Real replay IDs aren't counting numbers: store them as Salesforce sends them. HOW TO READ IT The usual choice: store the ID of the last one handled, and pick up after it © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

New only, everything kept, or after my last one: the usual choice is the last.

Where to startPub/Sub APIStreaming APIWhen
Only new eventsLATEST (the default)-1 (the default)The first time, or when missed events don’t matter
After the last one I handledCUSTOM, with the stored replay IDthe replay ID itselfAfter any drop: the usual choice
Everything still keptEARLIEST-2When the stored ID is lost or too old; “sparingly”

From the Subscribe RPC Method (opens in a new tab) reference (Pub/Sub API, checked 2026-11-07) and Streaming API’s Message Durability, both checked 2026-11-07. Both guides say to use the “everything” option sparingly: replaying thousands of stored events slows the subscriber down.

Notice what the defaults do. If the subscriber says nothing, it gets new events only. So a subscriber that reconnects without a stored replay ID quietly skips everything it missed. Nothing errors. The copy is just wrong. That’s why “after my last one” is a design decision, written down, not a default.

“After my last one” only works if someone remembers the last one. Four things to know about that.

Store it after you’ve handled the event, not when it arrives. If the subscriber crashes between receiving event 12 and saving its work, it should come back asking for “after 11”, and get 12 again.

Store it as Salesforce sends it. The numbers in the picture are for teaching. Real replay IDs are opaque: the Pub/Sub API sends them as bytes, and Salesforce’s guide says “Replay ID values aren’t guaranteed to be contiguous” and subscribers “must not compute new Replay IDs” from a stored one. You can’t add one to 11 and ask for 12.

Store the quiet ones too. When nothing’s happening, the Pub/Sub API sends keepalive messages, each with a newer replay ID. Salesforce recommends saving that one too, so a restart doesn’t have to search far back in the stream (Subscribe RPC Method (opens in a new tab), checked 2026-11-07).

Or let Salesforce keep it. With ManagedSubscribe, still in Beta, the subscriber commits the replay ID of the last event it processed on Salesforce’s side, and a new subscription picks up after it (Event Message Durability (opens in a new tab), checked 2026-11-07; module 2). Being a Beta, it’s a decision for your own risk appetite, not a default.

Subscribers inside the org, Apex triggers and flows, have their place kept for them. That’s module 6.

Here’s the line I use in class. When you publish an event, it could be delivered zero times, three times. It could be delivered in less than a second, five minutes later, or 71 hours and 59 minutes later. You publish, and you have no idea. The subscriber has to be ready for all of it.

Again, out of order, too late Again, out of order, too late: three panels. Again, the same event twice: publishing is at least once, not exactly once; a copy has the same EventUuid and a new replay ID; keep the IDs you've handled, and skip a repeat. Out of order, not the order they happened: one publish call keeps its order; separate calls may not arrive in order; change events follow the order of the saves. Too late, older than 72 hours: past 72 hours, a stored ID may no longer work; replay what's still kept, then resync the rest; a return receipt shows what never arrived. Underneath: design the subscriber for all three before go-live; each one happens rarely, and each one happens. {"author": "Steve Simpson", "blueprint": "1.0.0", "builder": "standard 1.58.0", "built": "2026-11-03", "id": "DGM-INT-EV-08", "rights": "\u00a9 2026 Twinshark LLC. All rights reserved.", "template": "comparison/side-by-side 0.5.0", "theme": "stevetecharc 1.4.0"} Again the same event twice Publishing is at least once, not exactly once. A copy has the same EventUuid and a new replay ID. Keep the IDs you've handled, and skip a repeat. Out of order not the order they happened One publish call keeps its order. Separate calls may not arrive in order. Change events follow the order of the saves. Too late older than 72 hours Past 72 hours, a stored ID may no longer work. Replay what's still kept, then resync the rest. A return receipt shows what never arrived. Design the subscriber for all three before go-live. Each one happens rarely, and each one happens. © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Again, out of order, too late: rare, and each one happens.

Salesforce publishes platform events “at-least-once… not the exactly-once model.” Rarely, the same event is published twice. Salesforce describes the copy exactly: “the same EventUuid field value, a different ReplayId field value, and the same payload” (Considerations for Defining and Publishing Platform Events (opens in a new tab), Platform Events Developer Guide, checked 2026-11-07). So the replay ID can’t tell you it’s a repeat. The event’s own ID can. A replay after a drop can send events again too: that’s what “after my last one” does when the last one wasn’t saved.

So the subscriber keeps the IDs it has handled and skips a repeat, or, better, does work that’s safe to do twice: “set the status to Delivered” is safe; “add one to the count” isn’t. That’s idempotency, and the Data Architect course takes it apart (Safe to Retry).

The same guide says something harder to hear: in rare cases, an event isn’t stored at all, and then “the events aren’t delivered to subscribers, and they aren’t recoverable.” Zero times is real. That’s the return receipt’s job, below.

Salesforce promises less about order than people assume. For platform events: “If you publish multiple events in one publish call, the order of events in a batch is guaranteed for that publish request… For events published across different requests, the order of events isn’t guaranteed” (Order of Events (opens in a new tab), Platform Events Developer Guide, checked 2026-11-07). Two flows that publish a second apart can arrive the other way round. Subscribers then receive them in replay ID order, the order they landed on the bus.

For change events: “The order of events delivered is based on the order of the corresponding committed transactions” (Subscribe to Change Data Capture Events (opens in a new tab), Change Data Capture Developer Guide, checked 2026-11-07). And within one transaction, the header’s sequenceNumber gives each change its place, with transactionKey grouping them (module 3).

So if order matters, put something in the event that says it: a timestamp from the business, a version number. Then the subscriber can check before it writes: if this Delivery Update is older than the one I already have, don’t overwrite.

Now the student’s question, and it’s the right one: what happens when you’ve been gone longer than 72 hours?

What if, at Steve Trucking, the integration layer goes down on Friday evening before a long weekend, and nobody notices until Tuesday morning? That’s more than 72 hours. The stored replay ID may point at events that are gone. Salesforce’s answer, in the Subscribe reference, is EARLIEST, for when “a client has been disconnected for more than 3 days and the last saved replay ID is no longer valid.” That gets back what’s still kept. It doesn’t get back what was purged.

So past 72 hours, the event stream alone can’t make the copy whole. Two patterns close the gap:

  • The return receipt. The publisher keeps its own record of what it sent, and the subscriber confirms what it received. Anything sent and never confirmed shows up, and gets sent again. That’s the Data Architect course’s return receipt, and it also catches the rare event that was never stored.
  • The resync. Replay what’s still kept, then compare the copy with Salesforce for the gap, by date, the way chapter 10 does it.

And one more reason to send an ID rather than the whole package: an event that sat on the bus for 70 hours carries 70-hour-old data. If the subscriber reads the record when it handles the event, it gets today’s values.

One last thing, rare and worth knowing. When Salesforce moves an org to a new instance, the stored stream can be reset and the retained events are no longer there, and the replay ID “isn’t guaranteed to be unique when Salesforce maintenance activities occur, such as an org migration” (Event Message Durability (opens in a new tab), checked 2026-11-07). A move is planned maintenance, so when one is planned for your org, put it on the integration’s calendar: it’s a planned resync, not a surprise.

Test knowledge. Platform events and change events are kept 72 hours (PushTopic and generic events, 24). Know the replay options: −1 or LATEST for new events only (the default), −2 or EARLIEST for everything still kept (use sparingly), a stored replay ID, or CUSTOM, for everything after it. Replay IDs are opaque and not contiguous; store them, don’t compute them. Delivery is at least once: duplicates share an EventUuid. Order is kept within one publish call, not across calls; change events follow the order of committed transactions. Past 72 hours, plan a resync or a return receipt.

In early 2024 I made a video on exactly this: keeping the replay ID, reconnecting, and catching up within the 72 hours. The note under the video says what’s still right and what’s changed since.

STA 1.21 · Across the APIs: Resilience in Integration,Platform Events and Use Of Replay Id Explained · 10:07 · published 2024-01-10 · plays from YouTube when you click it · Watch on YouTube · More videos: Salesforce Integration Architecture Videos
  • Where each subscriber picks up after a drop, written down: after the last one handled, not the default.
  • Who keeps the replay ID: the subscriber’s own store, or a managed subscription (Beta), and when it’s saved, after the work is done.
  • How the subscriber handles a repeat: by the event’s ID, or by work that’s safe to do twice.
  • What carries order, if order matters: a business timestamp or version in the event, checked before writing.
  • How long a subscriber can be down before someone is told, well inside 72 hours, and who’s told.
  • What closes the gap past 72 hours: a return receipt, a resync, or both, tested before go-live.