Events Inside the Org
Module 6 of 8 in the chapter Events, chapter 11 of the Integration Architect course. Each module stands on its own. Previous: Allocations · Next: Beyond the Bus
The question this module answers: When the subscriber is inside Salesforce, what does an event buy you, and what does the subscriber need to be set up for?
Not Only for Other Systems
Section titled “Not Only for Other Systems”So far in this chapter the subscriber has been outside: the integration layer, Snowflake, a partner. But Salesforce doesn’t only talk to outside clients. A flow or an Apex trigger in the same org can subscribe to the same event.
That’s process-to-process communication. One process publishes, others listen, and they never have to know about each other. Chapter 4 built the listening flow and the central error handler on it (Platform Events in Flow). Chapter 7 drew why it decouples (Publish and Subscribe). And module 5 showed it isn’t counted against the delivery allocation.
This module is about the other thing an event does inside the org: it starts a new transaction.
A New Transaction
Section titled “A New Transaction”The subscriber doesn’t run in the publisher’s transaction. Salesforce’s guide is direct about it: triggers on platform events “don’t execute in the same Apex transaction as the one that published the event. The trigger runs asynchronously in its own process” (Subscribe to Platform Event Notifications with Apex Triggers (opens in a new tab), Platform Events Developer Guide, checked 2026-11-07). Flows are the same.
That gives you two things:
- A fresh set of limits. A process that’s getting close to its transaction limits can do its part, publish an event, and be done. The subscriber picks up the rest in its own transaction, with its own limits. It’s not a replacement for Queueable or Batch Apex. But when you’re switching context, doing A and then switching to B, it’s a good way to hand off.
- Two saves that can’t be one. That’s the next section.
And it costs one thing: the subscriber runs a moment later, not in the same instant. Usually less than a second. But nothing that needs the result on the screen right now should wait for it.
Don’t Cross the Streams
Section titled “Don’t Cross the Streams”Has anyone heard of a mixed DML error? It gives people nightmares. Here’s how I describe it. There are two kinds of save. Saves on business records: accounts, contacts, work orders. And saves on the things that decide who can see and do what: users, permission sets, roles, groups. Salesforce won’t let you mix certain of them in one transaction.
Why? Say you assign a permission set to a user, and in the same transaction save a record as that user. Should Salesforce save it with the user’s old permissions or the new ones? The new ones aren’t committed yet. Salesforce’s answer is to make you do them apart: some objects “affect the user’s access to records in the org. You must insert or update these types of sObjects in a different transaction” (sObjects That Cannot Be Used Together in DML Operations (opens in a new tab), Apex Developer Guide, checked 2026-11-07). The list includes users, permission set assignments, group members and roles.
It’s like the line from the film: don’t cross the streams. Keep the two kinds of save apart. And a platform event is the way to do it.
Users and permissions in one save, the business records in another, with an event between.
Here’s an example at Steve Trucking. A new driver starts, and needs a user for the mobile app. The first save creates the user and assigns the mobile app permission set, then publishes a Driver Ready event with the driver and the new user. A platform event–triggered flow picks it up a moment later, in its own transaction, and saves the driver’s Contact and crew assignment. Part A, publish, part B.
Two things to design with it. The first save should publish with Publish After Commit, so the flow never hears about a user that wasn’t saved (module 3). And part B can fail on its own, after part A has succeeded. Give it a fault path that tells someone: a driver with a user and no crew assignment is a support call on their first morning.
A Flow or a Trigger
Section titled “A Flow or a Trigger”Both a flow and an Apex trigger can subscribe inside the org. They behave differently in four ways that matter to an architect.
Who it runs as, how many at once, what happens when it fails, and how many streams.
From the Platform Events Developer Guide, checked 2026-11-07: Subscribe to Platform Event Messages with Flows (opens in a new tab) and Subscribe to Platform Event Notifications with Apex Triggers (opens in a new tab). Chapter 4 covers the flow side; the rest of this module is the trigger side.
Who the Trigger Runs As
Section titled “Who the Trigger Runs As”By default, a platform event trigger runs as the Automated Process user. That has consequences people find in production: records the trigger creates are owned by Automated Process, Created By says Automated Process, and the trigger can’t send email.
You can change it. A PlatformEventSubscriberConfig, deployed through the Metadata API or the Tooling API, sets the trigger’s running user and its batch size (Configure the User and Batch Size for Your Platform Event Trigger with PlatformEventSubscriberConfig (opens in a new tab), checked 2026-11-07):
<?xml version="1.0" encoding="UTF-8"?><PlatformEventSubscriberConfig xmlns="http://soap.sforce.com/2006/04/metadata"> <masterLabel>DeliveryUpdateSubscriberConfig</masterLabel> <platformEventConsumer>DeliveryUpdateSubscriber</platformEventConsumer> <user>integration.user@stevetrucking.example</user> <batchSize>500</batchSize></PlatformEventSubscriberConfig>Two things to know about the user. It can be “any active user in the Salesforce org,” and the trigger still “runs in system context with privileges to access all records regardless of the user’s object and field-level permissions.” So the user decides who owns and created the records, and sharing when the code runs with sharing, not what the trigger can see. Pick a named integration user, not a person who might leave.
How Many at a Time
Section titled “How Many at a Time”A platform event trigger receives events in batches, in replay ID order, up to 2,000 at a time. That’s ten times an object trigger’s 200, and so ten times as likely to hit a governor limit. You can set the batch size anywhere from 1 to 2,000. Salesforce advises against small numbers: “Small batch sizes can slow down the processing of event messages.” Start with the default, measure, and lower it only if the trigger hits limits. A new setting takes effect when you suspend and resume the trigger, from the event’s Subscriptions list in Setup.
When the Trigger Fails
Section titled “When the Trigger Fails”Salesforce gives a trigger two ways to deal with failure. They do different things (Comparing setResumeCheckpoint() and EventBus.RetryableException (opens in a new tab), checked 2026-11-07):
- Try the whole batch again later: throw
EventBus.RetryableException. What the trigger saved so far is rolled back, and the whole batch is sent again after a delay that grows each time. Use it for something that’s likely to go away: a locked record, a system that’s briefly unavailable. A trigger can run “the initial run plus 9 retries.” After that it goes into the error state and stops. Worse: events sent while it’s in that state “aren’t resent to the trigger.” Salesforce’s advice is to stop retrying well before nine. - Pick up after the last one done: call
setResumeCheckpoint()with the replay ID of each event as it’s handled. If the trigger hits a limit, what it saved is kept, and it fires again starting with the next event.
Here’s a sketch of a Steve Trucking subscriber that keeps a status history (Status_History__c is an example object). It uses the first way, and it’s safe to run twice, because it upserts on the event’s own ID (module 4):
trigger DeliveryUpdateSubscriber on Delivery_Update__e (after insert) { List<Status_History__c> rows = new List<Status_History__c>(); for (Delivery_Update__e e : Trigger.New) { rows.add(new Status_History__c( Delivery_Number__c = e.Delivery_Number__c, Status__c = e.Status__c, Event_Uuid__c = e.EventUuid // an external ID: a repeat updates, it doesn't add )); } try { upsert rows Status_History__c.Event_Uuid__c; } catch (DmlException ex) { // A locked row usually clears. Try again, but stop well before the limit. if (EventBus.TriggerContext.currentContext().retries < 4) { throw new EventBus.RetryableException('Status history not saved yet; trying again.'); } // Gave up: hand it to the central error handler (chapter 4) so someone is told. }}Whichever you choose, the trigger is the subscriber, and the subscriber owns its error handling. The error state is the trigger’s version of a dropped line, and nobody is told unless you build the telling.
More Than One Stream
Section titled “More Than One Stream”One trigger processes one stream, in order. When the volume is high, that can be too slow. Parallel subscriptions let one trigger run as up to 10 streams side by side, for custom high-volume platform events (not standard events or change events) (Platform Event Processing at Scale with Parallel Subscriptions for Apex Triggers (opens in a new tab), checked 2026-11-07).
You choose a partition key, an event field, and Salesforce sends every event with the same key value to the same stream. That decides two things at once:
- Order. Events with the same key are handled in their original order. Events with different keys may not be (Order of Events in Parallel Subscriptions (opens in a new tab), checked 2026-11-07).
- Locks. Salesforce’s advice: “choose a partition key that ensures the placement of events that access the same records in the same partition” (Choosing a Partition Key (opens in a new tab), checked 2026-11-07).
The Data Architect course uses exactly this for Steve Trucking’s 4 p.m. status updates: the Manifest as the key, so each Manifest’s updates run in order, and different Manifests run side by side, with the configuration (Designing Out Contention).
Watch the Subscribers
Section titled “Watch the Subscribers”In Setup, each platform event’s page lists its subscribers inside the org: triggers, flows and processes, with each one’s state and the replay ID it last processed (View and Manage an Event’s Subscribers on the Platform Event’s Detail Page (opens in a new tab), checked 2026-11-07). That’s where you’ll see a trigger in the error state, and where you suspend and resume it. Pub/Sub API subscribers outside aren’t listed there; module 5’s usage metrics cover them.
One more line from the flow side: if several flows and triggers listen to the same event, Salesforce can’t guarantee which handles each event first. Don’t build one subscriber that depends on another having run.
Test knowledge. Subscribers inside the org run in their own transaction, asynchronously: a fresh set of limits, and the fix for mixed DML (setup objects such as User and PermissionSetAssignment in one transaction, business records in another). Platform event triggers are after insert, receive up to 2,000 events a batch in replay ID order, and run as Automated Process unless PlatformEventSubscriberConfig sets a user (and a batch size, 1–2,000). Flows take up to 200 and run as the publishing user.
EventBus.RetryableExceptionresends the batch (up to 9 retries, then the error state);setResumeCheckpoint()resumes after the last event handled. Parallel subscriptions: up to 10 partitions, a partition key, order kept only within a key.
What an Architect Decides Here
Section titled “What an Architect Decides Here”- Which hand-offs inside the org go through an event, and which need the result right now and stay in the same transaction.
- Where setup saves and business saves are split, with Publish After Commit on the first, and a fault path on the second.
- A flow or a trigger for each subscriber, by who it should run as, how many it takes, and how it fails.
- Each trigger’s running user and batch size, set in a PlatformEventSubscriberConfig, in source control.
- How each trigger fails: retry for what clears, a checkpoint for limits, a cap well below nine, and work that’s safe to do twice.
- Whether a trigger needs parallel subscriptions, and the partition key that keeps order and avoids locks.
- Who watches the Subscriptions list, and who’s told when a trigger goes into the error state.
Explore
Section titled “Explore”- Subscribe to Platform Event Notifications with Apex Triggers (opens in a new tab) and Subscribe to Platform Event Messages with Flows (opens in a new tab): Platform Events Developer Guide (checked 2026-11-07)
- Configure the User and Batch Size for Your Platform Event Trigger with PlatformEventSubscriberConfig (opens in a new tab) and PlatformEventSubscriberConfig (opens in a new tab), Metadata API Developer Guide (checked 2026-11-07)
- Retry Event Triggers with EventBus.RetryableException (opens in a new tab), Resume a Platform Event Trigger After an Uncaught Exception (opens in a new tab) and Comparing setResumeCheckpoint() and EventBus.RetryableException (opens in a new tab): Platform Events Developer Guide (checked 2026-11-07)
- Platform Event Processing at Scale with Parallel Subscriptions for Apex Triggers (opens in a new tab) and Choosing a Partition Key (opens in a new tab): the same guide (checked 2026-11-07)
- View and Manage an Event’s Subscribers on the Platform Event’s Detail Page (opens in a new tab): the same guide (checked 2026-11-07)
- sObjects That Cannot Be Used Together in DML Operations (opens in a new tab): Apex Developer Guide, mixed DML (checked 2026-11-07)
- Salesforce Developers, Platform Events Developer Guide, Order of Events in Parallel Subscriptions (opens in a new tab), checked 2026-11-07.
