Do not depend on webhook order. Providers can deliver events in any sequence, and the same event more than once. Track event IDs to drop duplicates, and treat each event as a hint to fetch the current object from the API. Your state should come from the object, not from arrival order.
What the documentation says
One payment provider states plainly that it does not guarantee delivery of events in the order they were generated. Its example is a new subscription, which can produce a subscription created event, an invoice created event, an invoice paid event and a charge created event. Your handler might see the invoice paid event first.
The same page says the created timestamp on an event is recorded in seconds, so distinct events can share a timestamp, and it tells you not to use it to decide order or to detect duplicates. Track event IDs instead. Its go-live checklist adds three requirements for a production endpoint: handle delayed notifications, handle duplicates, and do not require events to occur in a specific order.
Three habits that make order irrelevant
- Dedupe by event ID. Store the ID of each processed event with a unique constraint. If the insert fails, you have seen it.
- Fetch, then decide. When an invoice paid event arrives before you know the subscription, call the API for the subscription and invoice. The documentation suggests exactly this for the case where one event arrives first.
- Make handlers idempotent. Setting an order to paid twice should leave the same result as setting it once.
A simple state rule
If your system has a status such as pending, paid and refunded, define which transitions are allowed and ignore the rest. A paid event that arrives after a refunded event should not reopen the order.
| Arrival | Naive handler | Safer handler |
|---|---|---|
| Paid, then created | Errors on missing record | Fetches the object, creates record, sets status |
| Same event twice | Sends two emails | Skips on known event ID |
| Refund before paid | Order ends as paid | Reads current object state and keeps refunded |
Do not trust the payload as the final word
An event payload is a snapshot from when the event was created. By the time you process it, the object may have moved on. Retrieving the resource gives you the up to date definition. That extra call costs little compared with a wrong order status, and it also protects against a handler that processed a stale snapshot after a newer one.
If your provider offers thin events that carry only an identifier, this pattern is already built in, because you must call the API to learn the details.
Pair it with signature checks and replay limits
Order handling does not replace authentication. Verify the signature on the raw body first, then apply timestamp tolerance, then dedupe. The steps are in how to verify a webhook signature with HMAC, and the replay side is in webhook replay and duplicate handling.
Testing it
Replay events out of sequence in a sandbox. Send the same event twice. Delay one for several minutes. If any run ends in a wrong state, the handler depends on order. Use the provider's CLI or test tools to fire events on demand.
Where Safeguard fits
Webhook handlers are ordinary application code with ordinary dependencies. Safeguard's dependency scanning and reachability analysis show whether a vulnerable library in your handler is actually reachable.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how a finding is judged by whether the code path can be reached, not by its presence alone.
Quick check
Can I sort webhook events by their created timestamp?
No. The cited documentation says distinct events can share a timestamp and that you should not use it to decide order or detect duplicates. Use event IDs for duplicates and the API for current state.
What is the simplest fix for out of order events?
Treat every event as a notification, fetch the object it refers to, and update your records from that object.
Sources
- Receive events in your webhook endpoint, Stripe documentation
- Go-live checklist, Stripe documentation
Last reviewed: 19 October 2026.