Skip to main content
Insyrge - Zoho CRM & Business Automation Consulting
← Back to All ArticlesAI & Business Automation

Odoo API Integration with NetSuite and SAP

Data mapping, validation and sync patterns for custom connectors that keep Odoo, NetSuite and SAP records consistent.

•Manav Sharma
Odoo API Integration with NetSuite and SAP

Connect Odoo with NetSuite and SAP using custom API connectors: data mapping, validation and sync patterns that keep records consistent. See how it works.

Many growing companies end up running Odoo alongside another ERP. A subsidiary may run NetSuite, a parent company may run SAP, or a business may be part-way through moving from one to the other. In each case, customers, products, orders and invoices have to move between systems without being typed in twice. Odoo API integration is how that happens reliably. This guide explains how we design custom connectors between Odoo, NetSuite and SAP: choosing a system of record, mapping entities, using each platform's API correctly, and building sync jobs that recover cleanly when something fails.

Start with ownership, not endpoints

The most common integration failure we see is not a technical bug. It is two systems both believing they own the same data. Before writing code, decide which system is the source of truth for each entity and which direction changes flow.

Illustrative ownership matrix

EntitySystem of recordDirectionNotesCustomersNetSuiteNetSuite → OdooOdoo may add contacts but not edit billing detailsProducts and pricesSAPSAP → OdooPrice lists published nightlySales ordersOdooOdoo → NetSuiteCreated in Odoo, fulfilled and invoiced in NetSuiteInvoices and paymentsNetSuiteNetSuite → OdooRead-only in Odoo for visibilityStock levelsWhichever system runs the warehouseOne way onlyNever two-way

This is an example, not a template. The point is that every row has exactly one owner and, wherever possible, one direction.

Odoo API integration: how each platform exposes its data

Odoo

Odoo 19 introduced the External JSON-2 API, which exposes model methods over HTTP at /json/2/<model>/<method>. Requests authenticate with an API key in an Authorization: bearer header, and all method arguments are passed by name in a JSON body. Odoo's documentation states that the older XML-RPC and JSON-RPC endpoints are deprecated and scheduled for removal in Odoo 22. If you are building a new Odoo API integration today, use JSON-2 where your version supports it, and plan to migrate any XML-RPC connectors.

NetSuite

NetSuite offers SuiteTalk REST web services, documented in Oracle's SuiteTalk REST Web Services API Guide. Records such as customers, sales orders and invoices are available as REST resources, and authentication uses token-based authentication or OAuth 2.0. NetSuite applies account-level concurrency governance, so connectors must queue and throttle requests rather than firing them in parallel without limit.

SAP

SAP S/4HANA exposes much of its data through OData APIs, which are listed and documented on the SAP Business Accelerator Hub. The Business Partner, Product and Sales Order APIs cover the entities most Odoo integrations need. Older SAP ECC systems typically integrate through BAPIs, RFCs or IDocs, often via middleware.

Entity mapping across Odoo, NetSuite and SAP

The table below shows the usual starting map. Field-level mapping comes next and depends on your configuration.

Business entityOdoo modelNetSuite recordSAP S/4HANA (OData)Customerres.partnercustomerBusiness Partner (A_BusinessPartner)Productproduct.template / product.productinventoryItem (or other item types)Product (A_Product)Sales ordersale.order and sale.order.linesalesOrderSales Order (A_SalesOrder)Customer invoiceaccount.move (customer invoice type)invoiceBilling DocumentPayment termsaccount.payment.termtermPayment terms in the customer master

Two mapping rules save a lot of pain later:

  • Store the other system's ID on every synced record. In Odoo we use a dedicated field (or the partner's ref field for customers) to hold the NetSuite internal ID or SAP business partner number.
  • Map codes, not labels. Countries, currencies, tax codes, units of measure and payment terms should map by code through a lookup table that a functional consultant can maintain.

Worked example: upserting a NetSuite customer into Odoo with JSON-2

The illustrative Python below takes a customer that has already been fetched from NetSuite and creates or updates the matching Odoo partner. It finds the partner by the NetSuite ID stored in ref, so running it twice does not create duplicates.

import requests

ODOO_URL = "https://your-company.odoo.com"
HEADERS = {
    "Authorization": "bearer YOUR_ODOO_API_KEY",
    "Content-Type": "application/json",
    # "X-Odoo-Database": "your-db",  # only needed if several databases share the domain
}

