Miko for Odoo
Shopify Connector for Odoo
Shopify and Odoo, in lockstep.
A Shopify store and an Odoo database each hold a version of the truth about the same orders, the same stock and the same customers. Keeping them in agreement by hand does not scale past the first busy week. This connector keeps both sides in step on a schedule, and is explicit about the cases where it would rather stop than guess.
It is built on Shopify's GraphQL Admin API, which Shopify has required for new apps since October 2024 and is replacing REST with. That matters more than it sounds: REST product endpoints stopped supporting more than 100 variants in February 2025 and no longer receive new features. A REST connector is a connector with a rewrite in it.
What syncs, and which way
Products and customers each run in, out or both. Orders come in; stock and fulfillment go back out. A push against a pull-only store is refused with a reason rather than done quietly.
| Record | Direction | Notes |
|---|---|---|
| Orders | Shopify → Odoo | Checked against the total Shopify actually charged |
| Products and variants | Both ways | Matched on exact option-set equality |
| Customers | Both ways | Required even for order-only imports |
| Stock | Odoo → Shopify | Available-to-promise, to one named location |
| Fulfillment and tracking | Odoo → Shopify | On delivery validation or invoice posting |
How it works
- Create the API credentials in your store. Read and write access to products, customers, orders and inventory.
- Add the store in Odoo and press Test Connection. It verifies the credentials, names the store back to you, and says exactly what to fix if anything is wrong.
- Import, then look at what arrived. Products, then customers, then orders. Nothing is confirmed or invoiced yet: those switches start off on purpose.
- Turn on the schedule and the write-back. Once you are happy with what came in, enable the scheduled sync, stock publishing and fulfillment individually.
What it will not do, which matters as much as what it will
Most connector damage is silent. These are the four cases where this one stops instead of producing a plausible-looking wrong result.
- It never imports the same order twice, whatever happens mid-sync. Identity goes through a mapping table before anything is written, so re-running a job converges on the same single order rather than creating a second one.
- It never invents a tax. Anything Shopify sends without an Odoo equivalent stops and asks, rather than quietly producing an invoice short by the tax.
- It never attaches a variant to a nearly-matching one. A near match is reported and left unlinked rather than silently bound to the wrong SKU.
- It checks every imported order against the total Shopify actually charged. Anything that disagrees stays a quotation, flagged, with the difference spelled out.
Orders, exactly as the storefront recorded them
- Line discounts are kept as discounts rather than folded into the price, so margin reports stay truthful.
- Shipping lines are imported as their own line.
- Guest checkouts are handled through a fallback customer you choose.
- Cancelled orders are imported for the record but never confirmed.
- The order date comes from Shopify, not from the moment of import.
Products and variants, without breaking history
- Shopify options become Odoo attribute lines. Attribute lines are only ever added, never removed, because removing one deletes variants that historical orders reference.
- A variant is matched on exact option-set equality.
- A barcode that already belongs to another product is skipped with a warning instead of aborting the whole catalog.
Stock and fulfillment, back to the storefront
- Available-to-promise is published by default, because publishing on-hand is how the same unit gets sold twice.
- Stock is written to one named location you choose. A store with several would otherwise have the wrong shelf updated silently.
- Items not yet stocked at that location are activated first, which Shopify requires and which every newly created product needs.
- Fulfillment fires when the delivery is validated, when the invoice is posted, or whichever happens first. Businesses that never run Inventory need the invoice trigger.
- Tracking number and carrier are passed across.
notifyCustomeris off by default: switching it on over a backlog emails real people about orders that shipped weeks ago.
When something fails, you are told what to fix
Sync failures are separated into the two kinds that need different handling.
- Failed is transient: a dropped connection, a rate limit, a 502. It is retried automatically with exponential backoff.
- Needs attention means something has to change before it can work: a tax with no mapping, a product not in Odoo, a revoked key. These are never retried on a timer, because a timer cannot fix them, and each one carries the sentence saying what to do.
- Every job keeps the exact payload that caused it, so a retry replays the failure without asking the store for the data again.
- There is a Retry button on every job, and a dashboard filtered to what still needs a person.
Rate limits, handled the way Shopify actually works
- Shopify limits query cost from a leaky bucket, not request count. The client reads what is left and waits for the refill rather than earning a 429 on a large catalog.
- Every GraphQL document in the module was executed against a real Shopify development store on API 2025-07, not only against test stubs.
Requirements
- Odoo 16, 17, 18 or 19, Community or Enterprise.
- The free E-Commerce Connector Engine, which Odoo pulls in automatically as a dependency.
- A Shopify custom app token with
read_products,write_products,read_customers,write_customers,read_orders,read_inventory,write_inventory,read_locationsandwrite_merchant_managed_fulfillment_orders. read_customersis required even if you only import orders, because an order carries its customer.- Odoo Online cannot run third-party modules that contain Python, which is Odoo's rule rather than ours. Odoo.sh and self-hosted installs are both fine.
Questions
Does this use the REST API that Shopify is retiring?
What happens if a sync is interrupted and I run it again?
Can I import orders without pushing anything back to Shopify?
Which Odoo versions are supported?
Does it work on Odoo Online?
How do I get help?
Other Miko apps for Odoo
Live on the Odoo App Store, published by Tripster Developers.