Real-time inventory sync for a multi-channel e-commerce retailer

- 4 h → under 2 s
- Inventory sync latency
- None after go-live
- Overselling
Owner-reported: these figures are reported by me, not independently audited. Client names are withheld.
The Challenge
A retailer selling the same stock on three channels kept selling items it no longer had. The cause was not demand or a bad supplier: it was a reconciliation process that ran on spreadsheets and left every channel hours behind the real stock count.
The client sold through Shopify, Amazon FBA and a custom headless React storefront. Each channel kept its own idea of how many units were on hand, and the only thing tying them together was a manual routine: export orders, reconcile the counts, and upload corrected stock levels as CSV files.
That routine created a data lag of about four hours. For most of the day the lag was an annoyance. In high-traffic windows it was a real cost: a product could sell out on one channel while the other two kept taking orders for it. Every oversold order meant a cancellation and an apology to a customer who had already paid.
The manual work was a cost of its own. Someone on the operations team had to run the reconciliation, and doing it more often only meant more hours spent moving files between systems.
Constraints
- Three systems, three APIs. Shopify, Amazon and the headless storefront each expose stock differently and each has its own rate limits. The sync had to respect all of them.
- Concurrency. Orders on different channels can arrive in the same second. Two updates racing each other could write the wrong number back, which is the exact failure the project was meant to remove.
- No new platform for the team to run. The operations team needed to see when something went wrong, not learn a new tool to keep the sync alive.
The Solution
I treated the problem as an event problem, not a reporting problem. A scheduled job that reconciles faster would still leave a window in which channels disagree. The goal was for every sale, on any channel, to move the stock count everywhere as soon as it happened.
Where the gap was
The order events already existed on every channel: each one knows the moment a checkout completes. What was missing was anything listening to them. The "true" stock number also lived nowhere. Each channel had its own copy, and the spreadsheet acted as the referee a few times a day.
Architecture
The design has three parts:
- Capture. Webhooks on each channel fire on checkout, so the middleware learns about a sale the moment it happens instead of hours later.
- One source of truth. A Redis store holds the current stock count. Every order event updates Redis first. Redis handles concurrent writes safely, which is what removes the race between two channels selling the same unit at the same time.
- Broadcast. Once Redis has the new count, the workflow pushes it to every sales channel through their APIs.
n8n orchestrates the whole flow. I chose it because the logic is mostly "receive an event, update a value, call three APIs", which n8n expresses clearly, and because the client could read the workflow later without reading a codebase.
What I built
- An event-driven n8n middleware that receives order webhooks from each channel.
- The Redis stock store that every update goes through before any channel is touched.
- Fan-out updates to all channels at once, with retry logic for API rate limits, so a busy period slows the sync down instead of breaking it.
- Slack alerts for sync failures. If an update still fails after its retries, the operations team hears about it in the channel they already use, with enough context to act.
Results
Owner-reported results after go-live:
- Inventory sync latency: 4 hours → under 2 seconds. A sale on one channel now changes the count on the others almost immediately.
- No overselling after go-live, including through the peak season that followed, the period that used to produce the most cancellations.
- The manual reconciliation is gone. The operations team no longer exports, reconciles and re-uploads stock files; they act on the occasional Slack alert instead.
What the client can do now that they couldn't before: sell through busy windows on every channel at once without one channel taking orders for stock the others have already sold.
Stack
- n8n: orchestrates the webhooks, the stock update and the fan-out to each channel.
- Redis: the single source of truth for stock levels; absorbs concurrent updates.
- Webhooks: order events from each sales channel, captured at checkout.
- Shopify and Amazon FBA APIs, plus the headless React storefront: the three channels kept in step.
- Slack: alerts when a sync fails after retries.
What I learned
- Fix the source of truth before the speed. Making the CSV routine run more often would have narrowed the gap without closing it. The real change was deciding that one store, not three channels, owns the number.
- Concurrency is the hard part, not the APIs. Calling three APIs is routine work. Making sure two simultaneous orders can't leave the count wrong is where the design effort went.
- Alerts belong where the team already works. A failed sync that nobody sees is worse than the old lag. Sending failures to Slack meant the system could fail loudly without anyone watching a dashboard.
What I'd do next
The same event stream can do more than keep counts in step: low-stock warnings before a product sells out, and a daily summary of sync activity for the operations team. Both build on the webhooks and the Redis store that are already in place.
Tell me about the process that's slowing your team down. I'll tell you honestly whether automation, Salesforce or an AI agent is the right fix.