Khichdi InfoTech
Skip to content

Odoo Development · advanced · 14 min read

REST APIs and External Integrations

Integrating Odoo with external systems: JSON-RPC/XML-RPC, REST controllers, webhooks, API keys, idempotent sync, and when to build middleware outside Odoo.

Last reviewed 2026-07-30 · Estimated effort: 6+ weeks for multi-team change

Table of contents

Odoo exposes JSON-RPC and XML-RPC on /jsonrpc and /xmlrpc/2/object for authenticated CRUD and method calls. Custom @http.route controllers add REST-shaped endpoints when you need webhooks, HMAC validation, or public callbacks.

Production integrations need integration users with minimal ACL, external id mapping tables, retry queues, and idempotent upserts — not ad-hoc sudo in controllers copied from blog posts.

Heavy orchestration (Shopify catalog sync, 3PL event streams) often splits: middleware owns rate limits and retries; Odoo owns business documents and stock/accounting rules via RPC.

Every integration ticket sounds unique; the engineering patterns repeat. Authentication, mapping, conflict resolution, and observability matter more than whether you call it REST or RPC.

This article compares Odoo-native integration surfaces with external Python services and gives implementation guardrails for Odoo 16/17 deployments.

Authenticate to /web/session/authenticate or use API keys where supported, then call execute_kw on models: create, read, write, search_read, unlink. Batch with search_read domains and explicit field lists to reduce payload.

Use dedicated integration users — not admin — with groups matching least privilege. Rotate passwords and store secrets in ir.config_parameter or environment variables, not Git.

  • context in execute_kw passes lang, company, active_test
  • name_search and custom @api.model methods expose safe operations
  • Avoid unlink in sync jobs — archive or cancel per business rules

controllers/main.py defines routes with type='json' or type='http', auth='user'|'public'|'none', methods, csrf=False only when justified for external POST webhooks with alternate auth.

Validate signatures (Shopify HMAC, Stripe webhook secret) before touching env. Return proper HTTP status codes — partners retry 5xx, not vague 200 with error body.

Store partner ids on models (shopify_product_id) or a sync.mapping model with (source, external_id) unique constraint. Upsert: search mapping → write or create with same external key.

Last-write-wins is dangerous for inventory and invoices. Define authority per field — Odoo wins on posted accounting; channel wins on listing title if merchandising lives in Shopify.

When throughput, scheduling, or secret isolation exceeds Odoo worker comfort, run a Python service with official odoorpc, xmlrpc.client, or REST to Odoo. Use message queues for burst traffic.

Business decision whether integration is in scope for Odoo module vs external service overlaps the Customization pillar — link there for buy/build; here focus on engineering split.

Log correlation ids across middleware and Odoo chatter or custom log model. Replay failed payloads from dead-letter storage — not by re-firing production webhooks blindly.

Staging must mirror ACL of integration user and include sandbox credentials. Never point dev middleware at production Odoo without read-only safeguards.

  • Define integration contract document: models, fields, direction, frequency, error codes.
  • Wrap outbound HTTP in timeout + retry with exponential backoff; respect 429 headers.
  • Use savepoints or queue rows for partial failure — one bad SKU must not abort entire catalog.
  • Expose health endpoint or heartbeat record support can monitor.
  • Version controller URLs (/api/v1/) when external clients depend on shape.
  • Pen-test public routes — auth='public' routes are internet-facing attack surface.

  • !Admin API user in production scripts on laptops.
  • !sudo() in webhook handler without partner verification.
  • !Creating duplicate products every sync — no external id search.
  • !Synchronous 30s partner call inside HTTP request worker.
  • !Writing posted invoice fields from ecommerce webhook.
  • !csrf=False on routes that accept browser cookies without token checks.

Integrations succeed on mapping, least-privilege users, and idempotent jobs — not on picking RPC vs REST labels.

Split middleware from Odoo when rate limits and retries dominate; keep financial and stock truth inside Odoo transactions.

Channel → middleware → Odoo RPC → models → accounting/stock.
  • Client / Channel

    Users & devices

  • Odoo Application

    Business logic

  • PostgreSQL

    System of record

  • Workers / Cron

    Async work

  • clientapp(HTTP)
  • appdb(ORM)
  • appjobs(queue)
Webhook POST → verify HMAC → queue → upsert → ack.
1

Request

HTTP / RPC enters Odoo.

2

Auth & ACL

Users, groups, record rules.

3

ORM work

Reads/writes with business logic.

4

Response

UI, API payload, or job enqueue.

Integration user → ACL → record rules → method whitelist.
1

Authenticate

User session or API key.

2

Groups

Feature access.

3

Record rules

Row-level visibility.

4

Field access

Read/write restrictions.

Frequently asked questions

REST API vs JSON-RPC — which should I use?

JSON-RPC is native and stable for CRUD. Add REST controllers when partners require specific URLs, webhooks, or OAuth flows RPC does not cover.

Does Odoo have OpenAPI for all models?

Not built-in for custom models. Community modules exist; most teams document execute_kw contracts or ship explicit REST wrappers.

How do I handle multi-company in API writes?

Pass allowed_company_ids in context and set company_id on records. Integration user must have access to target companies.

Can Odoo call external APIs from cron?

Yes via requests/urllib in cron methods — keep batches small. High-volume outbound sync often belongs in external worker calling into Odoo after fetch.

Planning Shopify, WMS, or banking integration?

We design API users, mapping models, and middleware with staging replay and operator-visible error queues.

Business technology and software delivery