Install
openclaw skills install openclaw-odooClawHub Security found sensitive or high-impact capabilities. Review the scan results before using.
Full-featured Odoo 17/18/19 ERP connector for OpenClaw — Sales, CRM, Purchase, Inventory, Projects, HR, Fleet, Manufacturing (80+ operations, TypeScript plugin, XML-RPC integration).
openclaw skills install openclaw-odooFull-featured Odoo 17/18/19 ERP integration for OpenClaw. Control your entire business via natural language chat commands.
📦 Full Source Code: https://github.com/tuanle96/openclaw-odoo
npx clawhub install openclaw-odoo
The Odoo ERP Connector bridges OpenClaw and Odoo 17/18/19, enabling autonomous, chat-driven control over 153+ business modules including:
All operations use smart actions that handle fuzzy matching and auto-creation workflows.
The connector handles fuzzy/incomplete requests with intelligent find-or-create logic.
Example: "Create quotation for Rocky with product Rock"
The system:
ilike matching)This pattern applies across all smart actions:
create_quotation — customer + productscreate_invoice — customer + productscreate_purchase — vendor + productscreate_lead — partner (optional)create_task — project + taskcreate_employee — departmentcreate_event — event only (no dependencies)OdooClient — Low-level XML-RPC wrapper
search(), searchRead(), create(), write(), unlink() methodsModel Ops Classes — Business logic for each module
PartnerOps — Customers/suppliersSaleOrderOps — Quotations and sales ordersInvoiceOps — Customer invoicesInventoryOps — Products and stockCRMOps — Leads and opportunitiesPurchaseOrderOps — POs and vendorsProjectOps — Projects and tasksHROps — Employees, departments, expensesManufacturingOps — BOMs and MOsCalendarOps — Events and meetingsFleetOps — Vehicles and odometerEcommerceOps — Website orders and productsSmartActionHandler — High-level find-or-create workflows
The connector auto-detects required vs. optional fields:
OdooError with field name{
"url": "http://localhost:8069",
"db": "your_database",
"username": "api_user@yourcompany.com",
"apiKey": "your_api_key_from_odoo_preferences",
"timeout": 60,
"maxRetries": 3,
"pollInterval": 60,
"logLevel": "INFO",
"webhookPort": 8070,
"webhookSecret": ""
}
Config keys are camelCase only.
Alternatively, set in .env:
ODOO_URL=http://localhost:8069
ODOO_DB=your_database
ODOO_USERNAME=api_user@yourcompany.com
ODOO_API_KEY=your_api_key
The client auto-loads from .env if plugin config is missing.
import { OdooSession } from "./odoo/session";
import { loadOdooConfig } from "./odoo/config";
import { SmartActionHandler } from "./odoo/smart-actions";
// Load config from plugin config or env
const { config } = loadOdooConfig({ url: "...", db: "...", username: "...", apiKey: "..." });
const session = await OdooSession.connect(config);
// Test connection
const status = await session.testConnection();
console.log(`Connected to Odoo ${status.server_version}`);
// Use smart actions for natural workflows
const smart = new SmartActionHandler(session);
// Create a quotation with fuzzy partner and product matching
const result = await smart.smartCreateQuotation(
"Rocky",
[{ name: "Rock", quantity: 5, price_unit: 19.99 }],
{ notes: "Fuzzy match quotation" }
);
console.log(result.summary);
// Output: "Created quotation QT-001 for new customer Rocky with 1 × Rock at $19.99"
// Find-or-create a customer
const partnerResult = await smart.findOrCreatePartner("Acme Corp", {
isCompany: true,
defaults: { city: "New York" }
});
const partner = partnerResult.partner;
const created = partnerResult.created;
// Find-or-create a product
const productResult = await smart.findOrCreateProduct("Widget X", {
list_price: 49.99,
type: "consu"
});
const product = productResult.product;
// Smart quotation (auto-creates customer & products)
const orderResult = await smart.smartCreateQuotation(
"Rocky",
[
{ name: "Product A", quantity: 10 },
{ name: "Product B", quantity: 5, price_unit: 25.0 }
],
{ notes: "Created via smart action" }
);
const order = orderResult.order;
console.log(`Order ${order.name} created`);
// Smart lead creation
const leadResult = await smart.smartCreateLead("New Prospect", {
contactName: "John Doe",
email: "john@prospect.com",
expectedRevenue: 50000.0
});
// Smart task creation (auto-creates project if needed)
const taskResult = await smart.smartCreateTask(
"Website Redesign",
"Fix homepage",
{ description: "Update hero section" }
);
// Smart employee creation (auto-creates department if needed)
const empResult = await smart.smartCreateEmployee("Jane Smith", {
jobTitle: "Developer",
departmentName: "Engineering"
});
// Smart event creation
const eventResult = await smart.smartCreateEvent(
"Team Standup",
"2026-03-01T10:00:00",
{ location: "Room A", attendeeNames: ["Alice", "Bob"] }
);
// Search and read records
const partners = await client.searchRead(
"res.partner",
[["is_company", "=", true]],
["name", "email"],
{ limit: 10 }
);
// Create a record
const id = await client.create("sale.order", {
partner_id: 42,
order_line: [[0, 0, { product_id: 7, product_uom_qty: 10, price_unit: 49.99 }]]
});
// Update a record
await client.write("sale.order", id, { note: "Updated via API" });
// Delete a record
await client.unlink("sale.order", [id]);
// Execute workflow actions
await client.execute("sale.order", "action_confirm", [[id]]);
// Get field definitions
const fields = await client.fieldsGet("product.template");
The connector uses typed error classes:
import {
OdooError,
OdooAuthenticationError,
OdooRecordNotFoundError,
OdooAccessError,
OdooValidationError,
OdooConnectionError
} from "./odoo/errors";
try {
const result = await smart.smartCreateQuotation("Acme", [{ name: "Widget" }]);
} catch (e) {
if (e instanceof OdooAuthenticationError) {
console.error(`Authentication failed: ${e.message}`);
} else if (e instanceof OdooRecordNotFoundError) {
console.error(`Record not found: ${e.message}`);
} else if (e instanceof OdooAccessError) {
console.error(`Permission denied: ${e.message}`);
} else if (e instanceof OdooValidationError) {
console.error(`Validation error: ${e.message}`);
} else if (e instanceof OdooConnectionError) {
console.error(`Connection error: ${e.message}`);
} else if (e instanceof OdooError) {
console.error(`Odoo error: ${e.message}`);
}
}
The plugin registers 8 tools accessible via natural language:
| Tool | Description |
|---|---|
odoo_health | Config sanity check (no network) |
odoo_test_connection | Live XML-RPC connectivity check |
odoo_search | Search & read records with domain filters |
odoo_create | Create records on any model |
odoo_update | Update existing records |
odoo_delete | Delete records |
odoo_workflow | Execute model methods (confirm/cancel/post) |
odoo_smart_action | High-level find-or-create workflows |
User: "Create a quote for Acme Corp with 10 Widgets at $50 each"
OpenClaw → odoo_smart_action (create_quotation):
1. Search for customer "Acme Corp"
2. Search for product "Widgets"
3. Create quotation with both
4. Return summary
Result: "✅ Created quotation QT-001 for Acme Corp with 10 × Widgets at $50"
User: "Show me the sales pipeline"
OpenClaw → odoo_search (crm.lead):
- Query all leads/opportunities
- Group by stage
- Calculate total revenue by stage
- Return formatted summary
Result: "Qualified: $50k | Proposal: $100k | Negotiation: $75k | Total: $225k"
User: "What products are low on stock?"
OpenClaw → odoo_search (product.template):
- Query products with stock < reorder point
- List each product, stock level, reorder point
- Suggest PO quantities
Result: "Widget X: 5 on hand (min 20) | Component Y: 0 on hand (min 10)"
openclaw-odoo/
├── plugin/
│ ├── openclaw.plugin.json # Plugin manifest + config schema
│ ├── package.json # @openclaw/openclaw-odoo@2.0.0
│ ├── tsconfig.json
│ ├── src/
│ │ ├── index.ts # Entry point (registers 8 tools)
│ │ ├── odoo/ # Core: client, config, errors, retry
│ │ │ ├── client.ts # OdooClient (XML-RPC wrapper)
│ │ │ ├── config.ts # Configuration loader
│ │ │ ├── errors.ts # Typed error classes
│ │ │ ├── retry.ts # Retry logic
│ │ │ ├── smart-actions.ts # SmartActionHandler
│ │ │ └── models/ # 12 model operation classes
│ │ ├── tools/ # 8 OpenClaw tools
│ │ ├── services/ # Poller + webhook
│ │ ├── openclaw/ # OpenClaw API helpers + integration
│ │ └── utils/ # Validators + formatting
│ ├── tests/ # 18 tests (Vitest)
│ ├── skills/odoo/SKILL.md # This file (bundled AI context)
│ └── dist/ # Compiled output (CommonJS)
├── README.md # User setup guide
├── CHANGELOG.md # Version history
├── RELEASE.md # Release plan
├── GAP_ANALYSIS.md # Multi-version compatibility audit
└── odoo-src/ # Odoo source for analysis
cd plugin
pnpm install
pnpm build # TS → dist/ (CommonJS)
pnpm run typecheck # tsc --noEmit (strict)
pnpm test # vitest run (18 tests)
SmartActionHandler class (in src/odoo/smart-actions.ts)findOrCreatePartner() / findOrCreateProduct() primitives for dependenciessummary, the main record, and creation detailsActionSchema union in src/tools/odoo-smart-action.tsswitch statement to route the actiontests/Example:
async smartCreateInvoice(
customerName: string,
lines: Array<Record<string, unknown>>,
options: { invoiceDate?: string; extra?: Record<string, unknown> } = {},
): Promise<Record<string, unknown>> {
// Find or create customer
const customerResult = await this.findOrCreatePartner(customerName);
const customer = customerResult.partner;
// Find or create products
const products = [];
for (const line of lines) {
const prodResult = await this.findOrCreateProduct(line.name as string, line);
products.push(prodResult);
}
// Create invoice with resolved IDs
const invoiceId = await this.client.create("account.move", {
partner_id: customer.id,
move_type: "out_invoice",
invoice_date: options.invoiceDate,
invoice_line_ids: products.map((p, i) => [
0, 0, {
product_id: p.product.id,
quantity: (lines[i] as any).quantity ?? 1,
price_unit: (lines[i] as any).price_unit ?? 0,
},
]),
...options.extra,
});
return {
summary: `Created invoice for ${customer.name}`,
invoice: { id: invoiceId },
customer: customerResult,
products,
};
}
url, db, username, apiKey in confighttp://your-odoo-url/webproduct_tmpl_id, not product_id)name fieldodoo_search tool with id directlydate_from, date_toThis connector is part of the OpenClaw project. For issues, questions, or contributions, visit github.com/tuanle96/openclaw-odoo.
Last Updated: 2026-02-23
Odoo Versions: 17, 18, 19
Runtime: Node.js (TypeScript)
Status: Production Ready