Guide

Odoo Integration with Shopify and Marketplaces: Ownership, Sync and Failure Handling

How to connect Odoo to Shopify or a marketplace: which system owns each record, webhooks and polling, idempotency, rate limits, mapping pitfalls, a checklist.

Illustration: Odoo and Shopify connected by arrows, with the sync direction for products, stock, orders and refunds.

The short answer

Connecting Odoo to a storefront or marketplace is mostly a set of decisions, not code: which system owns each record, which way it flows, and what happens when a message is lost, repeated or late. Get those written down and the build is routine. Skip them and you get the usual symptoms: oversold stock, duplicate orders, and accounts that do not match the channel's payouts.

This guide uses Shopify as the worked example because its API is publicly documented. The pattern is the same for any marketplace or order-management system; the last section lists what to find out about a channel before you start. For what Odoo itself costs, including which plan allows API access, see Odoo implementation cost.

First, check whether Odoo's own connector is enough

Odoo ships a Shopify Connector. Its documentation says it synchronizes orders, products, inventory, deliveries, returns, and refunds between a Shopify store and Odoo, in these directions:

DataDirection in Odoo's Shopify Connector
Orders, products, customersShopify → Odoo
Inventory; fulfillments and deliveriesBoth ways
Returns; refunds and credit notesShopify → Odoo
Invoices and paymentsCreated in Odoo automatically

Three documented behaviors decide whether it fits:

If one Shopify store, a ten-minute delay and those directions suit you, use the connector. A custom integration is for the other cases: a channel Odoo has no connector for, several channels sharing one stock pool, a catalog owned by Odoo, or stock that must update faster than a polling interval. A custom integration talks to Odoo through its external API, and Odoo states that access to data via the external API is only available on Custom Odoo pricing plans.

Decide which system owns each record

One system owns each record; the other holds a copy and never edits it. The table is a default for a business that runs stock, purchasing and accounts in Odoo and sells through one or more channels. Change a row if your business differs, but do not leave a row with two owners.