def odoo_call(model, method, payload):
    r = requests.post(f"{ODOO_URL}/json/2/{model}/{method}", json=payload, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()

def upsert_partner_from_netsuite(ns_customer, country_map):
    ns_id = str(ns_customer["id"])
    vals = {
        "name": ns_customer["companyName"],
        "email": ns_customer.get("email"),
        "phone": ns_customer.get("phone"),
        "ref": ns_id,
        "is_company": True,
        "country_id": country_map.get(ns_customer.get("countryCode")),
    }
    existing = odoo_call("res.partner", "search_read", {
        "domain": [["ref", "=", ns_id]],
        "fields": ["id"],
        "limit": 1,
    })
    if existing:
        odoo_call("res.partner", "write", {"ids": [existing[0]["id"]], "vals": vals})
        return existing[0]["id"]
    created = odoo_call("res.partner", "create", {"vals_list": [vals]})
    return created[0]

In production this function would sit behind a queue worker, with the NetSuite fetch, retries and logging handled around it. The NetSuite field names shown are placeholders; map them to the fields your NetSuite account actually returns.

Designing sync jobs that recover from failure

Connectors fail. Networks drop, tokens expire, a record fails validation. A good connector is defined by how it behaves when that happens.

Detect changes incrementally

Poll for records modified since the last successful run, using each system's last-modified timestamp (in Odoo, write_date). Store the high-water mark only after a batch succeeds. Where a platform supports webhooks or event notifications, use them to trigger syncs, but keep a scheduled sweep to catch anything missed.

Make every write idempotent

Because every record carries the other system's ID, a retried job updates rather than duplicates. Never rely on matching by name.

Queue, retry and park

  • Put each record change on a queue rather than syncing in one long loop
  • Retry transient errors with increasing delays
  • After a set number of attempts, move the record to a dead-letter queue and alert someone
  • Give operations staff a simple screen to fix and replay parked records

Respect each platform's limits

Throttle NetSuite calls to stay within your account's concurrency allowance, batch reads where the API allows it, and schedule bulk loads outside business hours.

Log for people, not just machines

For each sync, record what changed, which direction, the source and target IDs, and the outcome. When finance asks why an invoice is missing in Odoo, the answer should take minutes, not an afternoon of reading code.

When a custom connector is the right choice

Off-the-shelf connectors and integration platforms work well for standard objects and simple flows. We recommend custom connectors when you have heavily customised records, strict ownership rules, high volumes, or multi-step flows such as order-to-invoice that must be traceable end to end. If you are weighing the two, our comparison of replacing Zapier and Make webhooks with custom API connectors walks through the trade-offs, and our article on moving from legacy SaaS workarounds to resilient custom connectors covers the pipeline design in more depth.

Frequently Asked Questions

Which Odoo API should we use for a new integration?

For Odoo 19 and later, Odoo documents the External JSON-2 API as the replacement for XML-RPC and JSON-RPC, which are deprecated and scheduled for removal in Odoo 22. Use JSON-2 where your version supports it.

Can Odoo and NetSuite both edit the same customer?

They can, but we advise against two-way editing of the same fields. Pick one system of record per entity, or per group of fields, and sync in one direction. Two-way edits need conflict rules that are hard to get right.

Do we need middleware between Odoo and SAP?

Not always. A well-designed custom service can call SAP's OData APIs directly. Middleware becomes worthwhile when you have many systems, need IDoc or RFC support for older SAP versions, or want a central place to monitor every flow.

How do we avoid duplicate records during an Odoo API integration?

Store the other system's ID on every record, look records up by that ID before creating them, and make every write an upsert. Never match records by name alone.

Get your Odoo integration planned properly

We design and build custom connectors between Odoo, NetSuite, SAP and CRMs such as Zoho, Salesforce and HubSpot. See our CRM and API integration services, or talk to us about connecting Odoo with your other systems.

About the Author

Manav Sharma is the founder of Insyrge and a CRM solutions architect. He works across Zoho, Salesforce, HubSpot, Odoo and more, designing the CRM setups, integrations and automations that keep sales and operations teams working from the same data.

Need Help Implementing This in Your Business?

Our certified Zoho consultants and automation experts can help you design and deploy custom workflows tailored to your operations.

Book Free Consultation