Shopify to Odoo Order Import: Complete Automation Guide
Every Shopify order that requires manual re-entry into Odoo is a liability — a chance for data entry errors, a delay in fulfillment processing, and a drain on staff time that compounds with every additional order. For a store processing 200 orders per day, manual order import consumes roughly 3–4 hours of staff time daily and introduces an error rate of 2–5% that cascades through inventory, shipping, and accounting.
Automating the Shopify-to-Odoo order pipeline eliminates this bottleneck entirely. Orders arrive in Odoo within seconds of placement, customer records merge automatically, payment statuses sync in real-time, fulfillment updates write back to Shopify with tracking numbers, and error conditions are caught and queued for resolution rather than silently dropped.
This guide covers the complete order import automation — from the Shopify webhook that starts the process through the fulfillment writeback that closes the loop. Every step includes the specific data mappings, edge cases, and failure modes you need to handle for a production-grade implementation.
Key Takeaways
- Shopify webhooks provide real-time order events — orders/create, orders/updated, orders/cancelled — that trigger the import pipeline
- Customer sync uses email as the primary match key, with automated creation for new customers and field merging for existing ones
- Payment status mapping translates Shopify's payment states (authorized, paid, partially_paid, refunded) into Odoo payment records
- Product matching connects Shopify line items to Odoo products using SKU as the primary identifier — variant-level matching is essential
- Fulfillment writeback sends Odoo delivery confirmation back to Shopify with tracking numbers, carrier information, and per-line-item fulfillment data
- Partial fulfillments (split shipments from multiple warehouses) require line-item-level fulfillment tracking, not order-level
- Error handling must include retry queues, dead letter processing, and alerting — silent failures are the most dangerous failures
- Idempotency keys (Shopify order ID) prevent duplicate orders from webhook retries
Architecture: The Order Import Pipeline
The complete order import pipeline has six stages, each with specific data transformations and failure modes:
Stage 1: Webhook Reception
Shopify → orders/create webhook → Integration endpoint
Stage 2: Customer Resolution
Find or create Odoo customer from Shopify customer data
Stage 3: Product Matching
Map Shopify line items to Odoo products by SKU/variant
Stage 4: Order Creation
Create Odoo sale order with lines, taxes, shipping, discounts
Stage 5: Payment Recording
Record payment status based on Shopify financial_status
Stage 6: Fulfillment Writeback
Odoo delivery → Shopify fulfillment with tracking
Each stage is a distinct operation that can fail independently. A robust implementation processes these as a pipeline with checkpointing — if Stage 4 fails, stages 1–3 are not re-executed on retry.
Stage 1: Receiving Shopify Order Webhooks
Shopify webhooks are the foundation of real-time order import. When an order event occurs, Shopify sends an HTTP POST request to your registered endpoint with the complete order payload.
Required Webhook Subscriptions
| Webhook Topic | Purpose | When It Fires |
|---|---|---|
| orders/create | New order placed | Customer completes checkout |
| orders/updated | Order modified | Payment captured, fulfillment added, edits made |
| orders/cancelled | Order cancelled | Merchant or customer cancels |
| refunds/create | Refund issued | Full or partial refund processed |
Webhook Verification
Every incoming webhook must be verified using Shopify's HMAC-SHA256 signature. The webhook payload is signed with your app's shared secret — verify the signature before processing. Unverified webhooks should be rejected with a 401 response.
Handling Webhook Delivery Guarantees
Shopify guarantees at-least-once delivery, meaning the same webhook may be sent multiple times — after network timeouts, during Shopify's retry cycle, or during incident recovery. Your endpoint must be idempotent: receiving the same orders/create webhook twice must not create two Odoo orders.
Implementation pattern: Use Shopify's order ID as an idempotency key. Before creating an Odoo order, check whether an order with that Shopify reference already exists. If it does, skip creation and return a 200 response so Shopify stops retrying.
Response Time Requirements
Shopify expects a 200 response within 5 seconds. If your endpoint takes longer, Shopify marks the delivery as failed and retries. For this reason, the webhook endpoint should accept the payload, validate the HMAC signature, enqueue the order for asynchronous processing, and return 200 immediately. The actual order creation happens in a background worker.
Stage 2: Customer Resolution
Every Shopify order includes customer data — email, name, phone, shipping address, and billing address. The integration must resolve this against Odoo's contact database.
Customer Matching Algorithm
The recommended matching logic follows this priority sequence:
- Exact email match: Search Odoo contacts for the email address from the Shopify order. Email is the most reliable unique identifier.
- Phone match (if no email match): Some B2B orders or POS orders may not have email. Fall back to phone number matching with normalization (strip spaces, dashes, country code normalization).
- Create new contact: If no match is found, create a new Odoo contact with all available data from the Shopify order.
Field Merging for Existing Customers
When an existing Odoo customer is matched, the integration should update (not overwrite) missing fields:
If Odoo contact has no phone but Shopify order does → add phone
If Odoo contact has phone and Shopify order has different phone → keep Odoo (source of truth)
If Shopify order has new shipping address → add as child contact (delivery address)
Address Handling
Shopify orders include both a billing address and a shipping address. In Odoo, these map to different contact types. The billing address should be associated with the main partner record (or a child contact of type "invoice address"). The shipping address should be a child contact of type "delivery address." If the customer has used multiple shipping addresses across different orders, each unique address should be a separate child contact — enabling address selection for future manual orders.
B2B Customer Handling
For B2B orders (Shopify Plus B2B features), the integration should map the Shopify company to an Odoo company-type contact, with the individual buyer as a child contact. This preserves the company-to-individual relationship that B2B operations require for credit terms, volume pricing, and account management.
Stage 3: Product Matching
Each line item on a Shopify order must be matched to a product in Odoo. This matching is where many integrations fail, because Shopify and Odoo represent products differently.
SKU-Based Matching
SKU is the recommended primary match key. When you manage products in Odoo and sync them to Shopify, the Odoo internal reference (SKU) should be set on both the Odoo product variant and the Shopify variant. The integration matches Shopify line item SKU to Odoo product variant internal reference.
Variant Matching Complexity
Shopify variants (Size: Large, Color: Blue) must match to Odoo product variants with the equivalent attribute values. This is straightforward when SKUs are consistent, but breaks when SKUs differ between systems, when Shopify has variants that do not exist in Odoo (e.g., a new variant added on Shopify but not yet synced to Odoo), or when Odoo products use configurable attributes while Shopify uses pre-defined variants.
Handling unmatched products: When a Shopify line item cannot be matched to an Odoo product, the integration should not silently skip it. Instead, it should create the order with a placeholder product line (e.g., a catch-all "Unmatched Shopify Product" item with the Shopify variant title in the description), flag the order for review, and alert the operations team.
Bundle and Kit Products
If you sell bundles on Shopify (e.g., a "Starter Kit" that ships as three separate products), the integration needs to know whether to create a single Odoo order line (for the bundle product) or multiple lines (for the component products). This depends on your Odoo inventory setup — whether you track bundle products as a single item or as individual components.
Stage 4: Order Creation in Odoo
With the customer resolved and products matched, the integration creates the Odoo sale order. This is the most data-intensive stage, requiring careful field mapping.
Field Mapping: Shopify to Odoo Sale Order
| Shopify Field | Odoo Field | Notes |
|---|---|---|
| order.id | x_shopify_order_id | Custom field for reference |
| order.name | client_order_ref | e.g., "#1042" |
| order.created_at | date_order | Order date |
| order.customer.email | partner_id | Resolved in Stage 2 |
| order.shipping_address | partner_shipping_id | Delivery address contact |
| order.billing_address | partner_invoice_id | Invoice address contact |
| order.currency | currency_id | Map to Odoo currency |
| order.note | note | Customer notes |
| order.tags | tag_ids | Optional tag sync |
Line Item Mapping
| Shopify Field | Odoo Sale Order Line Field | Notes |
|---|---|---|
| line_item.variant_id | product_id | Resolved in Stage 3 |
| line_item.quantity | product_uom_qty | Order quantity |
| line_item.price | price_unit | Unit price |
| line_item.total_discount | discount | Convert to percentage |
| line_item.tax_lines | tax_id | Map to Odoo fiscal position |
Discount Handling
Shopify represents discounts differently than Odoo. Shopify provides a discount amount per line item and an overall discount code at the order level. Odoo expects a discount percentage per line.
Conversion: discount_percentage = (shopify_discount_amount / (quantity * unit_price)) * 100
For order-level discounts (e.g., "10% off entire order"), the discount must be distributed across line items proportionally. Some implementations add a separate negative-amount line item for order-level discounts instead of distributing — both approaches are valid, but the proportional distribution produces cleaner per-line-item margin analysis.
Shipping Line
Shopify shipping charges should map to a dedicated Odoo product (e.g., "Shopify Shipping") on a separate order line. This keeps shipping revenue distinct from product revenue in reporting.
| Shopify Field | Odoo Line | Notes |
|---|---|---|
| shipping_lines[0].title | product_id → "Shopify Shipping" | Description from Shopify |
| shipping_lines[0].price | price_unit | Shipping amount |
| shipping_lines[0].tax_lines | tax_id | Shipping tax if applicable |
Order Confirmation
After creating the sale order in draft state, the integration should automatically confirm it (action_confirm) to trigger downstream workflows — delivery order creation, manufacturing orders (for MTO products), and purchase orders (for dropship products). Only confirm automatically if the payment status is "paid" or "authorized" — hold draft orders for "pending" payment states.
Stage 5: Payment Recording
Shopify's financial_status field indicates the payment state of the order. The integration must translate this into Odoo payment records.
Payment Status Mapping
| Shopify financial_status | Odoo Action | Notes |
|---|---|---|
| paid | Register payment (full) | Create payment matching invoice |
| authorized | No payment yet | Payment captured later |
| partially_paid | Register partial payment | Record amount paid so far |
| pending | Hold in draft | Await payment confirmation |
| refunded | Register payment + credit note | Full refund scenario |
| partially_refunded | Register payment + partial credit | Partial refund scenario |
| voided | Cancel order | Payment authorization voided |
Automatic Invoice Creation
For "paid" orders, the integration should create and validate the Odoo invoice automatically, then register the payment against it. This keeps the accounts receivable clean — fully paid Shopify orders should have zero AR balance in Odoo.
For "authorized" orders (common with Shopify Payments when manual capture is enabled), the integration should create the sale order but not the invoice. When the payment is later captured (triggering an orders/updated webhook with financial_status changing to "paid"), the integration creates the invoice and registers the payment.
Payment Method Configuration
Create a dedicated Odoo payment journal for Shopify payments (e.g., "Shopify Payments" journal of type "Bank"). All automated payment registrations should use this journal. This keeps Shopify payment records separate from other payment methods and simplifies bank reconciliation.
Stage 6: Fulfillment Writeback
The final stage closes the loop — when an order is fulfilled in Odoo (delivery order validated), the integration sends fulfillment data back to Shopify, updating the order status and providing the customer with tracking information.
Triggering the Writeback
In Odoo, the fulfillment event is the validation of a delivery order (stock.picking). The integration should listen for delivery order confirmation events and trigger a Shopify fulfillment API call.
Fulfillment Data Mapping
| Odoo Delivery Field | Shopify Fulfillment Field | Notes |
|---|---|---|
| carrier_tracking_ref | tracking_number | Carrier tracking number |
| carrier_id.name | tracking_company | e.g., "UPS", "FedEx" |
| move_line_ids | line_items | Map Odoo products back to Shopify line item IDs |
| date_done | — | Shopify timestamps automatically |
Partial Fulfillment Handling
Partial fulfillments occur when an order ships in multiple packages, from multiple warehouses, or when some items are backordered. Shopify supports partial fulfillments natively — you can fulfill specific line items while leaving others unfulfilled. The integration must track which Shopify line items correspond to which Odoo delivery order lines, and send only the fulfilled items in each fulfillment API call. Sending a fulfillment for items that have already been fulfilled causes an API error.
Handling Split Shipments
When Odoo splits a delivery order into multiple shipments (backorder scenario), each shipment generates a separate delivery order. The integration must:
- Detect that the new delivery order is a backorder (related to the same sale order)
- Identify which line items are in this shipment
- Create a partial fulfillment on Shopify for only those items
- Include the correct tracking number for this specific shipment
Fulfillment Writeback for Dropship Orders
For dropship fulfillments (where the supplier ships directly to the customer), the tracking information comes from the purchase order receipt, not a warehouse delivery order. The integration should monitor purchase receipt confirmations for dropship orders and use the supplier's tracking number for the Shopify fulfillment.
Error Handling and Recovery
Errors in the order import pipeline fall into three categories, each requiring a different recovery strategy.
Transient Errors (Retry Automatically)
Network timeouts, API rate limits, temporary service unavailability — these resolve themselves. The integration should implement exponential backoff retry: first retry after 30 seconds, second after 2 minutes, third after 10 minutes, up to a configurable maximum retry count (typically 5–10).
Data Errors (Quarantine for Review)
Unmatched products, invalid addresses, missing required fields — these require human intervention. The integration should quarantine the order in a review queue with a clear description of what failed, create an Odoo activity or notification for the operations team, and provide a one-click retry mechanism after the data issue is fixed.
Systemic Errors (Alert Immediately)
Authentication failures, API permission changes, connector configuration errors — these affect all orders, not just one. The integration should detect patterns (e.g., 5 consecutive failures) and escalate immediately via email and dashboard alert.
The Dead Letter Queue
Orders that fail all retry attempts move to a dead letter queue — a persistent record of unprocessable orders. This queue must be monitored daily. Common dead letter reasons include products that exist in Shopify but were never synced to Odoo, customer data with invalid characters that Odoo rejects, orders with zero-price items that fail Odoo validation, and currency mismatches when a new Shopify currency is not configured in Odoo.
Testing the Order Import Pipeline
Before going live, test every path through the pipeline:
Functional Test Scenarios
| Scenario | Expected Result |
|---|---|
| Standard paid order | Odoo SO created, confirmed, invoiced, payment registered |
| Order with discount code | Discount distributed across lines correctly |
| Order with multiple variants | Each variant maps to correct Odoo product |
| Order for new customer | New Odoo contact created with all addresses |
| Order for existing customer | Existing contact found, fields merged |
| Multi-currency order | Recorded in presentment currency, converted to functional |
| Partial fulfillment | Shopify shows partially fulfilled with correct line items |
| Full fulfillment | Shopify shows fulfilled with tracking number |
| Order cancellation | Odoo SO cancelled, inventory restored |
| Refund (full) | Credit note created, payment reversed |
| Refund (partial) | Partial credit note for specific line items |
| Duplicate webhook | Second webhook does not create duplicate order |
| Product not in Odoo | Order quarantined with alert |
| API rate limit hit | Order retried after backoff period |
Load Testing
If your store processes high volumes (500+ orders/day), load test the import pipeline with production-like data volumes. Verify that the pipeline handles burst traffic (flash sales), that database locks do not create bottlenecks, and that Shopify and Odoo API rate limits are respected.
Performance Optimization
Batch Processing for Historical Imports
When initially setting up the integration or importing historical orders, use Shopify's REST API to fetch orders in batches of 250 (the maximum page size) rather than processing each order individually. Create Odoo records in batches using ORM create method with a list of values.
Caching Product and Customer Lookups
Product matching (Stage 3) and customer resolution (Stage 2) involve database lookups for every order. Cache frequently accessed products and customers to reduce Odoo API calls. Invalidate the cache when products or customers are updated.
Webhook Processing Concurrency
For high-volume stores, process webhooks concurrently — multiple workers pulling from the message queue simultaneously. Ensure concurrency safety by using database-level locking on the idempotency key check to prevent two workers from creating the same order simultaneously.
Frequently Asked Questions
How quickly do Shopify orders appear in Odoo with webhook-based automation?
With a properly configured webhook-based integration, Shopify orders appear in Odoo within 2–10 seconds of order placement. This includes webhook delivery, HMAC verification, customer resolution, product matching, and sale order creation. Cron-based alternatives add 5–60 minutes of delay depending on the polling interval.
What happens if Odoo is down when a Shopify order comes in?
The integration's message queue holds the order webhook payload until Odoo is available. When Odoo comes back online, the queue processes all pending orders in sequence. Shopify also retries failed webhook deliveries for up to 48 hours, providing a secondary safety net. Orders are never lost if the integration uses a persistent message queue.
Can I import orders from multiple Shopify stores into one Odoo instance?
Yes. Each Shopify store connects via its own set of API credentials and webhook subscriptions. The integration tags each order with the source store (using a custom field or Odoo sales team) so you can report by store. Products can be shared across stores or store-specific, depending on your catalog strategy.
How does the integration handle Shopify draft orders?
Draft orders (created by merchants for phone orders or B2B quotes) should be imported when they are completed (paid), not when they are created as drafts. Subscribe to the orders/create webhook, which fires when a draft order is converted to a real order. Alternatively, subscribe to draft_orders/update and import only when the status changes to "completed."
What about Shopify POS orders — do they follow the same pipeline?
Shopify POS orders trigger the same orders/create webhook as online orders. The integration can process them identically, though you may want to tag POS orders with a different Odoo sales team or source for reporting. POS orders may also include cash or external terminal payment methods, which require different Odoo payment journal configurations.
How do I handle order edits after import (e.g., merchant edits an order on Shopify)?
Shopify fires an orders/updated webhook when an order is edited. The integration should compare the updated order against the existing Odoo sale order and apply the differences — added line items, removed items, quantity changes, or price adjustments. This is one of the most complex parts of the integration, and some implementations handle it by cancelling the Odoo SO and recreating it, which is simpler but creates more accounting entries.
Can the automation handle subscription orders from Shopify Subscriptions apps?
Subscription orders (from Recharge, Bold Subscriptions, or Shopify native subscriptions) trigger standard orders/create webhooks for each recurring charge. The integration imports them like any other order. To link recurring orders to an Odoo subscription record, use the subscription ID from the app's metafield as a reference, and associate all related Odoo sale orders with a single Odoo subscription.
Implementation With ECOSIRE
Building a production-grade Shopify-to-Odoo order import pipeline requires handling dozens of edge cases that are not obvious until you encounter them in production — partial payments, orders with both digital and physical items, complex tax jurisdictions, multi-warehouse routing, and vendor dropship fulfillments.
ECOSIRE has implemented this pipeline for hundreds of Shopify merchants. Our Shopify integration services include complete order automation with the full pipeline described in this guide, pre-built handling for all common edge cases, real-time monitoring and alerting, and ongoing maintenance as APIs evolve.
For businesses also looking to automate the financial side, see our guide to Shopify + Odoo Accounting Integration, or compare your options with our Shopify-Odoo connector comparison.
Schedule a consultation to discuss your Shopify order automation requirements with our integration team.
Written by
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
Related Articles
Accounting Automation: Eliminate Manual Bookkeeping in 2026
Automate bookkeeping with bank feed automation, receipt scanning, invoice matching, AP/AR automation, and month-end close acceleration in 2026.
AI Agents for Business: The Definitive Guide (2026)
Comprehensive guide to AI agents for business: how they work, use cases, implementation roadmap, cost analysis, governance, and future trends for 2026.
AI Agents vs RPA: Which Automation Technology is Right for Your Business?
Deep comparison of LLM-powered AI agents versus traditional RPA bots — capabilities, costs, use cases, and a decision matrix for choosing the right approach.