Syncing Shopify Orders Into Odoo
The problem
You sell on Shopify. Your business runs on Odoo.
Orders need to be in both, and re-typing them is neither fast nor accurate.
Three things have to happen for each order:
The customer must exist in Odoo — as an existing record if they have bought before, a new one if not.
The products must be matched to Odoo products.
The order itself must be created, with the right lines, prices and customer.
And it must not create duplicates, however many times the workflow runs.
Webhook, not polling
Shopify can call a URL when an order is placed. That is a webhook, and it is the right trigger here.
Why not poll on a schedule?
Delay. Polling every fifteen minutes means orders sit for up to fifteen minutes. For stock allocation and picking, that matters.
Wasted runs. Most polls find nothing. On usage-priced platforms you pay for those.
Missed edge cases. Polling by “modified since” is easy to get subtly wrong, and the failure is silent.
Webhook is immediate and only fires when something happened.
One thing it requires: your n8n instance must be reachable from the internet, over HTTPS.
FIGURE 1: THE ORDER SYNC CHAIN
Shopify webhook
- Order placed, n8n called
Check the order
- Already in Odoo?
Match the customer
- Find or create
Create the order
- With matched product lines
Preventing duplicates
The single most important part of this workflow.
Webhooks can fire more than once. Networks retry. A workflow can be re-run during testing. Somebody can replay a webhook while debugging.
Without a duplicate check, each of those creates another order in Odoo.
The pattern:
Store the Shopify order reference on the Odoo order — a field holding the source order number or ID.
Before creating anything, search Odoo for that reference.
If it exists, stop. Log it and finish successfully. This is not an error — it is the check working.
If it does not, proceed.
Match on the Shopify order ID, not the order name. Names can be reformatted; IDs do not change.
Matching customers
Second most important, and the source of most data quality problems.
Same pattern: search first, create only if not found.
What to match on:
Email is the usual choice. Reasonably stable and usually present.
A customer ID from Shopify is better if you store it on the Odoo partner — it survives an email change.
Not the name. “J Smith”, “John Smith” and “john smith” are the same person and three records.
Two things worth deciding in advance:
Guest checkouts. Shopify allows orders without an account. Create a partner anyway, or attach to a generic one? Decide before go-live.
Existing customers who also buy in person. If somebody exists in Odoo from an in-store sale and then orders online, will your match find them? If it matches on email and the in-store record has none, it will not — and you get a duplicate.
FIGURE 2: THE THREE CHECKS THAT DECIDE WHETHER THIS WORKS
Order already imported?
- Match on the source order ID. Prevents duplicate orders.
Customer already exists?
- Match on email or a stored source ID. Never on name.
Product matched?
- By SKU. Decide what happens when there is no match.
What about guest checkouts?
- Decide before go-live, not when the first one arrives.
Matching products
SKU is the usual link. The Shopify SKU matches an Odoo internal reference.
Which requires them to actually match. In practice they often do not — different formats, extra characters, products that exist in one system and not the other.
Decide what happens when a product cannot be matched:
Fail the order and alert somebody. Safest, and it forces the data to be fixed.
Create the order with a placeholder line and flag it. Keeps the order flowing, but somebody must act on the flag.
Create the product in Odoo. Convenient, and it risks a mess if SKUs are inconsistent.
Most businesses should fail and alert, at least initially. It surfaces the SKU mismatches that would otherwise accumulate quietly.
What else to map
Beyond customer and products, decide how to handle:
Shipping. A product line, or a delivery charge field?
Discounts. Line-level, or an order-level discount?
Taxes. Does Shopify’s tax match Odoo’s tax configuration? Check this carefully — mismatched tax produces accounting problems that surface at filing time.
Payment status. Paid in Shopify — does that mean paid in Odoo, or a pending payment to reconcile?
Order status. Which Odoo state does a new order arrive in? Draft for review, or confirmed?
That last one is a business decision, not a technical one. Confirming automatically is faster; arriving as a draft gives somebody a chance to look.
Failure handling
Orders are money. Silent failure is not acceptable here.
Four things:
Retry on temporary failures. Odoo restarting, a brief network problem.
Alert on permanent failures. A product that will not match, a malformed address. A named person, immediately.
Never lose the order. If it cannot be created, the data must be recoverable — logged, queued, or held somewhere a person can act on.
Reconcile periodically. Compare Shopify order counts against Odoo for the period. If they differ, something failed quietly and you want to know before month-end.
FIGURE 3: A SYNC THAT WORKS AND ONE THAT CAUSES CLEANUP
Working
- Duplicate check on source order ID
- Customers matched on email or stored ID
- Unmatched products fail loudly
- Counts reconciled periodically
Causing cleanup
- No duplicate check — replays create copies
- Customers matched on name
- Unmatched products silently skipped
- Nobody notices missing orders until month-end
Testing before go-live
Test these specifically:
- A new customer
- An existing customer
- The same webhook fired twice — you should get one order
- An order with a product that does not exist in Odoo
- A guest checkout
- An order with a discount
- An order with shipping
- A refund or cancellation, if you handle those
The duplicate test is the one that matters most. Fire the same order twice and confirm you get one record. If you get two, the workflow is not ready.
The short version
Use a webhook, not polling. Orders should arrive immediately.
Check for duplicates on the source order ID before creating anything. Webhooks fire more than once, and without this check you get copies.
Match customers on email or a stored source ID, never on name. Match products on SKU, and decide loudly what happens when a match fails.
And reconcile counts periodically. A quietly missing order is the failure you will not notice until it matters.
Retyping online orders into your ERP?
Get in touch. We build Shopify to Odoo order sync with duplicate prevention and proper customer matching — the two things that decide whether it stays clean.