Webhook delivery can repeat after a timeout, arrive out of order, or disappear during an endpoint outage. Validate the sender using the provider's supported secret or signature scheme, enforce a bounded request size, and acknowledge only after the event is durably accepted. Use an event ID for transport deduplication, but use the CMS record ID and current revision to decide the public state. A replayed publish event must not resurrect a record that was later unpublished. A scheduled inventory comparison repairs events that were never delivered. Keep the event log, refresh job, and site deployment identities separate so an operator can tell which transition failed.
CMS webhooks: handle duplicate, reordered, and missing publication events
Operational decision
A handbook entry is published at revision 72, edited at 73, and unpublished at 74. The 72 webhook arrives twice; the 73 webhook arrives after the 74 event. A worker stores each accepted event ID once, fetches the record's current CMS state, and computes the route action from revision 74 instead of blindly applying the event payload. If the record is now unpublished, it removes or expires the public route under the publishing contract. Each night, a reconciliation job compares the CMS list of published IDs with the site's route inventory and queues repairs for discrepancies. The operator tests a lost webhook, a repeated webhook, and an out-of-order pair before shipping the integration.
CREATE TABLE cms_event_receipts (
event_id text PRIMARY KEY,
record_id text NOT NULL,
accepted_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO cms_event_receipts (event_id, record_id)
VALUES ('evt-7284', 'lesson-47')
ON CONFLICT (event_id) DO NOTHING;Cost and verification
The receipt insert has indexed lookup cost near O(log N) for N stored events and storage proportional to retained event IDs. The SQL only deduplicates transport events; it does not decide which revision is public or atomically update a remote site. The worker still must fetch current state, compare revisions, and reconcile uncertain deployments. Keep receipts at least as long as the replay horizon, then bound table growth. Watch event age, signature failures, refresh failures, and inventory differences; webhook success alone cannot prove a live route changed.
Common Mistakes
- Do not apply a late publish event over a newer unpublish.
- Do not use only in-memory event deduplication.
- Do not treat an HTTP acknowledgement as proof that the public route changed.
Connected lessons
- DevOps: delivery, infrastructure, and reliable operations
- Headless CMS publishing: define which records become public routes
- Transactional outbox: commit business state and event intent together
- Serverless event idempotency: commit the effect and receipt together
