Webhook Architecture for RevenueCat
Backend Engineering

Webhook Architecture for RevenueCat: A Backend Sync That Doesn’t Silently Drift

7 min read | RevenueCat · Architecture

A webhook handler that works in every manual test and then quietly drifts out of sync in production is one of the more common failure modes we see in subscription backends — not because the code is wrong exactly, but because it was written for the happy path and webhooks don’t reliably stay on it. This is the pattern we default to, built around three specific failure modes rather than general best-practice advice.

1. The Three Failure Modes This Is Built Around

1
Duplicate delivery
Webhooks can be delivered more than once for the same event. A handler that isn’t idempotent will double-process a renewal — harmless for entitlement state, expensive if it also triggers a downstream side effect like a notification or a revenue-reporting write.
2
Out-of-order delivery
Network retries mean a later event can arrive before an earlier one. A handler that blindly applies whatever arrives last can overwrite correct state with stale state.
3
Endpoint downtime
Your server has a bad ten minutes — a deploy, a database failover, whatever. Events sent during that window need to not simply vanish once your endpoint comes back up.

2. The Pattern

RevenueCat
Sends webhook
→
Thin receiver
Verify signature, enqueue, return 200 fast
→
Queue
Durable, retries on failure
→
Worker
Idempotent processing, keyed on event ID

The receiver’s only job is to verify the request is genuinely from RevenueCat, hand the event to a durable queue, and return success immediately — not to do the actual entitlement-sync work inline. This matters because RevenueCat (like most webhook senders) will retry a delivery that doesn’t get a fast success response, which is exactly the mechanism that produces duplicates if your handler is slow or flaky.

The worker that actually processes events off the queue does three things every time: checks whether it has already processed this specific event ID (idempotency), applies the entitlement change only if the event’s timestamp is newer than the last applied change for that subscriber (ordering), and lets the queue’s own retry mechanism handle transient failures rather than the webhook sender’s retry mechanism.

Gotcha

Don’t rely purely on webhooks as your only source of truth. Reconcile against RevenueCat’s own API on a schedule — hourly or daily, depending on how much drift you can tolerate — so a webhook that was silently dropped somewhere in transit doesn’t leave a subscriber’s entitlement wrong indefinitely.

This general pattern is the missing piece underneath two things we’ve covered elsewhere: it’s what makes the analytics pipeline in our RevenueCat analytics piece trustworthy rather than approximately correct, and it’s the exact discipline that matters most during the dual-write validation window in our iOS migration playbook.


Webhook handler feels fragile, or you’re building one from scratch?

Walk us through your current setup (or lack of one) and we’ll point to exactly where it’s likely to drift first.

Start the Conversation →

Engineering Insights

Latest from Syntaxa Studio.

Loading latest posts