Odoo holds the data that most business questions depend on: customers, orders, stock, invoices, payments. An AI assistant that can read and act on that data answers "Where is the order for customer X?" or "Which invoices are overdue more than 60 days?" in seconds instead of minutes of clicking. Since Odoo 19, the cleanest way to connect such an assistant is the new JSON-2 API. This article shows the architecture we use: authentication, a dedicated integration user, tool design, exposing Odoo through the Model Context Protocol, and the guardrails that keep an agent from doing damage.

Why JSON-2 is the API to build on

Odoo 19 introduced the external JSON-2 API: you POST a JSON body to /json/2/<model>/<method> with an API key as a bearer token, and get the method's return value back as JSON (Odoo: external JSON-2 API). The older XML-RPC and JSON-RPC endpoints (/xmlrpc, /xmlrpc/2, /jsonrpc) are deprecated and scheduled for removal in Odoo 22 in autumn 2028 (Odoo: external RPC API). New integrations should use JSON-2 from day one.

Two practical consequences:

  • Plan licensing. External API access is only available on Odoo's Custom plans, not on One App Free or Standard. We explain the plans in Odoo Community vs Enterprise.
  • Plan upgrades. If you are on Odoo 17 or 18 with RPC-based integrations, migrating them to JSON-2 is part of your next upgrade project.

Authentication: API keys and a dedicated user

JSON-2 uses API keys sent in the Authorization header. Keys are created per user in Preferences → Account Security → New API Key, require a description and a duration, and cannot last longer than three months, so long-running integrations must rotate them (Odoo: API keys). Odoo also offers methods to generate and revoke keys programmatically, which makes automated rotation possible.

The key acts with the permissions of its user. That is the most important design decision:

  • Create a dedicated integration user for the agent, with only the access groups it needs — for example read access to Sales and Inventory, no access to Payroll or Accounting settings.
  • For user-facing assistants, act on behalf of the real user wherever possible, so a salesperson's assistant sees exactly what the salesperson sees. Shared "super user" keys turn every prompt injection into a data breach.
  • Use record rules to limit which records the integration user can read, such as only one company in a multi-company setup.

A minimal JSON-2 call

import requests

ODOO = "https://erp.example.com"
HEADERS = {
    "Authorization": f"bearer {API_KEY}",
    "X-Odoo-Database": "prod",
    "Content-Type": "application/json",
}

def odoo(model: str, method: str, **params):
    r = requests.post(f"{ODOO}/json/2/{model}/{method}",
                      json=params, headers=HEADERS, timeout=20)
    r.raise_for_status()
    return r.json()

overdue = odoo("account.move", "search_read",
    domain=[["move_type", "=", "out_invoice"],
            ["payment_state", "in", ["not_paid", "partial"]],
            ["invoice_date_due", "<", "2026-07-25"]],
    fields=["name", "partner_id", "amount_residual", "invoice_date_due"],
    limit=50)

Every database also exposes a /doc page listing the models, fields and methods available for that specific installation — useful when writing tool definitions against customized databases.

Designing tools for the agent

Do not give the model a generic "call any Odoo method" tool. Wrap the API in a small set of task-oriented tools with clear descriptions, narrow parameters and compact results:

Tool Odoo calls behind it Notes
find_customer(query) res.partner search_read by name, email, VAT Returns id, name, email, salesperson, credit
get_order_status(order_ref) sale.order + related pickings and invoices Combines three models into one answer
stock_level(product, warehouse) stock.quant grouped read Available vs reserved quantities
overdue_invoices(customer_id?) account.move search_read Limited fields, sorted by due date
draft_quotation(customer_id, lines) sale.order create in draft state Write tool: creates a draft only, a human confirms

Rules that keep tools reliable:

  1. Read tools first, write tools later, and only after reviewing logs of real usage.
  2. Writes create drafts. The agent can prepare a quotation, a purchase order or an email; confirming, posting or sending stays a human action.
  3. Validate arguments on the server, not only in the tool schema.
  4. Return business language. payment_state: "partial" is fine for code; the tool can translate it to "partially paid" for the model.

