The quick version: a platform event is a published contract, not a custom object you can tidy up later. Subscribers compile against its shape, published events cannot be recalled, and there is no versioning mechanism in the platform. So the schema decisions you make in the first hour are the ones you live with, and the only safe change after go-live is adding an optional field.
What you can actually put in one
Custom fields on an event come from a short list: checkbox, date, date/time, number, text and long text area. No picklists, no formulas, no relationship fields. To point at a record, use Text(18) holding the Id and let the subscriber do its own lookup.
Salesforce adds the system fields: ReplayId for replaying a stream, EventUuid, CreatedById and CreatedDate. CreatedDate is the publish time, not the time the business thing happened. If that difference could matter, carry your own timestamp.
Events are retained for 72 hours. A subscriber that is down for a long weekend has lost the events it missed, and no amount of replay recovers them.
Thin events or fat events
This is the first real decision.
A thin event carries an Id and an event type, and the subscriber calls back for the detail. Small payload, stable schema, at the cost of a round trip and a race: by the time the subscriber queries, the record may have moved on.
A fat event carries everything the subscriber needs. No callback, no race, a true snapshot of the moment. The cost is a wider schema, which is precisely the thing that is hard to change.
For integrations crossing a company boundary, fat events usually win, because the external system may have no route back in. Internally, thin is often enough.
Fields worth having in every event
- An idempotency key. Delivery is at-least-once, and replay can hand a subscriber the same event twice. A producer-generated unique reference lets the subscriber discard duplicates. This is not optional in anything touching money.
- A business timestamp, distinct from the publish time.
- A source identifier naming which system or process published it. When three things publish the same event type, this is how you debug it.
- A schema version number as a plain integer. It costs nothing and it gives subscribers something to branch on.
Resist the generic event with one long text field full of JSON. It works on day one, then you cannot filter on anything. Event-triggered flows cannot branch on the contents, and Pub/Sub API subscribers cannot filter server-side. The typed fields are the feature.
Why you cannot change one easily
The Pub/Sub API delivers events as Avro-encoded payloads carrying a schema ID. Consumers fetch the schema by that ID and cache it. Change the event definition and the schema ID changes, so every client has to handle refetching, and any client that decoded with a hard-coded schema fails.
Inside the org the problem is different but no smaller. An Apex trigger referencing a removed field will not compile, and a flow referencing it becomes invalid. Neither breaks when you make the change. They break when an event arrives.
The practical rule: adding a field is safe, because old subscribers ignore it. Everything else is a breaking change, including renaming a field, changing a type, and changing the meaning of a value while keeping the name.
Versioning by creating a new event
Since the platform does not version events, you do it with object names. Order_Status_Changed_v2__e alongside the original.
The sequence that works: create the new event, stand up its subscribers, dual-publish from the producer, watch the old event’s traffic fall to zero, then stop publishing it and delete it last. Dual-publishing costs event allocation for a while, which is cheaper than a cutover that strands a subscriber.
Mark the old event as deprecated in its description, because the description is the only documentation most people read.
Frequently asked
Can I delete a field from a platform event? Technically yes, and it will break every subscriber that references it, with no warning until an event is published. Treat it as creating a new event instead.
Publish immediately or publish after commit? After commit is the safer default, because the event only goes out if the transaction succeeded. Publish immediately is for cases where you want the event even when the transaction rolls back, such as logging a failure.
How many events does a trigger get at once? Up to 2,000 in a single execution. Write the trigger bulk-safe and use a resume checkpoint if the batch can exceed your governor limits.
Can I report on platform events? No. They are not stored as records. If you need history, have a subscriber write what it received to a custom object or a big object.
