Workflow AutomationOperationsn8nZapier

Webhooks arriving out of order? Fix the write, not the sort

Webhooks arrive out of order because sources like Stripe and Shopify document that they do not guarantee delivery order, and because automation platforms run webhook-triggered executions concurrently, so two runs for the same record can finish in the wrong sequence. Sorting by timestamp only addresses the first cause. The reliable fix is to treat the webhook as a signal, re-fetch the record from the source API, and guard the write with a per-record watermark so a late run cannot overwrite newer data.

Alexey YushkinFounder, GENERAL INFORMATICS3 min read

Webhooks arrive out of order because the systems sending them never promised an order. Stripe's documentation states that it does not guarantee delivery of events in the sequence they were generated, and Shopify's says the same for events within a topic and across topics for the same resource. The usual advice is to sort by a timestamp, which handles reordering that happens in transit. It does not touch the second reordering, the one that happens after delivery inside your own automation platform, where webhook runs execute concurrently and each run can see only its own payload. There is no queue to sort.

Order only matters when you overwrite state

Before fixing anything, look at the last step of the workflow. If it appends, order cannot hurt you. A row per event in a sheet, a message per event in Slack, a line per event in a log table: every event creates its own record, nothing is overwritten, and a late arrival is just a row with an earlier timestamp sitting lower in the file.

Order corrupts data only when the final step overwrites state. Setting a deal stage, updating a status field, writing a customer record that already exists, decrementing inventory. In those workflows the last write wins, and out-of-order delivery decides which write is last.

That split is the first thing to check because it tells you how much of this article applies. Roughly half the "my webhooks are out of order" threads describe an append pipeline where nothing was actually lost, just displayed in a confusing sequence. Sort the view, not the pipeline.

What the sources actually promise, and why the timestamp is not enough

Here is what the two most commonly integrated sources document, checked in August 2026.

SourceOrdering guaranteeField you can order byResolutionRetry window that can reorder
StripeNone. Documented explicitly, with a worked example of subscription events.event.created, or the object's own fields after re-fetching itOne secondUp to three days of exponential backoff in live mode
ShopifyNone, within a topic or across topics for the same resource. Their example is products/update arriving before products/create.X-Shopify-Triggered-At header, or updated_at in the payloadSub-second ISO 8601Eight retries over four hours
Most other appsUsually unstated, which means noneWhatever the payload happens to includeVaries, often none at allVaries

Two things in that table break the sort-by-timestamp advice.

The first is resolution. Stripe's event created field is a Unix timestamp measured in seconds. Two events generated in the same second carry identical values, and a subscription change routinely generates three or four events in the same second. You cannot order them with the field you were told to order by.

The second is the retry window. A first-attempt failure at your endpoint puts that event into a backoff schedule measured in hours. Stripe keeps retrying for up to three days. So the reordering is not a matter of milliseconds of network jitter, which is what most people picture. Event one can land two days after event five, long after your workflow has finished processing everything around it and long after any buffer you might have kept. That retry window is also the reason a webhook trigger recovers from downtime at all, which is the real axis in the webhook versus polling decision.

Even with a perfect field to sort on, sorting requires a buffer. You need events held together somewhere so they can be compared. A webhook trigger in Zapier, n8n, or Make does the opposite: it starts an independent run per event. Run one has no visibility into run two. There is nothing to sort.

The reordering your own platform adds after delivery

This is the part the vendor guides skip, and it is the part that produces the bug reports that make no sense.

Assume the source behaves perfectly and sends event A then event B, one second apart, both about the same customer. Your platform accepts both and starts two runs. Run A hits an enrichment step that takes eleven seconds because the API it calls was slow. Run B hits the same step and it returns in 400 milliseconds. Run B writes first. Run A writes second, with older data, and wins.

The source did nothing wrong. Your ordering was destroyed inside your own account.

This is the default behavior, not an edge case. n8n's concurrency control is disabled by default, and when you do enable it with N8N_CONCURRENCY_PRODUCTION_LIMIT, it applies only to production executions started from a webhook or trigger node, with anything over the limit queued in FIFO order. Zapier and Make similarly run webhook-triggered runs in parallel by design, since that is what makes them fast.

You can confirm this on your own instance in about two minutes. Open the execution history for a webhook-triggered workflow, find two runs for the same record ID, and compare the start time against the finish time for each. If a run that started first finished second, the ordering bug is already live in your account, whatever the source is doing.

