The catalogue decides what an event is called and what it contains, and everything downstream copies that decision.
An integration reads what happened, and how settled that fact is, without polling the chain or running its own indexer: one registry binds each event name and version to exactly one payload shape.
Every event names its lifecycle state. Read it with the event type: final can mean chain finality or a committed platform outcome. A final transaction-request event does not itself confirm a token movement.
Delivery retries can produce duplicates. Deduplicate on the event identifier and reconcile missing or retracted events.
Why an integration needs a catalogue, 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 the platform's event vocabulary 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 the same for every subscriber, and the envelope states it. Made independently by every integration, it is a second reading of the same chain, on that integration's own schedule and its own judgment of depth.
Each handoff between separate systems is a reconciliation. A platform event pins the handoff to one record: what the integration was told, checked against what the platform's own indexed record says happened. The platform keeps that delivery record for 30 days after it settles, so retain what your reconciliation needs.
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 catalogue, tagged with one of five lifecycle states.
Why this catalogue lives on the platform, not in a tenant's own indexer
The alternative to a platform-run catalogue is a tenant indexer running beside the platform: watching the same chain, choosing its own reorganisation 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 catalogue 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.
A vocabulary that cannot move under you
A platform event is one entry in a vocabulary defined by a single registry. Each entry binds an event name and version to one payload shape. The build rejects a second registration of the same name and version.
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 platform generates the machine-readable event manifest, event types for the TypeScript SDK, and sample payloads from that registry. The event catalogue is written by hand and checked against the registry. It explains when each event is sent and what a receiver does with it. Payload schemas and examples stay in the machine-readable definitions. Registration alone does not prove the platform sends an event. The catalogue separates live events from reserved names.
An integration selects the published payload contract by event name and version. Read both fields before interpreting the payload.
The envelope states how settled a fact is
Every platform event has a lifecycle_state: pending, provisional, final, retracted, or recalled. Recalled is reserved. No live event uses it. Many names end in a state, such as token.transfer.final, but others end in a word such as raised or published. Read the envelope's state and event type together. The lifecycle reference describes the live sequences and reserved states.
A state change is a new event and so is a correction, and nothing edits a delivery already sent.
For chain observations, provisional means included but still within the network's configured confirmation depth, and final means the source block has passed that depth. A platform administrator sets that depth for each network. The platform emits provisional at inclusion and final after the depth is reached. A chain reorganisation within the depth can retract a delivered provisional event. A reorganisation deeper than the configured depth is outside what a final event promises.
A fact does not change state in place. The same movement reported first as provisional and later as final reaches you as separate events with separate identifiers. Watch for the later event rather than for a field changing on the earlier one.
A retraction reports that an earlier fact no longer stands. It can follow a chain reorganisation or a business reversal, such as a trade bust. The platform leaves the earlier delivery intact. The consumer contract defines how to match the correction, including when its supersedes field is absent or names an event you never received.
Pending reports a transaction request that the platform broadcast and that is not confirmed. A request outcome uses final, including a failure or cancellation. This is the request's outcome, not a token movement's chain finality. Recalled is a reserved state, and no live event uses it.
From one log to one row per subscriber
For an event derived from a chain log, the platform records one pending delivery 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 the event and the indexed state it describes commit together or not at all.
Transaction-request events describe a different source: a state change recorded inside the platform. The platform records the event together with that state change, rather than deriving it from an indexed chain log.
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.
Every subject area the indexer writes, token movements and identity and role changes alike, handles a failure to write those rows the same way. A failure to record the deliveries 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 recorded delivery, and Webhook Delivery moves that delivery to your endpoint. The cost is that a delivery-recording problem delays the indexed record too.
Each endpoint has its own delivery sequence. A later eligible event waits while an earlier event remains pending, including during retry backoff. A linked correction waits until its cause was delivered or can no longer be delivered. Events waiting for chain finality do not block later events. A Replay can deliver an older observation after a newer one, so keep the newest state you hold. The ordering contract states the limits.
Writing a consumer that stays correct
A correct consumer subscribes only to the states it handles, deduplicates on the event identifier, and applies a superseding event by looking up the identifier that event names instead of assuming order.
An endpoint subscribes with patterns. A pattern matches a name prefix, as in token.transfer.*, a name suffix, as in *.final, or one exact name. The defaults match names ending in .provisional, .final, and .retracted, not the envelope's lifecycle state. The subscriptions reference lists live events these defaults skip. A *.pending pattern delivers nothing, because no live event name ends in .pending. A subscription change replays no earlier events. Replay is a separate request.
The webhook-id header carries the event identifier. Store each identifier and check it before you act. Delivery, 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 published manifest lists only the event names the platform sends, and schemas for names it does not send stay available for historical decoding. The 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., token.transfer.final version 2, and token.transfer.retracted versions 1 and 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 retraction caused by a chain reorganisation supersedes a provisional observation, or a stored version 1 token.transfer.final event, a legacy event the platform sent at inclusion. 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.