Events you can rebuild state from

Most webhook implementations send a payload and hope. The ones you can build on tell you what changed, at which revision, exactly once in effect — and let you replay the week you were down.

ERPShipped in segment S00 · 850 words
ONE OBJECT TYPE · SEVEN KINDS · ONE LIFECYCLEProblem reportsomething is wrongECRshould we change it?ECOwhat exactly changesECNtell everyoneReleasedatomicDeviationship off-spec, boundedWaiveraccept as-is, onceStop shiphalt, nowdo not alterthe designSeven kinds, one object type — because seven types means seven copies of the same attribute list drifting apart.

What it is, and what it is not

An outbound event stream: subscribe to object types and lifecycle transitions, receive a signed HTTP callback when something happens, and pull the detail you need through the API.

It is not a data replication mechanism. Events tell you *that* something changed and give you enough identity to fetch it; they do not attempt to be a complete mirror of the object, because a payload large enough to reconstruct state is a payload that goes stale between being built and being delivered.

It is also not a substitute for the connectors. The six ERP connectors handle their own lifecycle, retries and conflict behaviour. Webhooks are for the integrations you build yourself, where you own what happens next.

The mechanism: identity, not payload

An event names an object at a revision. change.released carries the change identifier and the revisions it produced; part.revised carries the part and the new revision. What it does not carry is the whole structure, because by the time your handler runs, a large embedded payload is a claim about a moment that has passed.

You then fetch what you need through the API, which resolves under conditions you state. That is the important consequence: your handler asks the same question the interface would, rather than parsing somebody's guess about which fields you wanted.

Delivery is at-least-once, and every event carries an idempotency key. Duplicates happen — a timeout on our side, a retry that arrives after your handler already succeeded — and a consumer that keys on the identifier is unaffected. Promising exactly-once over HTTP would be promising something the transport cannot give.

Retries are exponential with a bounded ceiling, and a subscription that fails persistently is suspended rather than retried forever. You are notified, because a silently disabled subscription is how an integration stops working for three weeks before anyone notices.

The failure it prevents

A team builds a notification service on a PLM's webhooks. It works for months, then their endpoint is down for four hours during a deploy.

The PLM retries for fifteen minutes and gives up. Eleven change releases are lost — not delayed, lost — and nobody knows which eleven, because there is no record of what was attempted. The team discovers it a fortnight later when somebody asks why a supplier never received a change notice.

The recovery is a manual reconciliation against the PLM's audit trail, and the lesson learned is not to trust webhooks. A replayable event log turns that four hours into a command: replay from this timestamp. The events were never lost, only undelivered, and those are different problems.

How it meets the rest of the product

Events fire on the same lifecycle transitions the workflow engine acts on, so what you can subscribe to is what the product genuinely considers a state change — not an arbitrary list somebody chose to expose.

Subscriptions are permission-scoped. An event about an object the subscribing identity may not see is not delivered, and the absence does not reveal that something happened. A webhook that leaked existence through delivery patterns would undo the isolation work everywhere else.

Every delivery attempt is logged with its response code and timing, and the log is queryable. That makes the question every integration eventually asks — did you send it, or did we drop it — answerable rather than a matter of assertion between two teams.

Agent actions emit events like human ones, attributed to the agent and its tier, so a downstream system can distinguish a change drafted by the Change Agent from one raised by a person.

How it meets your ERP

The six built-in connectors do not use webhooks — they are invoked directly as part of the atomic release transaction, so publication cannot be delayed or lost by a delivery failure. That is deliberate: an ERP publication is too consequential to sit behind at-least-once HTTP.

Webhooks are the right tool for the systems the connectors do not cover: a homegrown MES, a customer's procurement portal, a regional distributor's system, an internal dashboard. You own the handler and the retry semantics on your side.

Where you need the same lifecycle, retry and conflict behaviour the built-in connectors have, the connector SDK is the supported path rather than webhooks plus your own scheduler — because reimplementing reconciliation correctly is harder than it looks and getting it wrong produces the silent-overwrite failure the matrix exists to prevent.

Where the boundary is

No inbound webhooks. There is no endpoint that accepts an event from an outside system and mutates product data, because a write path with no attributed actor and no change process is precisely the hole every other control on this site is designed to close. Inbound work goes through the API with a scoped token.

Retention on the replay log is finite — long enough to cover an outage and a holiday, not long enough to be an archive. If you need the full history of what changed and when, that is the audit trail, which is retained for the life of the tenant.

Facts

PayloadObject at a revision, not an embedded copy of it
DeliveryAt-least-once, with an idempotency key on every event
RetriesExponential, bounded; persistent failure suspends and notifies
ReplayFrom a timestamp — undelivered is not the same as lost
ScopingPermission-scoped; non-delivery reveals nothing
Delivery logQueryable, with response codes and timings
ERP connectorsDo not use webhooks — invoked inside the release
Not offeredInbound webhooks that mutate product data

Frequently asked

Why not send the whole object in the payload?

Because a payload large enough to reconstruct state is a claim about a moment that has already passed by the time your handler runs. An event names the object at a revision, and you fetch what you need through the API, resolving under conditions you state yourself.

Is delivery exactly-once?

No — at-least-once, with an idempotency key on every event. Promising exactly-once over HTTP would be promising something the transport cannot give. A consumer that keys on the identifier is unaffected by the duplicates that timeouts and retries inevitably produce.

What happens if our endpoint is down?

Retries are exponential with a bounded ceiling, and a persistently failing subscription is suspended rather than retried forever — always with a notification, because a silently disabled subscription is how an integration quietly stops working for three weeks before anybody notices.

Can we recover events we missed?

Yes, by replaying from a timestamp. That is the difference between undelivered and lost, and it turns a four-hour outage from a manual reconciliation against the audit trail into a single command. Replay retention covers an outage and a holiday, not an archive.

Do the ERP connectors use webhooks?

No, deliberately. They are invoked directly inside the atomic release transaction, so a publication cannot be delayed or lost by a delivery failure. An ERP publication drives procurement to act, which is too consequential to sit behind at-least-once HTTP delivery.

Can events leak things we cannot see?

No. Subscriptions are permission-scoped, so an event about an object the subscribing identity may not see is not delivered — and the absence does not reveal that anything happened. Leaking existence through delivery patterns would undo the isolation work everywhere else.

Do you accept inbound webhooks?

No. There is no endpoint that takes an event from an outside system and mutates product data, because a write path with no attributed actor and no change process is exactly the hole every other control here is designed to close. Inbound work uses the API.