Delivery guarantees
Return a
2xx as soon as you have persisted the event, then process it
asynchronously. Doing the work before you respond risks a timeout, which
produces a retry and therefore a duplicate.
Retries
An attempt that returns a non-
2xx status, or that does not respond within 10
seconds, is retried up to 5 times at 30-second intervals. After the last retry
the event is not delivered again — there is no manual replay, so treat a
sustained failure as data loss and reconcile from the API.
Every attempt for the same event carries the same event id, so retries are
safe to collapse. See Deduplicating events.
Endpoint auto-disable
After 20 consecutive failed deliveries, the destination is automatically disabled and stops receiving events. This protects both sides from indefinite retry traffic against a dead endpoint. The counter is consecutive: a single successful delivery resets it. Re-enable a disabled webhook from the Events & Endpoints list in the Developer Area, or by re-subscribing via the API. Events that occurred while the endpoint was disabled are not backfilled — reconcile the gap from the relevant API endpoints.Deduplicating events
Every delivery carries an event id:- On the Standard-Webhooks envelope events
(
creator.*,checkout_link.*,app.*,payout.*) it is the top-levelid, also sent as thewebhook-idheader. - On the legacy flat events it is the
eventIdfield appended to the payload body.
- Every delivery attempt for the same event carries the same id, so collapsing on it removes retries.
- Two genuinely distinct events carry different ids, even when the same fan and creator are involved, so collapsing on it does not remove real events.
Read receipts
Onmessage.read and creator.message.read the id is derived from the creator
and the fan only. Every read receipt in a conversation therefore carries one id
indefinitely, and a consumer keying on it records the first receipt and discards
every later one.
A read receipt reports a count for a conversation rather than a specific
message, so the payload carries no occurrence identifier to key on. Use the
timestamp as the occurrence component instead:
timestamp is set once when the event is emitted, not per attempt, so it
stays constant across retries while differing between two genuine receipts.
This is a known gap rather than the intended contract, and read receipts are
expected to gain an occurrence component in the id.
id + timestamp keeps
working either way, which is why it is the recommendation here.Repeated subscription state transitions
Subscription lifecycle events (creator.subscription.*,
app.subscription.*) key on the state transition rather than the occurrence. A
cancel and a later reactivation differ, but the same transition repeated inside
one billing period — cancel, reactivate, cancel again — reuses the earlier id, so
the second cancel is dropped and your copy of the subscription is left showing
auto-renew on.
If a subscription can change state more than once within a period, pair the id
with the timestamp on these topics as well, or read the current state back from
the API rather than tracking it from the event stream alone.