RecordOwnerDirectionTriggerMatch onWatch for
Product and variantsOdoo (or the channel, if you use Odoo's connector)Odoo → channelOn change, plus nightly compareSKU, stored with the channel's variant IDDuplicate or blank SKUs; variants modeled differently on each side
Descriptions and imagesChannelNoneNoneNoneMarketing edits overwritten by a sync that sends every field
PriceOdoo pricelist, one per channelOdoo → channelOn changeSKU and currencyTax-inclusive against tax-exclusive prices; rounding
StockOdooOdoo → channelOn every stock move, plus scheduled full pushSKU and locationWhich Odoo quantity you publish (on hand or available after reservations); buffer for slow channels
OrderChannel until paid, then OdooChannel → OdooWebhook, plus polling for missed ordersChannel order ID, stored on the Odoo sales orderEdits and cancellations after import; orders with unknown SKUs
CustomerChannel for the storefront account; Odoo for the accounting contactChannel → OdooWith the orderChannel customer ID, then emailGuest checkouts; one person with several emails; marketplaces that mask buyer details
Fulfillment and trackingWhoever ships (usually Odoo)Odoo → channelOn delivery validationOrder ID and linePartial shipments; split across warehouses; carrier name mapping
Return and refundChannel starts it; Odoo records stock and credit noteChannel → OdooWebhook, plus pollingChannel refund IDRefund without a return; partial refunds; restock or not
Invoice and paymentOdooNone, or Odoo → customerOn order confirmation or deliveryOrder IDChannel fees and payouts: reconcile against the payout report, not order totals
TaxDecide once: channel calculates and Odoo records, or Odoo calculatesChannel → OdooWith the orderTax code mapping tableTwo systems each calculating tax and disagreeing by a cent

Webhooks and polling: use both

Webhooks are fast and polling is complete. Shopify says so about its own webhooks: an app shouldn't rely on receiving data from Shopify webhooks because delivery isn't always guaranteed, and it recommends reconciliation jobs that periodically fetch data, built on updated-at filters. Four more documented behaviors shape the receiver:

On the Odoo side, changes leave by an automated action or by your integration polling for records changed since its last run. Odoo can also receive webhooks: its documentation describes them as created in Studio to automate an action in your Odoo database when a specific event occurs in an external system, with a warning that if not properly configured, webhooks may disrupt the Odoo database. For a channel integration we would not point the channel straight at Odoo. A small service in between gives you the queue, the duplicate check and the retry policy in one place.

The resulting flow for an order:

  1. Receiver verifies the signature, records the delivery ID, queues the payload, answers 200.
  2. Worker takes the job, skips it if that delivery or order version was already processed.
  3. Worker maps the order and calls one Odoo method that creates or updates the sales order.
  4. On failure the job is retried with backoff; after a set number of attempts it goes to a dead-letter list a person reviews.
  5. A scheduled job asks the channel for orders updated since the last run and queues any the webhooks missed.
  6. Stock changes in Odoo are pushed to the channel, and a scheduled full comparison corrects drift.

Idempotency and replay

Every message will at some point be delivered twice, and every job will at some point be run twice. The integration is correct only if doing so changes nothing.

Rate limits on both sides

Shopify. The GraphQL Admin API is limited by calculated query cost: 100 points per second on standard plans, 200 on Advanced Shopify, 1,000 on Shopify Plus and 2,000 for enterprise, with a single query capped at 1,000 points and input arrays capped at 250 items. Limits apply per combination of app and store. The older REST Admin API, which Shopify has marked legacy as of October 1, 2024, allows 2 requests per second on standard plans and answers 429 Too Many Requests beyond that. Shopify's recommended backoff time is one second, and it notes it may temporarily reduce rate limits to protect platform stability. Very large catalogs meet one more limit: once a store has 500,000 product variants, no more than 10,000 new variants can be created per day, except on Shopify Plus.

Odoo. The external API pages we read publish no request-rate figure, so do not assume one. Measure on a staging copy, keep concurrency low, and ask Odoo about limits on your hosting. What the documentation does say: use dedicated bot users for integrations, so that the minimum required permissions can be granted, and note that every API key has a duration, after which the key becomes invalid. An expired key is a common cause of an integration that stops overnight.

The design consequence is the same on both sides: all outbound calls go through a queue with a limiter per system, bulk jobs such as a full stock push run at a lower priority than orders, and a throttle response slows the queue instead of failing the job.

Versions expire: plan for it

Pin the version in every request, log the version the channel reports back, and put a recurring task in the calendar to move forward one version at a time.

Mapping pitfalls: units, tax and currency

  • SKU. It is the only practical join between systems, and neither enforces it the way you need. Check for blanks and duplicates on both sides before the first sync, and store the channel's own IDs next to it so a renamed SKU does not break the link.
  • Units of measure. Odoo automatically converts unit measurements between a product's units; a channel usually sells “1” of a listing. If you stock in single units and sell a pack of six, the listing must map to six units, both when the order comes in and when stock goes out.
  • Tax. Odoo applies tax through fiscal positions, which adapt the taxes and the income and expense accounts used for a transaction automatically. If the channel has already calculated tax, import its amounts and map them to Odoo tax codes; do not let Odoo recalculate and differ. Decide whether prices are sent tax-inclusive or tax-exclusive, per channel.
  • Currency. Odoo can record transactions in currencies other than the main currency configured for your company. Record the order in the currency the customer paid, and take the exchange rate from the payout, not from the day of import.
  • Rounding. Line-level and order-level rounding give different totals. Compare order totals on import and post any difference to a rounding account, with an alert above a cent or two.
  • Time zones. Store timestamps in UTC and convert for display. Day-boundary errors show up as orders in the wrong accounting period.
  • Status. Write the mapping from each channel status to an Odoo action (confirm, cancel, hold) as a table, including what happens to an order edited after it was imported.
  • Bundles and kits. Decide whether a bundle is one stock item or a bill of components, because that determines what stock number the channel is sent.

Failure modes and how to detect them

FailureHow to detect itWhat to do
Missed order (webhook lost, receiver down)Scheduled comparison of channel order IDs against Odoo for the last few daysQueue the missing orders; this job is part of the design, not a repair tool
Webhook subscription removedDaily check that expected subscriptions exist; no deliveries for longer than normalRe-subscribe, then run the comparison for the gap. Shopify warns that if failures persist, the subscription is removed
Duplicate order in OdooMore than one sales order with the same channel order IDUnique constraint on that field; create-or-update logic
Order stuck (unknown SKU, missing tax mapping, validation error)Dead-letter queue count above zero; age of oldest unprocessed jobA screen where staff see the reason, fix the data and retry
OversellChannel stock differs from Odoo's published quantity in the scheduled comparison; negative available stockPush on every move, full push on a schedule, a safety buffer for slow channels
Half-created orderSales orders with no lines, or customers created with no order, from the integration userOne Odoo method per operation, so it commits or rolls back as a whole
Throttled429 responses; queue depth growing; Shopify's reported available cost near zeroLimiter per system; slow the queue; move bulk work off-peak
Expired or revoked credentialsAuthentication errors on every call; key expiry date within two weeksAlert before expiry; documented rotation
Silent API version changeVersion in the response header differs from the version requestedUpgrade on a schedule; contract tests on the fields you read
Out-of-order updateStored record has a newer timestamp than the incoming messageIgnore older messages, or always fetch the current record
Refund not in the accountsChannel refund total against Odoo credit notes per periodImport refunds as their own record type, with or without a stock return
Totals do not reconcileSum of imported orders against the channel's payout reportRecord fees and adjustments from the payout; fix tax and rounding mapping

Go-live checklist

  • Ownership table agreed and signed by operations and finance.
  • SKUs unique and non-blank on both sides; channel IDs stored in Odoo.
  • Odoo plan allows external API access; a dedicated integration user with minimum permissions; key expiry on the calendar.
  • Receiver answers inside the channel's timeout under load, with signature verification on.
  • Replay test passed: the same day of payloads sent twice produces no duplicates and no stock change.
  • Out-of-order test passed: update before create, cancel before create.
  • Outage test passed: receiver off for longer than the retry window, then recovered by the comparison job.
  • Tax, currency and rounding checked on real orders from every country and price type you sell in.
  • Partial shipment, partial refund and refund-without-return each run end to end.
  • Limiters set below each system's documented limit; a full stock push tested without starving orders.
  • Dashboards and alerts live: queue age, dead letters, 429s, missing subscriptions, stock drift.
  • A first full stock push scheduled for a quiet hour, and a rollback plan: channel listings paused, integration off, manual order entry.
  • API version pinned on both sides, with the next upgrade date written down.

Applying this to another marketplace or order system

Nothing above is specific to Shopify except the numbers. For any other channel, whether a general marketplace, a specialist one or an order-management system, get the answers to these from its own developer documentation before estimating the work:

  1. Does it push events, or must you poll? If it pushes, what are the timeout, retry schedule and signature method?
  2. Is there a stable ID for every order, line, shipment and refund, and a delivery ID for every message?
  3. Can you ask for “everything changed since a timestamp”? That is the reconciliation job.
  4. What are the rate limits, and are they per account, per key or per endpoint?
  5. Does it accept an absolute stock quantity, and how quickly does a stock update take effect?
  6. Who calculates tax, and what does the order payload contain: gross, net, tax lines, fees?
  7. How are API versions retired, and how much notice is given?
  8. Is there a sandbox with realistic data?

If the documentation does not answer a question, ask the channel in writing before you build. We have not named other marketplaces here because we only describe an API from its own published documentation.

Frequently Asked Questions

Does Odoo have a Shopify integration?

Yes. Odoo’s Shopify Connector synchronizes orders, products, inventory, deliveries, returns, and refunds between a Shopify store and Odoo. It is scheduled, not real-time: it does not rely on webhooks, and by default orders are pulled every 10 minutes.

Is the Odoo Shopify integration real-time?

Not with Odoo’s own connector, which does not perform real-time synchronization. A custom integration can react to Shopify webhooks within seconds, but still needs a scheduled comparison because Shopify says webhook delivery isn’t always guaranteed.

Which Odoo plan do I need to connect Odoo to another system through its API?

Odoo’s documentation says access to data via the external API is only available on Custom Odoo pricing plans. Our Odoo implementation cost guide covers the plans.

Should a new Odoo integration use XML-RPC or the JSON-2 API?

JSON-2. Odoo’s XML-RPC and JSON-RPC endpoints are scheduled for removal in Odoo 22 (fall 2028) and Online 21.1 (winter 2027), and Odoo names the JSON-2 API as the replacement.

How do you stop duplicate orders when syncing a sales channel with Odoo?

Store the channel’s order ID on the Odoo sales order and create only if it is absent. Skip repeated deliveries using the delivery ID; Shopify sends an X-Shopify-Webhook-Id header to detect and skip duplicates. Create the order in one Odoo call, because each JSON-2 call runs in its own SQL transaction.

Can Odoo connect to marketplaces other than Shopify?

Through its external API, yes, where the marketplace also offers an API. The pattern in this guide applies: decide ownership per record, combine events with scheduled comparison, make every operation safe to repeat, and respect both systems’ rate limits. Read the marketplace’s own developer documentation for its limits and retry behavior.

Odoo facts are from Odoo's 19.0 documentation and Shopify facts from shopify.dev, read on October 10, 2026. Limits, intervals and version dates change; check the linked pages before you rely on a number. This is neutral technical guidance: Peregrine has not published an Odoo case study, and nothing here describes a client project or a delivered integration.

Planning an Odoo integration? See our Odoo service and how we approach API integration in general. Every project starts with a 30-minute technical discovery call with an engineer.

Technical consultation

Connecting Odoo to a Sales Channel?

Tell us which channels you sell through, which system holds stock and prices today, and where orders go wrong. An engineer will walk through ownership, sync and failure handling for your setup.

  • A 30-minute technical discovery call with an engineer, not a salesperson.
  • Fixed-scope projects, monthly retainers, or a combination, agreed after the call.
  • Small, well-defined tasks get a scoped estimate. We reply within 1 business day.

Prefer to pick a time? Book a call on Calendly

Tell us about your project

We'll review your details and reach out within 1 business day.

Book Strategy CallCase Studies