Exposing Odoo through MCP

If several AI applications need Odoo — a chat assistant for sales, an internal agent for finance, a coding assistant for developers — wrap these tools in a Model Context Protocol server instead of rebuilding them per application. Any MCP-compatible host can then use the same tools with consistent permissions and logging. The protocol, server design and its security model are covered in MCP for business.

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("odoo")

@mcp.tool()
def get_order_status(order_ref: str) -> dict:
    """Status of a sales order by its reference (e.g. S00042): order state,
    delivery status with carrier tracking, and invoice/payment status."""
    order = odoo("sale.order", "search_read",
                 domain=[["name", "=", order_ref]],
                 fields=["state", "partner_id", "picking_ids", "invoice_ids"], limit=1)
    if not order:
        return {"error": f"No order {order_ref}. Check the reference format S00000."}
    return summarize_order(order[0])   # joins pickings and invoices into a compact dict

Use cases that pay off first

  • Sales assistant: order status, stock availability, customer history and draft quotations from an email.
  • Collections: overdue invoices by customer with suggested reminder emails for an accountant to review.
  • Purchasing: products below reorder point with draft purchase orders grouped by vendor.
  • Support: order and delivery status for support agents, combined with knowledge-base answers via retrieval, as described in RAG for business.
  • Management questions: "Revenue by product category this quarter versus last" via read-only grouped queries.

Guardrails

  • Least privilege for the integration user, enforced by Odoo groups and record rules.
  • Human confirmation for anything that posts, confirms, sends or pays.
  • Audit logging of every tool call with user, arguments and result size.
  • Rate limits and timeouts so an agent loop cannot overload Odoo workers — see Odoo performance tuning.
  • Prompt injection awareness: customer emails, notes and product descriptions are untrusted text. Our checklist is in AI agent security.
  • Key rotation: automate renewal before the three-month expiry and revoke keys immediately when an integration is retired.

Measuring whether the assistant helps

Treat the integration like any product feature and measure it from the first pilot week:

  • Task success rate: share of questions answered correctly or actions prepared without correction, checked on a sample of logged sessions.
  • Time saved: for example, minutes to answer an order-status question before and after.
  • Tool error rate: failed or retried calls per session, often a sign of unclear tool descriptions or missing permissions.
  • Human correction rate on drafts: how often quotations or purchase orders prepared by the agent need edits before confirmation.
  • Load on Odoo: API calls per minute and response times, to make sure the assistant doesn't slow down users.

We describe how to build a test set from real questions in How to evaluate LLM applications.

FAQ

Does this work with Odoo 17 or 18? Those versions use XML-RPC or JSON-RPC for external access. The architecture is the same; the transport differs, and you will migrate to JSON-2 when you upgrade.

Can we use Odoo's own AI features instead? Recent Odoo versions include built-in AI capabilities on Custom plans. They are a good fit inside Odoo screens; an external agent makes sense when you combine Odoo with other systems or need your own workflows.

Is it safe to let an AI create records in our ERP? Yes, with drafts and confirmation. The agent prepares, a person approves.

How long does an integration take? A read-only assistant with five or six tools typically takes two to four weeks, including the integration user, logging and testing on real questions.

Which language model should we use? The integration pattern is model-agnostic. Choose based on quality on your own evaluation set, cost per completed task and data processing terms; we compare cost levers in LLM cost optimization.

Can the agent work in Ukrainian? Yes. Modern language models handle Ukrainian well; pass the user's language in the request context (for example {"lang": "uk_UA"}) so Odoo returns translated field values and names.

Sources

  1. Odoo. External JSON-2 API.
  2. Odoo. External RPC API (deprecated).
  3. Odoo. Pricing.
  4. Model Context Protocol. Specification, revision 2026-07-28.
  5. OWASP. Secrets Management Cheat Sheet.