The obvious fix is to set concurrency to one. Resist it. That limit is global rather than per record, so serializing to fix ordering for the 0.2 percent of events that collide also serializes the 99.8 percent that never touch the same record, and one slow run blocks everything behind it. You would be trading a correctness bug for a throughput ceiling.

The three shapes this takes in a real workflow

Named failures are easier to spot in your own build than an abstract description of a race.

The orphan. An update event beats a create event. Your workflow does its usual find-then-update, finds nothing, and the branch quietly does nothing at all. Then the create event arrives and writes the original, pre-update values. The updated data is not late. It is gone, and no step failed, so nothing alerted. This is Shopify's own documented example of the ordering they do not guarantee, and it is the most expensive of the three because the loss is permanent and invisible.

The rollback. Two update events for the same record process in reverse. An order that went paid then shipped gets written as shipped then paid, so your CRM says a shipped order is awaiting fulfillment. Somebody in operations ships it again. This one is at least visible, usually to a customer.

The resurrection. A delete event processes before a preceding update, so the record is removed and then the update recreates it as a partial record with whatever fields the payload happened to carry. You end up with a customer who was deleted for a compliance request and came back with half their data intact. If you handle deletion requests through automation, this failure has a regulatory cost attached to it, not just an operational one.

The fix: write re-fetched state, guarded by a watermark

Stop writing the payload. The payload is a snapshot of how the record looked when the event was queued, which may have been minutes or days ago. Treat the webhook as a signal that something changed and nothing more.

The build is four rules, in this order.

  1. Take only the record ID and the event type from the payload. Discard the rest. This single change removes most ordering damage, because it stops old field values from ever entering your workflow.
  2. Re-fetch the record from the source API as your first real step. Now both runs, whatever order they execute in, read the same current state and write the same current values. Stripe's own webhook documentation points at this, noting you can use the API to retrieve the objects an out-of-order event left you missing.
  3. Compare before you write. If the destination already holds the value you are about to write, skip the write. This is the same gate that stops a two-way sync from overwriting good data, and it kills the timestamp churn that makes ordering problems compound.
  4. Keep a watermark per record. Store the source's updated_at alongside the record in your destination. On every run, compare the freshly fetched updated_at against the stored one and skip the write when it is not newer. A run that was delayed by an hour now arrives, discovers the destination already reflects newer state, and does nothing. That is the behavior you want from a late event.

Two caveats worth stating. Re-fetching roughly doubles your read calls against the source API, so on a chatty topic add a dedupe gate on record ID within a short window before the fetch step. And deletes are the exception, because there is nothing to re-fetch: use a deleted_at flag rather than a hard delete, and let a scheduled reconciliation clean up later.

Note what this approach does not require. No sorting, no buffering, no shared queue, no concurrency limit, and no per-vendor timestamp field. It works the same whether the source is Stripe with documented ordering behavior or an internal tool that sends a bare JSON body with no timestamp at all.

What to do next

Pick your highest-volume webhook workflow and answer one question: does the final step append or overwrite? If it appends, you are done, and you can stop reading about ordering. If it overwrites, add the watermark first, before the re-fetch. It is the cheaper of the two changes, it takes one extra field in the destination and one filter step, and it converts the worst version of this failure, silent permanent data loss, into a skipped write you can count.

Then re-fetch. If you want the pattern applied across an existing set of integrations rather than one workflow at a time, that is the kind of audit we run as part of workflow automation systems work, and you can get in touch with the list of sources you receive webhooks from.

Frequently Asked Questions

SOURCES & CITATIONS

  1. Receive Stripe events in your webhook endpoint Stripehttps://docs.stripe.com/webhooks
  2. Webhooks, App development Shopifyhttps://shopify.dev/docs/apps/build/webhooks
  3. Control concurrency, n8n hosting docs n8nhttps://docs.n8n.io/deploy/host-n8n/configure-n8n/scaling/control-concurrency
  4. The Event object, Stripe API reference Stripehttps://docs.stripe.com/api/events/object

About Alexey Yushkin

Alexey is the founder of GENERAL INFORMATICS LLC. He designs and ships AI and automation systems for businesses and operators across the US.

Connect on LinkedIn

Related reading

Want this kind of system in your business?

We build practical AI and automation systems for operators. Send us your current workflow and we will show you what to automate first.

Request a Workflow Review