The Integration That Every Growing E-commerce Business Needs
If you are selling on Shopify and managing operations in Odoo, you have probably hit the point where manual data entry between the two systems is eating your team alive. Copy-pasting orders from Shopify to Odoo, manually updating inventory, and reconciling payments across systems is not just tedious. It is error-prone and it does not scale.
I have built Shopify-Odoo integrations for over 20 clients, ranging from a single Shopify store doing 50 orders a day to multi-store setups processing 2,000+ daily orders. Here are the patterns that work, the mistakes to avoid, and the architectural decisions that determine whether your integration is a reliable asset or a constant headache.
Choosing Your Sync Strategy
The first architectural decision is how data flows between Shopify and Odoo. There are two approaches, and the right choice depends on your volume and requirements.
Real-Time (Webhook-Driven)
Shopify sends webhooks for events: order created, order paid, fulfillment created, product updated, refund issued. Your integration listens for these events and processes them immediately.
# Webhook handler for Shopify order creation
@app.post("/webhooks/shopify/orders/create")
async def handle_order_created(request: Request):
# Step 1: Verify webhook authenticity
if not verify_shopify_hmac(request):
raise HTTPException(status_code=401)
payload = await request.json()
# Step 2: Enqueue for processing (never process inline)
await queue.enqueue(
"process_shopify_order",
order_id=payload["id"],
shop_domain=request.headers["x-shopify-shop-domain"],
payload=payload
)
# Step 3: Return 200 immediately
return {"status": "queued"}
Use real-time when: You need up-to-the-minute inventory accuracy, your fulfillment team processes orders continuously, or your customers expect instant order confirmation emails from Odoo.
Batch (Polling-Based)
A scheduled job runs every 5-15 minutes, queries Shopify's API for new or updated records, and processes them in bulk.
Use batch when: You process orders in batches (morning and afternoon runs), real-time is not worth the infrastructure complexity, or you are dealing with Shopify API rate limits on a high-volume store.
My Recommendation
Start with webhooks for orders and refunds (time-sensitive), and use batch sync for product updates and inventory reconciliation (less time-sensitive). This hybrid approach gives you speed where it matters and simplicity where it does not.
The Five Data Flows You Need to Get Right
1. Orders: Shopify to Odoo
This is the core flow. Every Shopify order should create a corresponding sale order in Odoo. The devil is in the details:
- Customer matching: Match Shopify customers to Odoo contacts by email. Create new contacts for first-time buyers. Handle guest checkouts (Shopify allows orders without accounts).
- Product matching: Map Shopify product variants to Odoo products by SKU. Maintain a mapping table for products where SKUs differ between systems.
- Tax handling: Shopify calculates taxes at checkout. Import the tax amounts as-is rather than recalculating in Odoo. Recalculation leads to penny-rounding differences that will drive your accountant crazy.
- Discounts: Shopify discount codes should be mapped to Odoo pricelists or recorded as order-level discounts. Decide this early.
async def create_odoo_sale_order(shopify_order: dict) -> int:
# Find or create customer
partner_id = await match_customer(
email=shopify_order["email"],
name=shopify_order["shipping_address"]["name"],
address=shopify_order["shipping_address"]
)
# Build order lines
lines = []
for item in shopify_order["line_items"]:
product_id = await match_product_by_sku(item["sku"])
lines.append((0, 0, {
"product_id": product_id,
"product_uom_qty": item["quantity"],
"price_unit": float(item["price"]),
"discount": calculate_line_discount(item),
"tax_id": [(6, 0, [])], # Tax imported as total, not per-line
}))
# Create sale order
order_id = odoo.create("sale.order", {
"partner_id": partner_id,
"order_line": lines,
"x_shopify_order_id": shopify_order["id"],
"x_shopify_order_number": shopify_order["order_number"],
})
return order_id
2. Inventory: Odoo to Shopify
Inventory should flow from Odoo (your source of truth) to Shopify. Never let Shopify be the inventory master if you sell on multiple channels.
Critical rule: Update Shopify inventory levels only from Odoo. If inventory is adjusted in both systems, you will have conflicts and overselling. Use Odoo's inventory adjustment features and push changes to Shopify.
Update frequency depends on your business. A store selling 10 orders per day can update inventory every 15 minutes. A store doing flash sales with limited inventory needs real-time updates.
3. Products: Bidirectional (Carefully)
Product data is the most complex sync because both systems have legitimate reasons to be the source:
- Odoo is master for: SKU, cost, weight, supplier info, BOM
- Shopify is master for: SEO titles, product descriptions, images, collections
Sync product core data from Odoo to Shopify. Let your marketing team manage Shopify-specific content directly in Shopify. Trying to manage Shopify SEO titles from Odoo is a poor user experience for everyone.
4. Fulfillment: Odoo to Shopify
When an order is shipped from Odoo (delivery order validated), push the fulfillment and tracking number back to Shopify. This triggers Shopify's shipping confirmation email to the customer.
async def push_fulfillment_to_shopify(odoo_delivery: dict):
shopify_order_id = odoo_delivery["x_shopify_order_id"]
tracking_number = odoo_delivery["carrier_tracking_ref"]
await shopify_client.post(
f"/orders/{shopify_order_id}/fulfillments.json",
json={
"fulfillment": {
"tracking_number": tracking_number,
"tracking_company": odoo_delivery["carrier_id"]["name"],
"line_items": build_fulfillment_lines(odoo_delivery)
}
}
)
5. Refunds: Shopify to Odoo
Refund sync is where most integrations break down. Shopify supports partial refunds, restocking refunds, and refunds without restocking. Your integration needs to handle all three:
- Full refund with restock: Create a credit note in Odoo + return the inventory
- Partial refund with restock: Create a partial credit note + return specific items
- Refund without restock: Create a credit note only (damaged goods, goodwill refunds)
Test refund scenarios extensively. An incorrect refund sync can create accounting discrepancies that take hours to untangle.
Handling Flash Sales and High Volume
Flash sales are the stress test for any integration. A thousand orders in 30 minutes will expose every weakness in your architecture. Here is how to survive them:
Pre-Sale Inventory Buffer
Before a flash sale, push a reduced inventory count to Shopify. If you have 500 units, show 480 on Shopify. This buffer absorbs timing differences between when a Shopify order is placed and when Odoo decrements inventory.
Queue Depth Monitoring
During high volume, your processing queue will grow. Monitor queue depth and processing lag. If the queue exceeds 5 minutes of backlog, trigger an alert. You may need to temporarily switch to batch processing to keep up.
Idempotent Processing
Shopify may send the same webhook multiple times. Your order processing must be idempotent. Always check if a Shopify order ID already exists in Odoo before creating a new sale order.
Error Handling Patterns
Integrations fail. Networks time out, APIs return errors, and data has unexpected formats. Here is how to build resilience:
- Retry with backoff: Failed API calls retry 3 times with exponential backoff (1s, 4s, 16s)
- Dead letter queue: After 3 retries, move the failed item to a dead letter queue for manual review
- Daily reconciliation: Run a nightly job that compares Shopify orders from the last 24 hours against Odoo sale orders. Flag any discrepancies
- Alerting: Send notifications for critical failures (order sync failure, inventory mismatch > 10 units)
Common Mistakes
After 20+ implementations, these are the mistakes I see most often:
- Processing webhooks synchronously: Always enqueue and process asynchronously. Shopify expects a 200 response within 5 seconds.
- Not verifying webhook signatures: Security aside, unverified webhooks mean you cannot trust the data.
- Syncing inventory from Shopify to Odoo: This creates circular updates. Odoo should be the single source of truth.
- Ignoring Shopify's API rate limits: 40 requests per app per store per minute (REST API). Plan your batch operations accordingly.
- Hardcoding mappings: Store SKU mappings, tax mappings, and category mappings in configuration, not code. They will change.
Getting Started
If you are running Shopify + Odoo and struggling with manual processes, a well-built integration can transform your operations. I have helped businesses go from spending 3 hours daily on manual order entry to fully automated processing with exception-based handling.
Check out our Shopify integration services or reach out directly to discuss your specific setup.
Dealing with Shopify-Odoo sync headaches? Contact me for a free integration assessment. I will review your current setup and recommend the right architecture for your volume and requirements.
Written by
Muhammad Amir
ERP architect and technical consultant with 8+ years of experience building enterprise systems. Founder of ECOSIRE Private Limited. Specializes in Odoo ERP, marketplace integrations, and AI-powered business automation.
Related Articles
Marketplace Connector Architecture: Patterns from Building 35+ Integrations
A technical deep-dive into connector architecture for marketplace integrations — queue-based processing, idempotency, error handling, retry strategies, and lessons learned from 35+ real-world connectors.
8 min readodooMigrating from SAP to Odoo: A Complete Guide from Someone Who Has Done It
A step-by-step guide to migrating from SAP to Odoo ERP, covering data mapping, parallel running, testing, training, and go-live — based on real experience migrating a 200+ employee manufacturer.
7 min readodooOdoo 19: What's New and What It Means for Your Business
A practical breakdown of Odoo 19's biggest features — new UI, improved manufacturing, enhanced PoS, better reporting, and Knowledge app upgrades — and which businesses benefit most from upgrading.
5 min read