Create Odoo customers and sales orders from paid Shopify orders

When a Shopify order is paid, we match or create the customer in Odoo, build the sales order with mapped products, and post a Slack recap to your ops channel.

Deterministic Code
OdooShopifySlackOperationsSalesData SyncNotifications & Alerts
PromptCreate

When a Shopify order is paid, create the matching customer and sales order in my Odoo instance automatically so my storefront and my ERP stay in step.

Trigger this on a Shopify webhook for the order paid event (orders/paid). When the webhook fires, take the order id from the payload and use the Shopify Get Order operation to pull the full order, including line items, customer details, currency and totals. Work from that fetched order rather than trusting the webhook body, so the data is always complete.

Before writing anything, check for a redelivery. Shopify retries webhook deliveries, so use Search & Read Records (search_read) on sale.order in Odoo with a domain matching client_order_ref against the Shopify order id. If a sales order already exists, update that order instead of creating a new one, and end the run with a Slack message that says it was an update rather than a new order. Never create a second sales order for the same Shopify order id.

Resolve the customer next. Use Name Search (name_search) on res.partner to find an existing contact by the order's email, and fall back to the customer name if the email finds nothing. If there is no match, use Create Record (create) on res.partner to add the customer with their name, email, phone and billing address. Reuse the existing contact whenever one is found so regular buyers do not get duplicated.

Then resolve products. For each Shopify line item, use Name Search (name_search) on product.product to find the matching Odoo product, searching on the SKU first and then the product title. Keep a list of any line items that come back with no match, or with an ambiguous match of more than one product, and treat those as unmatched.

Create the sales order with Create Record (create) on sale.order. Set partner_id to the resolved customer, set client_order_ref to the Shopify order id or order name so future redeliveries can find it, and build order_line from the matched line items using Odoo's relational create commands, one line per item with the product id, quantity and unit price taken from Shopify. Include shipping and discounts as their own lines where the Shopify order has them, so the Odoo total reconciles against the Shopify total.

If every line item matched a product, use Call Model Method (execute_kw) to confirm the sales order (action_confirm) so fulfillment and invoicing pick it up. If any SKU failed to match, do not confirm: leave the sales order in draft with the matched lines on it, and leave the unmatched items off the order so nobody accidentally ships the wrong thing.

Finally, post to my ops channel in Slack with Send a Message. Include the Shopify order number, the customer name, the order total with currency, a link to the Odoo sales order, and whether the order was confirmed or left in draft. If there were unmatched line items, call them out clearly at the top of the message with the SKU, product title and quantity for each one, so someone can add the missing product in Odoo and confirm the order by hand. Never silently drop a line item.

If Odoo rejects a call or is unreachable, do not swallow the error: post a Slack message naming the Shopify order number and the reason it failed, so the order can be replayed or entered manually.

What does this prompt do?

  • Watches your Shopify store and reacts the moment an order is marked as paid, then pulls the full order with its line items, customer details and totals
  • Finds the matching customer in Odoo by email or name, and adds them as a new contact if this is their first purchase
  • Builds the sales order in Odoo with each Shopify line item mapped to the right product, then confirms it so your warehouse and invoicing pick it up
  • Posts a Slack message to your ops channel with the order number, customer, total, and a clear callout of any product it could not match

What do I need to use this?

  • A Shopify store where you can add a notification for paid orders
  • An Odoo account with sales orders and products already set up, plus a login that can create contacts and orders
  • On Odoo Online (the odoo.com hosted version), outside access to your data requires a Custom plan. Self-hosted and Odoo.sh accounts have no such restriction
  • A Slack workspace and the channel your ops team watches
  • Your Shopify products and your Odoo products should share a code or name so they can be matched to each other

How can I customize it?

  • Choose which Slack channel gets the recap, or send unmatched-product alerts to a separate channel from the routine confirmations
  • Decide whether orders are confirmed automatically or always left as drafts for someone to review first
  • Change how products are matched, for example by product code instead of product name, or skip low-value orders entirely

FAQs

Will a duplicate order be created if Shopify sends the same notification twice?
No. The Shopify order number is stored on the Odoo sales order, so a repeated notification finds the existing order and updates it instead of creating a second one. This matters because Shopify retries delivery when it does not get a clean response.
What happens if one of my products does not exist in Odoo?
The order is still created, but it is left as a draft rather than confirmed, and the Slack message names exactly which line could not be matched. Nothing is silently dropped, so you can add the missing product and confirm the order yourself.
Does this work with the free version of Odoo?
Self-hosted Odoo and Odoo.sh work with no restrictions. On Odoo Online, the hosted version at odoo.com, outside access to your data is only available on a Custom plan, so Free and Standard accounts will not be able to connect.
What if the customer already exists in Odoo?
They are reused. The workflow looks for an existing contact by email first and then by name, and only creates a new contact when there is no match, so you do not end up with duplicates of your regular buyers.
Does this run for every order, or only paid ones?
Only orders that have actually been paid. Abandoned checkouts and unpaid draft orders never reach Odoo, so your ERP only ever sees real revenue.

Related templates

Auto-fix your calendar when a flight slips, and flag what's at risk

When your flight moves, your calendar times get corrected automatically and you get a Slack note naming the meetings you're about to miss.

Google Calendar
AviationStack
Slack
Agentic Task
Trace phishing emails to the sending IP and report abuse

Every 15 minutes, forwarded phishing reports get traced back to the server that really sent them, with a verdict in Slack and the worst senders reported.

AbuseIPDB
Gmail
Slack
Agentic Task
Turn procurement portal tenders into CRM deals each morning

Every weekday at 7am, sign in to the tender portals you track, filter new notices against your bid criteria, and open a deal for the ones worth chasing.

Anchor Browser
Google Sheets
HubSpot
+1
Agentic Task
Turn each week's football fixtures into a venue staffing plan

Every Monday, rank the week's matches by expected demand, put the big ones on your venue calendar, and post a rota-ready summary to Slack.

API-Sports
Google Calendar
Slack
Agentic Task
Auto-correct address typos and hold the orders you can't fix

Every weekday morning, harmless address typos get fixed on unshipped orders, and anything undeliverable is held while the customer confirms it.

Shippo
Shopify
Gmail
+1
Agentic Task
Turn 5 star Yotpo reviews into a weekly marketing content queue

Every Tuesday we pull your best new reviews, draft social captions, email testimonials and product page quotes, then stage them in Notion for approval.

Yotpo
Notion
Slack
Agentic Task

Stop retyping Shopify orders into your ERP.

Every paid order lands in Odoo with the right customer and products in seconds, and your ops team hears about anything that needs a human.