Skip to content

Connecting Claude to Odoo via API: Complete Guide

DeployMonkey Team · March 22, 2026 14 min read

What You Will Build

By the end of this guide, you will have a Python script that connects Claude to your Odoo instance. Claude will be able to search records, read data, create entries, and generate reports — all through Odoo's XML-RPC API. This is the foundation for building any custom AI agent for Odoo.

Prerequisites

  • Python 3.10+
  • An Odoo instance (deploy one free on DeployMonkey)
  • Anthropic API key (console.anthropic.com)
  • Admin credentials for your Odoo instance

Step 1: Connect to Odoo

import xmlrpc.client

ODOO_URL = 'https://your-odoo.deploymonkey.com'  # Your Odoo URL
DB = 'your_database'
USER = 'admin'
PASSWORD = 'your_password'

# Authenticate
common = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/common')
uid = common.authenticate(DB, USER, PASSWORD, {})
print(f'Authenticated: uid={uid}')

# Get models proxy
models = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/object')

def odoo_call(model, method, args=None, kwargs=None):
    """Helper to call Odoo methods."""
    return models.execute_kw(
        DB, uid, PASSWORD, model, method,
        args or [], kwargs or {}
    )

Step 2: Define Odoo Tools for Claude

import anthropic
import json

client = anthropic.Anthropic()

# Define tools that Claude can use
tools = [
    {
        "name": "odoo_search",
        "description": "Search for records in an Odoo model. Returns record IDs matching the domain filter.",
        "input_schema": {
            "type": "object",
            "properties": {
                "model": {"type": "string", "description": "Odoo model name, e.g. 'sale.order'"},
                "domain": {"type": "array", "description": "Search domain, e.g. [['state','=','sale']]"},
                "limit": {"type": "integer", "description": "Max results", "default": 10}
            },
            "required": ["model", "domain"]
        }
    },
    {
        "name": "odoo_read",
        "description": "Read fields from Odoo records by ID.",
        "input_schema": {
            "type": "object",
            "properties": {
                "model": {"type": "string"},
                "ids": {"type": "array", "items": {"type": "integer"}},
                "fields": {"type": "array", "items": {"type": "string"}}
            },
            "required": ["model", "ids", "fields"]
        }
    },
    {
        "name": "odoo_read_group",
        "description": "Aggregate data from an Odoo model (like SQL GROUP BY).",
        "input_schema": {
            "type": "object",
            "properties": {
                "model": {"type": "string"},
                "domain": {"type": "array"},
                "fields": {"type": "array", "items": {"type": "string"}},
                "groupby": {"type": "array", "items": {"type": "string"}}
            },
            "required": ["model", "domain", "fields", "groupby"]
        }
    },
    {
        "name": "odoo_count",
        "description": "Count records matching a domain.",
        "input_schema": {
            "type": "object",
            "properties": {
                "model": {"type": "string"},
                "domain": {"type": "array"}
            },
            "required": ["model", "domain"]
        }
    }
]

Step 3: Handle Tool Calls

def execute_tool(tool_name, tool_input):
    """Execute an Odoo tool call and return the result."""
    if tool_name == "odoo_search":
        ids = odoo_call(tool_input['model'], 'search',
            [tool_input['domain']], {'limit': tool_input.get('limit', 10)})
        return json.dumps(ids)

    elif tool_name == "odoo_read":
        records = odoo_call(tool_input['model'], 'read',
            [tool_input['ids'], tool_input['fields']])
        return json.dumps(records, default=str)

    elif tool_name == "odoo_read_group":
        result = odoo_call(tool_input['model'], 'read_group',
            [tool_input['domain'], tool_input['fields'], tool_input['groupby']])
        return json.dumps(result, default=str)

    elif tool_name == "odoo_count":
        count = odoo_call(tool_input['model'], 'search_count',
            [tool_input['domain']])
        return json.dumps(count)

    return json.dumps({"error": f"Unknown tool: {tool_name}"})

Step 4: Chat Loop with Tool Use

def chat_with_odoo(question: str) -> str:
    """Ask Claude a question about your Odoo data."""
    messages = [{"role": "user", "content": question}]

    while True:
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            system="You are an Odoo data analyst. Use the available tools to "
                   "query the Odoo database and answer business questions. "
                   "Always use read_group for aggregations instead of reading "
                   "individual records.",
            tools=tools,
            messages=messages
        )

        # Check if Claude wants to use a tool
        if response.stop_reason == "tool_use":
            # Process each tool call
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = execute_tool(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result
                    })

            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # Claude has the final answer
            return response.content[0].text

# Example usage
print(chat_with_odoo("What were our top 5 customers by revenue this quarter?"))
print(chat_with_odoo("How many open support tickets do we have?"))
print(chat_with_odoo("What is our inventory value by warehouse?"))

Security Best Practices

  • Use a dedicated read-only Odoo user — Do not use admin credentials for analytics queries
  • Limit accessible models — Define which models the tools can access
  • Set query timeouts — Prevent long-running queries from blocking the server
  • Log all queries — Audit trail for compliance and debugging
  • Rate limit tool calls — Prevent runaway agents from overwhelming the API

Going Further

  • Add write tools (create, write) for automation agents — with appropriate guardrails
  • Add SSH tools for server monitoring agents
  • Add file tools for coding agents
  • Deploy as a web service for team access
  • Connect to DeployMonkey's built-in AI agent for a managed experience