Odoo Shopify Integration: Automate Product, Order & Inventory Sync
Connect Shopify to Odoo for automated product sync, order import, and real-time inventory updates. Native connector, third-party apps, and n8n automation — all approaches compared with working code examples.
Why Integrate Odoo with Shopify
Shopify is excellent at what it does — storefront, checkout, and payment processing. Odoo is excellent at what it does — inventory, accounting, manufacturing, and CRM. The problem starts when you run both without a bridge: you end up manually exporting Shopify orders into Odoo, copy-pasting inventory counts back to Shopify, and spending hours reconciling discrepancies.
An Odoo-Shopify integration eliminates that manual work. Here is what it looks like in practice:
- •Product sync: Create or update a product in Odoo, and it appears in Shopify with the correct price, description, images, and variants. No manual data entry.
- •Order sync: A customer places an order on Shopify. Within seconds, a sale order is created in Odoo with the correct customer, line items, taxes, and shipping address.
- •Inventory sync: Stock levels in Odoo update automatically in Shopify. When inventory drops to zero, Shopify marks the product as out of stock. When a purchase order is received in Odoo, Shopify stock goes back up.
- •Customer sync: New Shopify customers are created as contacts in Odoo. Existing customers are matched by email to avoid duplicates.
The question is not whether to integrate, but how. There are four main approaches, each with different trade-offs in cost, complexity, and flexibility.
Integration Approaches Compared
Before diving into setup details, here is a comparison of every approach:
| Odoo Shopify Connector | Third-Party Apps | n8n / Make | Custom XML-RPC | |
|---|---|---|---|---|
| Cost | $150-300 one-time | $100-500 one-time or subscription | Free (self-hosted n8n) | Developer time only |
| Community Edition | No | Yes (most apps) | Yes | Yes |
| Enterprise Edition | Yes | Yes | Yes | Yes |
| Setup complexity | Low — install + configure | Low-Medium | Medium — workflow design | High — custom code |
| Product sync | Yes | Yes | Yes (build workflow) | Yes (build script) |
| Order sync | Yes | Yes | Yes (webhook-driven) | Yes (webhook or cron) |
| Inventory sync | Yes | Yes | Yes (event-driven) | Yes (cron-based) |
| Real-time sync | Polling (5-15 min) | Polling or webhooks | Webhooks (seconds) | Depends on implementation |
| Multi-store | Limited | Depends on app | Yes — multiple workflows | Yes — custom logic |
| Customizability | Module options only | Depends on app | Unlimited | Unlimited |
| Maintenance | Odoo S.A. / vendor | Third-party developer | You maintain workflows | You maintain code |
Quick recommendation
- •Small store, Enterprise Edition, simple needs — use the Odoo Shopify Connector. It works, it is maintained, and it covers the basics.
- •Community Edition, standard sync — pick a well-reviewed third-party app from the Odoo Apps Store or OCA.
- •Complex workflows, multi-channel, or real-time requirements — use n8n. It gives you webhook-driven sync with full control over data transformation and error handling.
- •Complete control or unusual requirements — build a custom integration with XML-RPC. More work upfront, but zero dependencies on third-party modules.
Native Odoo Shopify Connector
The Odoo Shopify Connector is available on the Odoo Apps Store. It provides a UI-driven setup for connecting your Shopify store to Odoo and syncing products, orders, customers, and inventory.
What it syncs
- •Products: Odoo → Shopify (name, description, price, images, variants, weight)
- •Orders: Shopify → Odoo (customer, line items, discounts, shipping, taxes)
- •Customers: Shopify → Odoo (name, email, phone, addresses)
- •Inventory: Odoo → Shopify (stock quantities per location)
- •Order status: Fulfillment and tracking info from Odoo → Shopify
Setup steps
Step 1: Create a Shopify custom app
In your Shopify admin, go to Settings → Apps and sales channels → Develop apps. Create a new app and configure the API scopes:
- •
read_products,write_products - •
read_orders,write_orders - •
read_inventory,write_inventory - •
read_customers,write_customers - •
read_fulfillments,write_fulfillments
After installing the app, copy the Admin API access token. You will need this in Odoo.
Step 2: Install the connector in Odoo
Install the Shopify Connector module from the Odoo Apps Store. Navigate to Connectors → Shopify → Instances and create a new instance with your Shopify store URL and the API access token from Step 1.
Step 3: Configure sync settings
Set up which direction each entity syncs, the polling interval, and field mappings. Most connectors default to importing orders from Shopify and exporting products/inventory from Odoo — which is the recommended pattern.
Step 4: Run initial import
Do an initial sync to pull existing Shopify orders and customers into Odoo. Depending on your store size, this can take a few minutes to several hours.
Limitations
- •Polling-based sync adds 5–15 minute delays — not true real-time
- •Limited error handling — sync failures often require manual intervention
- •Complex variant mapping (more than 3 attributes) requires workarounds
- •Multi-store setups (multiple Shopify stores to one Odoo) can be tricky to configure
Third-Party Connectors
The Odoo Apps Store has dozens of Shopify connector modules from various vendors. For Community Edition users, these are often the most practical option. Here is what to look for:
Key features to evaluate
- •Webhook support — modules that use Shopify webhooks (not just polling) give you near-real-time order import
- •Bidirectional product sync — some modules only import from Shopify; make sure it can push from Odoo to Shopify too
- •Variant and attribute mapping — does it handle Odoo product variants correctly, including images per variant?
- •Refund and cancellation handling — can it create credit notes and reverse stock moves?
- •Odoo version compatibility — check that it supports your exact Odoo version (17, 18, or 19)
- •Active maintenance — look for modules updated within the last 6 months with responsive support
OCA Shopify modules
The Odoo Community Association (OCA) maintains community-driven connector modules on GitHub. These are free, open-source, and peer-reviewed. The trade-off is that setup can be more involved and documentation varies. Check the OCA connector-ecommerce repository for available Shopify-related modules.
Watch out for abandoned modules. Search the Odoo Apps Store for “Shopify” and you will find 50+ results. Many are poorly maintained or incompatible with recent Odoo versions. Filter by your Odoo version, sort by downloads, and read the reviews before purchasing.
n8n / Make Automation Approach
If you need real-time sync, complex data transformation, or integration with additional services beyond just Odoo and Shopify, a workflow automation tool like n8n (self-hosted, free) or Make (cloud, paid) is the most flexible option. This section focuses on n8n since it can run on the same server as Odoo.
Architecture overview
The setup works like this: Shopify sends webhooks to n8n when orders are placed or products change. n8n transforms the data and pushes it to Odoo via XML-RPC. In the reverse direction, n8n polls Odoo for inventory changes (or listens to a custom Odoo webhook) and updates Shopify via its REST or GraphQL API.
Workflow 1: Shopify order → Odoo sale order
This is the most common integration. When a customer places an order on Shopify, n8n creates a corresponding sale order in Odoo with the customer, line items, shipping, and taxes.
{
"name": "Shopify Order → Odoo",
"nodes": [
{
"name": "Shopify Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"httpMethod": "POST",
"path": "shopify-order",
"responseMode": "onReceived"
}
},
{
"name": "Find or Create Customer",
"type": "n8n-nodes-base.odoo",
"parameters": {
"resource": "contact",
"operation": "getAll",
"filters": {
"filter": [
{
"fieldName": "email",
"operator": "=",
"value": "={{ $json.customer.email }}"
}
]
}
}
},
{
"name": "Create Customer If New",
"type": "n8n-nodes-base.odoo",
"parameters": {
"resource": "contact",
"operation": "create",
"fieldsToCreateOrUpdate": {
"fieldValues": [
{ "fieldName": "name", "fieldValue": "={{ $json.customer.first_name }} {{ $json.customer.last_name }}" },
{ "fieldName": "email", "fieldValue": "={{ $json.customer.email }}" },
{ "fieldName": "phone", "fieldValue": "={{ $json.customer.phone }}" },
{ "fieldName": "street", "fieldValue": "={{ $json.shipping_address.address1 }}" },
{ "fieldName": "city", "fieldValue": "={{ $json.shipping_address.city }}" },
{ "fieldName": "zip", "fieldValue": "={{ $json.shipping_address.zip }}" }
]
}
}
},
{
"name": "Create Sale Order",
"type": "n8n-nodes-base.odoo",
"parameters": {
"resource": "sale.order",
"operation": "create",
"fieldsToCreateOrUpdate": {
"fieldValues": [
{ "fieldName": "partner_id", "fieldValue": "={{ $json.partner_id }}" },
{ "fieldName": "client_order_ref", "fieldValue": "={{ $json.order_number }}" },
{ "fieldName": "note", "fieldValue": "Shopify Order #{{ $json.order_number }}" }
]
}
}
},
{
"name": "Add Order Lines",
"type": "n8n-nodes-base.odoo",
"parameters": {
"resource": "sale.order.line",
"operation": "create",
"fieldsToCreateOrUpdate": {
"fieldValues": [
{ "fieldName": "order_id", "fieldValue": "={{ $json.sale_order_id }}" },
{ "fieldName": "product_id", "fieldValue": "={{ $json.odoo_product_id }}" },
{ "fieldName": "product_uom_qty", "fieldValue": "={{ $json.quantity }}" },
{ "fieldName": "price_unit", "fieldValue": "={{ $json.price }}" }
]
}
}
}
]
}Workflow 2: Odoo product → Shopify
Push new or updated products from Odoo to Shopify. This workflow runs on a schedule (every 15 minutes) and checks for products modified since the last run.
{
"name": "Odoo Product → Shopify",
"nodes": [
{
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": { "interval": [{ "field": "minutes", "minutesInterval": 15 }] }
}
},
{
"name": "Get Modified Products",
"type": "n8n-nodes-base.odoo",
"parameters": {
"resource": "product.template",
"operation": "getAll",
"filters": {
"filter": [
{
"fieldName": "write_date",
"operator": ">",
"value": "={{ $now.minus({ minutes: 16 }).toISO() }}"
},
{
"fieldName": "sale_ok",
"operator": "=",
"value": "true"
}
]
}
}
},
{
"name": "Check Shopify Product Exists",
"type": "n8n-nodes-base.shopify",
"parameters": {
"operation": "getAll",
"resource": "product",
"filters": {
"handle": "={{ $json.default_code }}"
}
}
},
{
"name": "Create or Update Shopify Product",
"type": "n8n-nodes-base.shopify",
"parameters": {
"operation": "{{ $json.shopify_id ? 'update' : 'create' }}",
"resource": "product",
"title": "={{ $json.name }}",
"bodyHtml": "={{ $json.description_sale }}",
"vendor": "={{ $json.brand }}",
"productType": "={{ $json.categ_id.name }}",
"variants": "={{ $json.product_variant_ids }}"
}
}
]
}Workflow 3: Inventory sync (Odoo → Shopify)
Keep Shopify stock levels in sync with Odoo. This workflow queries Odoo for current stock and updates Shopify inventory levels.
{
"name": "Odoo Inventory → Shopify",
"nodes": [
{
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": { "interval": [{ "field": "minutes", "minutesInterval": 5 }] }
}
},
{
"name": "Get Odoo Stock Levels",
"type": "n8n-nodes-base.odoo",
"parameters": {
"resource": "product.product",
"operation": "getAll",
"fields": ["id", "default_code", "qty_available", "virtual_available"],
"filters": {
"filter": [
{ "fieldName": "type", "operator": "=", "value": "product" },
{ "fieldName": "sale_ok", "operator": "=", "value": "true" }
]
}
}
},
{
"name": "Map to Shopify Inventory",
"type": "n8n-nodes-base.set",
"parameters": {
"values": {
"string": [
{ "name": "sku", "value": "={{ $json.default_code }}" }
],
"number": [
{ "name": "available", "value": "={{ Math.max(0, $json.qty_available) }}" }
]
}
}
},
{
"name": "Update Shopify Inventory",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"method": "POST",
"url": "https://{{ $credentials.shopifyDomain }}/admin/api/2024-01/inventory_levels/set.json",
"headers": {
"X-Shopify-Access-Token": "={{ $credentials.shopifyAccessToken }}"
},
"body": {
"location_id": "={{ $json.shopify_location_id }}",
"inventory_item_id": "={{ $json.shopify_inventory_item_id }}",
"available": "={{ $json.available }}"
}
}
}
]
}Why n8n for Shopify integration? n8n runs as a Docker container alongside Odoo. With OEC.sh, you can deploy both on the same server, which means the n8n ↔ Odoo communication happens over localhost with zero latency. You also avoid paying for a cloud automation service — n8n self-hosted is free and handles thousands of workflow executions per day.
Custom XML-RPC Integration
For maximum control, you can build your own integration using Odoo's XML-RPC API and Shopify's REST/GraphQL API. This is the most work upfront but gives you complete ownership over the sync logic, error handling, and data transformation.
Shopify order → Odoo sale order (Python)
This script listens for Shopify order webhooks and creates a sale order in Odoo via XML-RPC:
import xmlrpc.client
import hmac
import hashlib
import json
from flask import Flask, request, abort
app = Flask(__name__)
# Odoo connection
ODOO_URL = "https://odoo.yourdomain.com"
ODOO_DB = "odoo_production"
ODOO_USER = "admin"
ODOO_PASSWORD = "your-api-key"
# Shopify webhook secret
SHOPIFY_WEBHOOK_SECRET = "your-webhook-secret"
# Authenticate with Odoo
common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common")
uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_PASSWORD, {})
models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object")
def verify_shopify_webhook(data, hmac_header):
"""Verify the webhook came from Shopify."""
digest = hmac.new(
SHOPIFY_WEBHOOK_SECRET.encode("utf-8"),
data,
hashlib.sha256,
).hexdigest()
import base64
computed = base64.b64encode(
hmac.new(
SHOPIFY_WEBHOOK_SECRET.encode("utf-8"),
data,
hashlib.sha256,
).digest()
).decode("utf-8")
return hmac.compare_digest(computed, hmac_header)
def find_or_create_partner(customer):
"""Find existing partner by email, or create a new one."""
email = customer.get("email", "")
if email:
partner_ids = models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"res.partner", "search",
[[["email", "=", email]]]
)
if partner_ids:
return partner_ids[0]
# Create new partner
return models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"res.partner", "create",
[{
"name": f"{customer['first_name']} {customer['last_name']}",
"email": email,
"phone": customer.get("phone", ""),
"customer_rank": 1,
}]
)
def find_product_by_sku(sku):
"""Find Odoo product by internal reference (SKU)."""
product_ids = models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"product.product", "search",
[[["default_code", "=", sku]]]
)
return product_ids[0] if product_ids else None
@app.route("/webhook/shopify/order", methods=["POST"])
def handle_order():
data = request.get_data()
hmac_header = request.headers.get("X-Shopify-Hmac-SHA256", "")
if not verify_shopify_webhook(data, hmac_header):
abort(401)
order = json.loads(data)
# Check for duplicate (idempotency)
existing = models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"sale.order", "search",
[[["client_order_ref", "=", str(order["order_number"])]]]
)
if existing:
return {"status": "duplicate", "order_id": existing[0]}, 200
# Find or create customer
partner_id = find_or_create_partner(order.get("customer", {}))
# Create sale order
sale_order_id = models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"sale.order", "create",
[{
"partner_id": partner_id,
"client_order_ref": str(order["order_number"]),
"note": f"Shopify Order #{order['order_number']}",
}]
)
# Add order lines
for item in order.get("line_items", []):
product_id = find_product_by_sku(item.get("sku", ""))
if not product_id:
# Create a placeholder product or log warning
continue
models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"sale.order.line", "create",
[{
"order_id": sale_order_id,
"product_id": product_id,
"product_uom_qty": item["quantity"],
"price_unit": float(item["price"]),
"name": item["title"],
}]
)
# Confirm the sale order
models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"sale.order", "action_confirm",
[[sale_order_id]]
)
return {"status": "created", "sale_order_id": sale_order_id}, 201
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)Push inventory from Odoo to Shopify (Python)
A cron script that reads current stock from Odoo and updates Shopify inventory levels:
import xmlrpc.client
import requests
ODOO_URL = "https://odoo.yourdomain.com"
ODOO_DB = "odoo_production"
ODOO_USER = "admin"
ODOO_PASSWORD = "your-api-key"
SHOPIFY_STORE = "your-store.myshopify.com"
SHOPIFY_TOKEN = "shpat_xxxxxxxxxxxxx"
SHOPIFY_LOCATION_ID = "12345678"
# Connect to Odoo
common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common")
uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_PASSWORD, {})
models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object")
# Get all stockable products with a SKU
products = models.execute_kw(
ODOO_DB, uid, ODOO_PASSWORD,
"product.product", "search_read",
[[
["type", "=", "product"],
["default_code", "!=", False],
["sale_ok", "=", True],
]],
{"fields": ["id", "default_code", "qty_available"]}
)
headers = {
"X-Shopify-Access-Token": SHOPIFY_TOKEN,
"Content-Type": "application/json",
}
for product in products:
sku = product["default_code"]
available = max(0, int(product["qty_available"]))
# Find inventory item in Shopify by SKU
resp = requests.get(
f"https://{SHOPIFY_STORE}/admin/api/2024-01/products.json",
headers=headers,
params={"fields": "id,variants", "limit": 1},
)
# In production, use a mapping table instead of
# searching Shopify by SKU on every run
# Update inventory level
resp = requests.post(
f"https://{SHOPIFY_STORE}/admin/api/2024-01/inventory_levels/set.json",
headers=headers,
json={
"location_id": int(SHOPIFY_LOCATION_ID),
"inventory_item_id": 0, # Replace with actual ID
"available": available,
},
)
if resp.status_code == 200:
print(f"Updated {sku}: {available} units")
else:
print(f"Failed {sku}: {resp.text}")Shopify GraphQL for bulk product export
For stores with many products, use Shopify's GraphQL API instead of REST. It is more efficient and lets you fetch exactly the fields you need:
mutation productCreate($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
handle
variants(first: 100) {
edges {
node {
id
sku
price
inventoryQuantity
}
}
}
}
userErrors {
field
message
}
}
}
# Variables:
# {
# "input": {
# "title": "Ergonomic Office Chair",
# "bodyHtml": "<p>Premium ergonomic chair...</p>",
# "vendor": "OfficePro",
# "productType": "Furniture",
# "variants": [
# { "sku": "CHAIR-BLK-L", "price": "299.00" },
# { "sku": "CHAIR-GRY-L", "price": "299.00" }
# ]
# }
# }Data Mapping: Odoo Fields ↔ Shopify Fields
Getting the field mapping right is the most important part of any integration. Here are the key mappings for products, orders, customers, and inventory.
Product fields
| Odoo Field | Shopify Field | Notes |
|---|---|---|
| product.template.name | product.title | Direct mapping |
| product.template.description_sale | product.body_html | Odoo uses plain text; Shopify accepts HTML |
| product.template.list_price | variant.price | Odoo stores base price on template; Shopify on each variant |
| product.template.default_code | variant.sku | Use as the unique key to match products |
| product.template.weight | variant.weight | Both use kg by default; check unit settings |
| product.template.categ_id.name | product.product_type | Odoo category → Shopify product type |
| product.template.image_1920 | product.images[] | Base64 in Odoo; URL or base64 in Shopify API |
| product.attribute.name | product.options[] | Shopify limited to 3 options per product |
| product.product (variant) | product.variants[] | Shopify limited to 100 variants per product |
| product.template.barcode | variant.barcode | Direct mapping |
Order fields
| Shopify Field | Odoo Field | Notes |
|---|---|---|
| order.order_number | sale.order.client_order_ref | Store Shopify order number for reference |
| order.customer.email | res.partner.email | Use to find existing customer |
| order.line_items[].sku | sale.order.line.product_id | Look up Odoo product by SKU |
| order.line_items[].quantity | sale.order.line.product_uom_qty | Direct mapping |
| order.line_items[].price | sale.order.line.price_unit | Check tax-inclusive vs exclusive |
| order.shipping_address | res.partner (delivery) | Create as child contact in Odoo |
| order.total_tax | sale.order tax lines | Map Shopify tax to Odoo fiscal position |
| order.discount_codes[] | sale.order.line discount | Apply as line-level discount % or fixed |
| order.shipping_lines[].price | sale.order.line (delivery) | Add as a service product line |
| order.financial_status | sale.order payment | paid = create payment; pending = leave open |
Tricky mappings to watch out for
- •Tax handling: Shopify can send prices tax-inclusive or tax-exclusive depending on your store settings. Odoo also supports both modes. Make sure they match, or you will have rounding discrepancies on every order.
- •Currency: If your Shopify store uses a different currency than your Odoo company, you need to handle conversion. Odoo supports multi-currency natively, but the exchange rate must be set correctly.
- •Variant limits: Shopify allows a maximum of 3 option types (e.g., Size, Color, Material) and 100 variants per product. Odoo has no such limit. If your Odoo products have more than 3 attribute types or 100 combinations, you need to split them into multiple Shopify products.
- •Shipping as a product: Odoo treats shipping charges as a sale order line with a service-type product. Create a “Shipping” product in Odoo and map Shopify shipping line items to it.
Common Issues and Troubleshooting
Duplicate orders in Odoo
This happens when your webhook handler does not check for existing orders before creating new ones. Shopify can send the same webhook multiple times (retries on timeout, network issues).
Fix: Always check client_order_ref for the Shopify order number before creating a sale order. If it already exists, return 200 without creating a duplicate. This is the idempotency pattern shown in the XML-RPC code example above.
Inventory counts do not match
The most common cause is timing: a Shopify order comes in but has not been synced to Odoo yet, so the inventory push from Odoo still shows the pre-order quantity.
- •Fix 1: Sync orders before syncing inventory. Always process the order import first so Odoo's stock levels reflect all Shopify orders.
- •Fix 2: Use Odoo's
virtual_availableinstead ofqty_available. Virtual available accounts for outgoing orders that have not shipped yet. - •Fix 3: Treat Odoo as the single source of truth. Never update Odoo stock based on Shopify data — only import orders, and let Odoo handle the stock moves.
Product images not syncing
Odoo stores images as base64-encoded data in the database. Shopify expects either a URL or base64 data in the API call.
Fix: When reading images from Odoo via XML-RPC, the image_1920 field returns base64. Send it to Shopify's images[].attachment field (also base64). For large catalogs, consider hosting images on a CDN and using URLs instead — this is significantly faster.
Shopify API rate limit errors (429)
You are making too many API calls too fast. Shopify returns a 429 Too Many Requests response.
Fix: Implement exponential backoff. When you get a 429, wait for the time specified in the Retry-After header. For bulk operations (initial product import, full inventory sync), use Shopify's GraphQL Bulk Operations API instead of individual REST calls.
Tax amount mismatch between Shopify and Odoo
Shopify and Odoo may calculate tax differently due to rounding, rate differences, or tax-inclusive vs. tax-exclusive pricing.
- •Verify both systems use the same tax rates
- •Set the Odoo fiscal position to match Shopify's tax behavior
- •For small rounding differences ($0.01), consider importing the Shopify total as-is and using a rounding account in Odoo
Webhook not reaching your server
- Verify SSL is valid — Shopify requires HTTPS for webhooks
- Check firewall rules — port 443 must be open for inbound requests
- Confirm the webhook URL is publicly accessible (not behind a VPN or private network)
- Check Shopify admin → Settings → Notifications → Webhooks for delivery failure logs
- If using n8n, make sure the workflow is activated (not just saved as draft)
Performance Considerations for High-Volume Stores
If your Shopify store processes hundreds or thousands of orders per day, the basic sync approaches described above will need optimization. Here is what to consider:
Batch processing
Instead of processing one order at a time, batch your Odoo XML-RPC calls. Use create with a list of records instead of calling create once per order. This reduces network round-trips and takes advantage of Odoo's database transaction batching.
# Instead of creating one order at a time:
for order in shopify_orders:
models.execute_kw(db, uid, pw, "sale.order", "create", [order])
# Batch create (much faster for 50+ orders):
order_vals = [
{
"partner_id": order["partner_id"],
"client_order_ref": order["shopify_number"],
"order_line": [
(0, 0, {
"product_id": line["product_id"],
"product_uom_qty": line["qty"],
"price_unit": line["price"],
})
for line in order["lines"]
],
}
for order in shopify_orders
]
sale_order_ids = models.execute_kw(
db, uid, pw, "sale.order", "create", order_vals
)Use Shopify GraphQL Bulk Operations for large catalogs
Shopify's REST API is limited to 250 items per page and 2 requests/second. For initial product imports or full catalog syncs, use the GraphQL Bulk Operations API, which lets you query the entire catalog in a single request and download the result as a JSONL file.
Queue and worker pattern
For stores with 500+ orders per day, do not process webhooks synchronously. Instead, receive the webhook, put the order data in a queue (Redis, RabbitMQ, or even a database table), and have a worker process pick them up. This way, your webhook endpoint always responds quickly (Shopify times out after 5 seconds), and you can process orders at whatever pace your Odoo instance can handle.
Inventory sync frequency
For high-volume stores, syncing inventory every 5 minutes may not be fast enough. Consider event-driven inventory updates: when a stock move is validated in Odoo (picking confirmed, purchase order received), immediately push the updated quantity to Shopify. You can do this with an Odoo automated action that calls a webhook, or with n8n polling Odoo's stock.move model for recently validated moves.
Server sizing
- •Low volume (up to 100 orders/day): 2 vCPU, 4 GB RAM is sufficient for Odoo + n8n
- •Medium volume (100–1,000 orders/day): 4 vCPU, 8 GB RAM, SSD storage
- •High volume (1,000+ orders/day): 8+ vCPU, 16+ GB RAM, dedicated PostgreSQL server, multiple Odoo workers
Deploy Your Integrated Odoo on OEC.sh
The infrastructure behind a Shopify integration matters. You need SSL for webhooks, a reverse proxy for routing, reliable uptime so you do not miss orders, and the ability to run n8n alongside Odoo. OEC.sh handles all of that.
Deploy Odoo on any cloud provider through OEC.sh and you get:
- •Valid SSL certificates automatically provisioned and renewed — required for Shopify webhook delivery
- •Odoo + n8n on the same server — Docker-based deployment makes adding n8n alongside Odoo straightforward, with zero-latency communication between them
- •Reverse proxy with proper webhook routing — Shopify webhooks reach your Odoo or n8n endpoints reliably
- •Any cloud provider — DigitalOcean, AWS, Google Cloud, Hetzner, or your own infrastructure
- •Automated backups — your Odoo database and filestore are backed up, so order data is never at risk
Use our Docker Compose Generator to create a production-ready Odoo + PostgreSQL configuration, then extend it with n8n for automation workflows. Or skip the setup entirely and let OEC.sh deploy and manage everything.
Ready to connect Shopify to Odoo?
OEC.sh deploys Odoo with SSL, webhooks, and n8n support out of the box — on any cloud provider you choose. Both Community and Enterprise supported.
- Free tier available
- No credit card required
- Webhook-ready infrastructure
Frequently Asked Questions
Does Odoo have a built-in Shopify connector?
Not exactly. Odoo does not ship a native Shopify module in the core codebase. However, Odoo S.A. offers the Odoo Shopify Connector as a separate app on the Odoo Apps Store. It supports product sync, order import, and basic inventory updates. For Community Edition, you will need a third-party module or a custom integration using n8n or XML-RPC.
Can I sync Shopify orders into Odoo automatically?
Yes. All four approaches covered in this guide support automatic order sync. The Odoo Shopify Connector and third-party modules typically poll Shopify on a schedule (every 5-15 minutes). n8n can use Shopify webhooks for near-real-time sync — when an order is placed in Shopify, a webhook fires immediately, n8n processes it, and creates the sale order in Odoo within seconds.
Which Odoo edition works with Shopify?
Both Community and Enterprise editions can integrate with Shopify. The official Odoo Shopify Connector from the Apps Store works with Enterprise. For Community Edition, use a third-party connector from OCA or the Apps Store, or build a custom integration with n8n or XML-RPC. The Shopify API does not care which Odoo edition you run — it only needs valid API credentials.
How do I handle inventory sync between Odoo and Shopify?
The recommended pattern is to treat Odoo as the source of truth for inventory. When stock levels change in Odoo (from purchases, manufacturing, or manual adjustments), push the updated quantities to Shopify. When a Shopify order comes in, import it into Odoo so the stock move is recorded. Avoid bidirectional inventory writes — that creates race conditions and phantom stock discrepancies.
What happens to Shopify orders during a sync failure?
Orders stay in Shopify regardless of sync status — nothing is lost. If your integration goes down, orders queue up in Shopify. When the connection is restored, connectors typically catch up by pulling all orders since the last successful sync (using Shopify's updated_at_min filter). With n8n, you can add error handling nodes that retry failed operations and send alerts via Slack or email.
Can I sync product variants and attributes?
Yes, but it requires careful mapping. Shopify uses a flat variant model (up to 3 options per product, 100 variants per product). Odoo uses a more flexible attribute/value system with no hard limit. When syncing, you need to map Odoo product attributes to Shopify options and Odoo variants to Shopify variants. Most connectors handle simple cases (size, color) well, but complex attribute combinations may need custom mapping logic.
Should I use Shopify webhooks or polling for order sync?
Use webhooks if possible. Shopify webhooks deliver order data within seconds of creation, giving you near-real-time sync. Polling (checking for new orders every X minutes) adds delay and consumes API quota. However, webhooks can occasionally miss events (network issues, endpoint downtime), so the best approach is webhooks as the primary mechanism with a periodic polling job as a safety net to catch anything that was missed.
How do I handle refunds and returns across both systems?
When a refund is issued in Shopify, your integration should create a credit note in Odoo and reverse the stock move if the item is returned. Most third-party connectors handle basic refunds. For partial refunds, restocking fees, or exchange workflows, you will likely need custom logic. In n8n, subscribe to Shopify's refunds/create webhook and build a workflow that creates the corresponding Odoo credit note via XML-RPC.
What is the API rate limit for Shopify?
Shopify uses a leaky bucket algorithm. For standard plans, you get 2 requests per second with a bucket size of 40. For Shopify Plus, it is 4 requests per second with a bucket of 80. For GraphQL (recommended for bulk operations), you get 50 points per second with a 1,000-point bucket. A single product update typically costs 1 REST call or 10 GraphQL points. Plan your sync frequency and batch sizes around these limits.
Can OEC.sh run both Odoo and n8n on the same server?
Yes. OEC.sh deploys Odoo using Docker containers, which makes it straightforward to add n8n as an additional service on the same server. Both services share the same reverse proxy and SSL certificate infrastructure. This is the most cost-effective setup for small to mid-size stores — you avoid paying for a separate n8n hosting service and keep latency between Odoo and n8n minimal.
Automate Your Odoo-Shopify Sync Today
Whether you use a connector module, n8n automation, or custom XML-RPC — a connected Odoo and Shopify eliminates manual data entry and keeps your inventory, orders, and customers in sync. Deploy on any cloud with OEC.sh.
Related Resources
n8n Automation for Odoo
Automate Odoo workflows with n8n — webhooks, data sync, and 400+ integrations.
Odoo WhatsApp Integration
Connect WhatsApp to Odoo for sales, support, and automated notifications.
Odoo Docker Compose Guide
Production-ready Docker Compose configurations for Odoo deployments.
Deploy Odoo Guide
Step-by-step guide to deploy Odoo on any cloud provider.
Docker Compose Generator
Generate production-ready Docker Compose files for Odoo deployments.
OEC.sh Pricing
See plans and pricing for managed Odoo deployments.