What "two-way" actually means
Two-way stock sync means changes in either system propagate to the other. For a given SKU, a receipt posted in Odoo increases the quantity on Shopify. A Shopify order that sells through decrements the quantity in Odoo. Both directions are live, not just one-way exports run on a schedule.
That sounds straightforward until two updates arrive for the same product at the same time. Without explicit rules about which direction controls what, both systems write to each other, and the quantity bounces between whatever value each side last pushed.
Why direction rules matter
The fix is to assign each event a single authoritative system. The two main event types map naturally to different owners:
Warehouse movements belong to Odoo. When a purchase order is received, goods are transferred between internal locations, or a stock adjustment is posted after a physical count, Odoo is the system of record. Those events should flow outward to Shopify, never the other way around.
Storefront sales belong to Shopify. When a customer checks out, Shopify owns that demand signal. The quantity reduction should push into Odoo, where it can be matched to a sales order or reservation.
Returns complicate this. A returned item may need an inspection step in Odoo before restocking. In that case, the Odoo-to-Shopify direction is correct — Shopify should not increase availability until Odoo confirms the item is back in sellable stock.
Getting these rules wrong is common. A connector that treats every Odoo stock change as authoritative will overwrite a Shopify sale that arrived while the sync was mid-run. A connector that always reads from Shopify will miss receipts posted in Odoo when no Shopify event triggered a pull. Both failure modes produce negative inventory or ghost availability.
Multi-warehouse location mapping
A single "default warehouse" assumption works for one fulfillment point. It breaks the moment a store has more than one.
Consider a merchant with a main distribution center, a 3PL warehouse handling large items, and a small retail location. Shopify represents these as three inventory locations. Odoo represents them as three warehouses or internal stock locations. When Odoo posts a receipt to the 3PL warehouse, the connector must know that this corresponds to the second Shopify location — not the main one.
Without explicit mapping, the connector has to guess. Most fall back to the default Shopify location, which means the 3PL stock never appears correctly on the storefront. Orders that Shopify thinks can be fulfilled from the default location are routed to the wrong warehouse.
Good location mapping also handles cases where a single Odoo warehouse feeds multiple Shopify locations (split shipping), or where a Shopify location aggregates stock from several Odoo internal locations. These are not edge cases — any merchant with a 3PL, a retail location, or a kit SKU with components in different bins will need them.
During setup, each Odoo warehouse or stock location should be matched explicitly to its Shopify location counterpart. That mapping is what makes a stock update from Odoo land in the right place on Shopify, and what makes a Shopify sale decrement the correct Odoo location.
Retry-safe processing with queues
Two-way sync that runs inline fails at scale. When Shopify starts rate-limiting mid-sync — and it will, once order volume picks up — inline API calls return errors that are either swallowed silently or crash the sync run. Either way, updates are lost and neither system has a record of the failure.
The solution is a job queue between the sync trigger and the API call. When Odoo posts a stock movement, the event creates a job. When a Shopify order arrives, it creates a job. Workers pick up jobs, make the API calls, and handle the result. If Shopify returns a rate-limit error, the job waits and retries with backoff. If the update succeeds, the job is marked complete. If it fails repeatedly, it is flagged for investigation rather than silently dropped.
This approach also handles bursts. A warehouse receiving 200 purchase order lines at once generates 200 stock update jobs. The queue absorbs the burst and the workers drain it steadily without hammering the Shopify API.
The same retry-safe model applies to the Shopify-to-Odoo direction. A Shopify order webhook creates a job. If the Odoo server is temporarily unreachable, the job retries when it comes back. The storefront sale is never lost.
Idempotency matters here too. A job that retries must produce the same result if it runs twice. For inventory updates, this usually means setting an absolute quantity rather than incrementing, so a duplicate run does not double the adjustment.
What SyncO does
SyncO handles both sync directions with configurable warehouse-to-location mapping, direction rules per shop, and queue-based processing for reliability under API rate limits. Product links between Shopify variants and Odoo products are stored as persistent identifiers so inventory jobs do not rely on name matching. For a full walkthrough of how the inventory sync is structured, see Shopify Odoo inventory sync.
Get started
Two-way stock sync that actually holds up in production requires direction rules, explicit location mapping, and retries baked in from the start. If you are managing inventory across Shopify and Odoo today, install SyncO from the Shopify App Store and connect both systems without the manual reconciliation.