Platform Event
A Platform Event tells an integration what happened, under a name and shape that cannot change, so it never has to poll the chain or re-derive finality itself.
The catalog decides what an event is called and what it contains. Everything downstream copies that decision.
An integration reads what happened without polling the chain or re-deriving finality itself: one list binds every event name and version to a payload shape, and registering the same pair twice fails at startup.
Every event names its lifecycle state. Final means the event passed the configured chain reorg depth. Reorg retractions apply to provisional observations only.
Delivery retries can produce duplicates. Deduplicate on the event identifier and reconcile missing or retracted events.
Why an integration needs a catalog, not raw logs
A platform event is how an integration learns what happened on the platform: what occurred, when, and how settled that fact is. A chain log answers the first two on its own. It says nothing about the third, because the block behind it can still be reorganised.
The alternative to a generated catalog is an integration that reads raw logs itself. It decides, on its own, how deep a block must sit before a fact counts as final, and what to do when a block it already trusted gets replaced. Made once, by the platform, that decision is a fact every subscriber can check. Made independently by every integration, it is a second reading of the same chain, taken on that integration's own schedule and its own judgment of depth.
Asset Lifecycle names the general risk in that second reading. Spreading one instrument's issuance, custody, trading and reporting across separate systems turns every boundary between them into a reconciliation, and a reconciliation is where an instrument's ledger state and its legal record drift apart. A platform event holds that boundary at one place: what the integration was told, checked against what the platform's own indexed record says happened, from a name and a payload shape fixed by one registered list.
That list is not tied to one stage of an instrument's life the way a coupon payment belongs to servicing. Issuance, servicing and an eventual retirement each produce their own facts, and each one reaches an integration through this same catalog, tagged with one of five lifecycle states.
Why this catalog lives on the platform, not in a tenant's own indexer
The alternative to a platform-run catalog is a tenant indexer running beside the platform: watching the same chain, choosing its own reorg depth, and turning raw logs into its own vocabulary. That second indexer answers the same question the platform already answers, from a read of the chain that only the tenant sees.
This catalog holds no second reading, because the same indexer that decides a chain fact for the platform's own record is the one that derives the outbox row a subscriber later receives. The event and the indexed fact it describes come from that one decision, not from two.
For a financial institution operating a regulated instrument, two different readings of the same chain fact would leave the integration's own reconciliation and the platform's ledger starting from different guesses instead of the same registered description.
A vocabulary that cannot move under you
A platform event is one entry in a catalog the platform generates from a single registered list. Each entry binds an event name to exactly one payload shape, and registering the same name and version a second time fails while the platform is still starting, so a name cannot come to mean something else without anyone noticing.
That guarantee targets the two quiet ways an integration built on notifications usually breaks. A field changes shape and the consumer keeps parsing the old one. Or a name starts covering a slightly different occurrence and nobody tells the consumers. Both fail silently, and a reconciliation turns them up weeks later.
The list is also the only source for everything a consumer reads. Generation produces the typed client library, the machine-readable subscription manifest, the event catalog pages and the sample payloads from that one list. A build check then compares each generated file against a fresh copy of what the list says it should be, and rejects as stale any edit someone made to the published description rather than to the list. The generated catalog describes registered event contracts. Registration alone does not prove an event has an active producer.
The practical effect is that an integration team can commit to an event name. The name and its contents move together, in the open, or they do not move at all.
The lifecycle state is part of the name
Every platform event names how settled the fact it reports is. The five lifecycle states are pending, provisional, final, retracted and recalled, and the state is the last segment of the event name, so token.transfer.final and token.transfer.retracted are separate catalog entries with separate payload shapes. The suffix tells you the state without any payload parsing.
A state change is a new event, and so is a correction. Nothing ever edits a delivery already sent.
The first three states describe different stages of a chain fact. Pending means observed before inclusion, provisional means included but still within the configured reorg depth, and final means the source block has passed that depth. Current transfer producers emit provisional at inclusion and final after the depth is reached. A reorg may retract a delivered provisional event; it does not retract a final event.
A fact does not change state in place. The same movement reported first as provisional and later as final reaches you as two separate events with two separate identifiers, because the state is part of the event name and each name is its own catalog entry. Watch for the arrival of the later event rather than for a field changing on the earlier one.
The remaining two states report that an earlier event no longer stands. A retracted event follows a chain reorganisation that invalidated the log behind the original. A recalled event follows an operator deciding, deliberately, that a delivery must be withdrawn. Either way the platform leaves the original delivery alone and sends a new event naming it, so the record of what you were told, and when, survives.
Recall is narrower than it sounds. Only an event family that declares a recalled state accepts a recall at all, and today exactly one does: the compliance freeze family. Ask to recall anything else and the platform refuses with that reason rather than accepting the request and ignoring it. You do not have to ask anyone which families qualify: each catalog entry lists the lifecycle states it declares, and the published manifest reflects that list, so a recallable family is the one with a recalled entry in the catalog.
Declaring a recalled state and currently emitting one are different facts. The compliance freeze family has no active recall producer, so its schema remains available for historical decoding but is absent from the published manifest.
From one log to one row per subscriber
The indexer derives most platform events from a chain log it writes. Two routes are the exception: a recall and a test event both originate from an API call rather than a chain log, so an operator or a caller triggers those directly. For everything the indexer does derive, it writes one outbox row for every endpoint whose subscription matches the event name, in the same database transaction as the indexed fact. No queue sits in memory between the two, so an event and the indexed state it describes commit together or not at all.
The fan-out is per subscriber, not per fact. Ten subscribed endpoints mean ten rows from one log, each signed on its own and each with its own identifier, so one endpoint's delivery history says nothing about another's.
Each subject area the indexer writes decides for itself what a failure to write those rows should do, and the areas disagree. Read one path and assume the other, and the difference will surprise you.
Token movement
The outbox write runs in a nested savepoint. When it fails, the platform logs the failure and commits the indexed movement anyway, and you never receive that notification. A notification never holds the ledger hostage. The cost is that your record can lag the chain silently, with no signal on your end that it happened.
Identity and role changes
An outbox failure propagates and blocks the indexed write, so the whole unit retries together. When the retry succeeds, the indexed change and its notification land in that same commit, so a caller never sees one without the other. No gap can open between the indexed fact and its notification. The cost is that a notification problem delays the indexed record too.
Two keys carry the ordering. Rows sort by chain position first, meaning chain, block number and log index, and then by a per-endpoint sequence number that climbs each time the same log produces another row for that endpoint. The dispatcher reads its work in exactly that order. It then sends deliveries concurrently, so the order it picked work up is not the order your endpoint sees. Assume events can arrive out of order, and treat every handler as safe to run on its own. If you need a strict sequence, rebuild it from the ordering keys on arrival rather than from the order in which requests reach you.
Writing a consumer that stays correct
Three properties decide whether a consumer stays correct. It subscribes only to the states it intends to handle, deduplicates on the event identifier, and applies a superseding event by looking up the identifier that event names rather than assuming order.
An endpoint subscribes with patterns rather than with a list of names, so a new event in a family you already follow reaches you without a configuration change. A pattern matches a whole prefix, as in token.transfer.*, or a whole suffix, as in *.final, or one exact name. A new endpoint starts on the four suffix patterns for provisional, final, retracted and recalled. Pending is opt-in: add a pattern matching its suffix, such as *.pending, to receive it. Those patterns govern what arrives from that moment on. They replay nothing that happened before the endpoint existed. A replay is a separate request somebody has to make on purpose.
Store it and check it before you act. DeliveryWebhook Delivery retries a signed notification for three days and records whether a counterparty was told, so an integrator skips its own retry logic.Read the concept, the signed HTTPS POST that carries one event to one subscribed endpoint, uses bounded retries: a retry after a lost acknowledgement can repeat a delivery your endpoint already processed. Nothing on the platform side deduplicates for you.
Treat an event as an append-only record rather than as an update. When a later event supersedes an earlier one, it includes a supersedes field naming the identifier it replaces, and the platform never rewrites the original delivery. That field holds one identifier, never a list, so a superseding event always points at exactly one predecessor. A consumer that applies a retraction by looking up the superseded identifier and reversing its effect stays correct through a reorganisation. A consumer that overwrites in place cannot tell the difference between a correction and a fresh occurrence.
The platform publishes machine-readable definitions as a subscription manifest, the DALP events manifest, with a version marker that identifies the inventory it describes. Pin that marker if you generate code from the manifest, so an inventory change is a decision you make rather than one that arrives during a deploy.
Frequently asked questions
Does subscribing to an event name guarantee the platform sends it?
No. The internal corpus retains unavailable event schemas for historical decoding and future producers. The published manifest lists only event names with a composed producer. At present, live names include the access-control role events, asset creation, burn, issuance and pause events, compliance freeze and unfreeze events, identity registration events, the transfer events token.transfer.provisional, token.transfer.final version 2 and token.transfer.retracted version 2, the identity claim events, the provider verdict event, the trading venue order status event, token sale events, identity status, custody events, and the eleven XvP settlement events under xvp.*.final. Compliance recall, settlement retraction, and pre-inclusion pending names have no active producer.
Can a retracted or recalled event itself be retracted later?
No. A chain reorg retraction supersedes a provisional observation only. A retracted or recalled event is not itself retracted by a later chain reorganisation.
Do two endpoints see the same identifier for the same occurrence?
No. An identifier names one endpoint's delivery record, not the occurrence itself, which is why deduplication works per endpoint rather than across endpoints.
Reporting Obligation
A reporting obligation lets your organisation prove to a regulator which rules governed a filing, and keeps that proof fixed even after the duty changes.
Webhook Delivery
Webhook Delivery retries a signed notification for three days and records whether a counterparty was told, so an integrator skips its own retry logic.