Odoo Manager Skill
🔐 URL, Database & Credential Resolution
URL Resolution
Odoo server URL precedence (highest to lowest):
- •
temporary_url— one-time URL for a specific operation - •
user_url— user-defined URL for the current session - •
ODOO_URL— environment default URL
This allows you to:
- •Switch between multiple Odoo instances (production, staging, client-specific)
- •Test against demo databases
- •Work with different client environments without changing global config
Examples (conceptual):
// Default: uses ODOO_URL from environment
{{resolved_url}}/xmlrpc/2/common
// Override for one operation:
temporary_url = "https://staging.mycompany.odoo.com"
{{resolved_url}}/xmlrpc/2/common
// Override for session:
user_url = "https://client-xyz.odoo.com"
{{resolved_url}}/xmlrpc/2/common
Database Resolution
Database name (db) precedence:
- •
temporary_db - •
user_db - •
ODOO_DB
Use this to:
- •Work with multiple databases on the same Odoo server
- •Switch between test and production databases
Username & Secret Resolution
Username precedence:
- •
temporary_username - •
user_username - •
ODOO_USERNAME
Secret (password or API key) precedence:
- •
temporary_api_keyortemporary_password - •
user_api_keyoruser_password - •
ODOO_API_KEY(if set) orODOO_PASSWORD
Important:
- •Odoo API keys are used in place of the password, with the usual login.
- •Store passwords / API keys like real passwords; never log or expose them.
Environment variables are handled via standard OpenClaw metadata: requires.env declares required variables (ODOO_URL, ODOO_DB, ODOO_USERNAME, ODOO_PASSWORD). ODOO_API_KEY is an optional environment variable used instead of the password when present; it is not listed in metadata and should simply be set in the environment when needed.
Resolved Values
At runtime the skill always works with:
- •
{{resolved_url}}— final URL - •
{{resolved_db}}— final database name - •
{{resolved_username}}— final login - •
{{resolved_secret}}— password or API key actually used to authenticate
These are computed using the precedence rules above.
🔄 Context Management
The
temporary_*anduser_*names are runtime context variables used by the skill logic, not OpenClaw metadata fields. OpenClaw does not have anoptional.contextmetadata key; context is resolved dynamically at runtime as described below.
Temporary Context (One-Time Use)
User examples:
- •"Pour cette requête, utilise l’instance staging Odoo"
- •"Utilise la base
odoo_demojuste pour cette opération" - •"Connecte-toi avec cet utilisateur uniquement pour cette action"
Behavior:
- •Set
temporary_*(url, db, username, api_key/password) - •Use them for a single logical operation
- •Automatically clear after use
This is ideal for:
- •Comparing data between two environments
- •Running a single check on a different database
Session Context (Current Session)
User examples:
- •"Travaille sur l’instance Odoo du client XYZ"
- •"Utilise la base
clientx_prodpour cette session" - •"Connecte-toi avec mon compte administrateur pour les prochaines opérations"
Behavior:
- •Set
user_*(url, db, username, api_key/password) - •Persist for the whole current session
- •Overridden only by
temporary_*or by clearinguser_*
Resetting Context
User examples:
- •"Reviens à la configuration Odoo par défaut"
- •"Efface mon contexte utilisateur Odoo"
Action:
- •Clear
user_url,user_db,user_username,user_password,user_api_key - •Skill falls back to environment variables (
ODOO_URL,ODOO_DB,ODOO_USERNAME,ODOO_PASSWORD/ODOO_API_KEY)
Viewing Current Context
User examples:
- •"Sur quelle instance Odoo es-tu connecté ?"
- •"Montre la configuration Odoo actuelle"
Response should show (never full secrets):
Current Odoo Context: - URL: https://client-xyz.odoo.com (user_url) - DB: clientxyz_prod (user_db) - Username: api_integration (user_username) - Secret: using API key (user_api_key) - Fallback URL: https://default.odoo.com (ODOO_URL) - Fallback DB: default_db (ODOO_DB)
⚙️ Odoo XML-RPC Basics
Odoo exposes part of its server framework over XML-RPC (not REST). The External API is documented here: https://www.odoo.com/documentation/18.0/fr/developer/reference/external_api.html
Two main endpoints:
- •
{{resolved_url}}/xmlrpc/2/common— authentication and meta calls - •
{{resolved_url}}/xmlrpc/2/object— model methods viaexecute_kw
1. Checking Server Version
Call version() on the common endpoint to verify URL and connectivity:
common = xmlrpc.client.ServerProxy(f"{resolved_url}/xmlrpc/2/common")
version_info = common.version()
Example result:
{
"server_version": "18.0",
"server_version_info": [18, 0, 0, "final", 0],
"server_serie": "18.0",
"protocol_version": 1
}
2. Authenticating
Use authenticate(db, username, password_or_api_key, {}) on the common endpoint:
uid = common.authenticate(resolved_db, resolved_username, resolved_secret, {})
uid is an integer user ID and will be used in all subsequent calls.
If authentication fails, uid is False / 0 — the skill should:
- •Inform the user that credentials or database are invalid
- •Suggest checking
ODOO_URL,ODOO_DB, username, and secret
3. Calling Model Methods with execute_kw
Build an XML-RPC client for the object endpoint:
models = xmlrpc.client.ServerProxy(f"{resolved_url}/xmlrpc/2/object")
Then use execute_kw with the following signature:
models.execute_kw(
resolved_db,
uid,
resolved_secret,
"model.name", # e.g. "res.partner"
"method_name", # e.g. "search_read"
[positional_args],
{keyword_args}
)
All ORM operations in this skill are expressed in terms of execute_kw.
🔍 Domains & Data Types (Odoo ORM)
Domain Filters
Domains are lists of conditions:
domain = [["field_name", "operator", value], ...]
Examples:
- •All companies:
[['is_company', '=', True]] - •Partners in France:
[['country_id', '=', france_id]] - •Leads with probability > 50%:
[['probability', '>', 50]]
Common operators:
- •
"=","!=",">",">=","<","<=" - •
"like","ilike"(case-insensitive) - •
"in","not in" - •
"child_of"(hierarchical relations)
Field Value Conventions
- •Integer / Float / Char / Text: use native types.
- •Date / Datetime: strings in
YYYY-MM-DDor ISO 8601 format. - •Many2one: usually send the record ID (
int) when writing; reads often return[id, display_name]. - •One2many / Many2many: use the Odoo command list protocol for writes (not fully detailed here; see Odoo docs if needed).
🧩 Generic ORM Operations (execute_kw)
Each subsection below shows typical user queries and the corresponding
execute_kw usage. They are applicable to any model (not only res.partner).
List / Search Records (search)
User queries:
- •"Liste tous les partenaires société"
- •"Cherche les commandes de vente confirmées"
Action (generic):
ids = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "search",
[domain],
{"offset": 0, "limit": 80}
)
Notes:
- •
domainis a list (can be empty[]to match all records). - •Use
offsetandlimitfor pagination.
Count Records (search_count)
User queries:
- •"Combien de partenaires sont des sociétés ?"
- •"Compte les tâches en cours"
Action:
count = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "search_count",
[domain]
)
Read Records by ID (read)
User queries:
- •"Affiche les détails du partenaire 7"
- •"Donne-moi les champs name et country_id pour ces IDs"
Action:
records = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "read",
[ids],
{"fields": ["name", "country_id", "comment"]}
)
If fields is omitted, Odoo returns all readable fields (often a lot).
Search and Read in One Step (search_read)
Shortcut for search() + read() in a single call.
User queries:
- •"Liste les sociétés (nom, pays, commentaire)"
- •"Montre les 5 premiers partenaires avec leurs pays"
Action:
records = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "search_read",
[domain],
{
"fields": ["name", "country_id", "comment"],
"limit": 5,
"offset": 0,
# Optional: "order": "name asc"
}
)
Create Records (create)
User queries:
- •"Crée un nouveau partenaire 'New Partner'"
- •"Crée une nouvelle tâche dans le projet X"
Action:
new_id = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "create",
[{
"name": "New Partner"
# other fields...
}]
)
Returns the newly created record ID.
Update Records (write)
User queries:
- •"Met à jour le partenaire 7, change son nom"
- •"Baisse la probabilité de ces leads"
Action:
success = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "write",
[ids, {"field": "new value", "other_field": 123}]
)
Notes:
- •
idsis a list of record IDs. - •All records in
idsreceive the same values.
Delete Records (unlink)
User queries:
- •"Supprime ce partenaire de test"
- •"Efface ces tâches temporaires"
Action:
success = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "unlink",
[ids]
)
Name-Based Search (name_search)
Useful for quick lookup on models with a display name (e.g. partners, products).
User queries:
- •"Trouve le partenaire dont le nom contient 'Agrolait'"
Action:
results = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "name_search",
["Agrolait"],
{"limit": 10}
)
Result is a list of [id, display_name].
👥 Contacts / Partners (res.partner)
res.partner is the core model for contacts, companies, and many business relations in Odoo.
List Company Partners
User queries:
- •"Liste toutes les sociétés"
- •"Montre les sociétés avec leur pays"
Action:
companies = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "search_read",
[[["is_company", "=", True]]],
{"fields": ["name", "country_id", "comment"], "limit": 80}
)
Get a Single Partner
User queries:
- •"Affiche le partenaire 7"
- •"Donne-moi le pays et le commentaire du partenaire 7"
Action:
[partner] = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "read",
[[7]],
{"fields": ["name", "country_id", "comment"]}
)
Create a New Partner
User queries:
- •"Crée un partenaire 'Agrolait 2' en tant que société"
- •"Crée un contact personne rattaché à la société X"
Minimal body:
partner_id = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "create",
[{
"name": "New Partner",
"is_company": True
}]
)
Additional fields examples:
- •
street,zip,city,country_id - •
email,phone,mobile - •
company_type("person"or"company")
Update a Partner
User queries:
- •"Change l’adresse du partenaire 7"
- •"Met à jour le pays et le téléphone"
Action:
models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "write",
[[7], {
"street": "New street 1",
"phone": "+33 1 23 45 67 89"
}]
)
Delete a Partner
User queries:
- •"Supprime le partenaire 999 de test"
Action:
models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "unlink",
[[999]]
)
🧱 Model Introspection (ir.model, ir.model.fields, fields_get)
Discover Fields of a Model (fields_get)
User queries:
- •"Quels sont les champs de res.partner ?"
- •"Montre les types et labels des champs pour ce modèle"
Action:
fields = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "fields_get",
[],
{"attributes": ["string", "help", "type"]}
)
The result is a mapping from field name to metadata:
{
"name": {"type": "char", "string": "Name", "help": ""},
"country_id": {"type": "many2one", "string": "Country", "help": ""},
"is_company": {"type": "boolean", "string": "Is a Company", "help": ""}
}
List All Models (ir.model)
User queries:
- •"Quels modèles sont disponibles dans ma base Odoo ?"
Action:
models_list = models.execute_kw(
resolved_db, uid, resolved_secret,
"ir.model", "search_read",
[[]],
{"fields": ["model", "name", "state"], "limit": 200}
)
state indicates whether a model is defined in code ("base") or created dynamically ("manual").
List Fields of a Specific Model (ir.model.fields)
User queries:
- •"Donne-moi la liste des champs du modèle res.partner via ir.model.fields"
Action (simplified):
partner_model_ids = models.execute_kw(
resolved_db, uid, resolved_secret,
"ir.model", "search",
[[["model", "=", "res.partner"]]]
)
fields_meta = models.execute_kw(
resolved_db, uid, resolved_secret,
"ir.model.fields", "search_read",
[[["model_id", "in", partner_model_ids]]],
{"fields": ["name", "field_description", "ttype", "required", "readonly"], "limit": 500}
)
⚠️ Error Handling & Best Practices
Typical Errors
- •Authentication failure: wrong URL, DB, username, or secret →
authenticatereturnsFalseor later calls fail. - •Access rights / ACLs: user does not have permission on a model or record.
- •Validation errors: required fields missing, constraints violated.
- •Connectivity issues: network errors reaching
xmlrpc/2/commonorxmlrpc/2/object.
The skill should:
- •Clearly indicate if the issue is with connection, credentials, or business validation.
- •Propose next steps (check env vars, context overrides, user rights).
Pagination
- •Use
limit/offsetonsearchandsearch_readto handle large datasets. - •For interactive use, default
limitto a reasonable value (e.g. 80).
Field Selection
- •Always send an explicit
fieldslist forread/search_readwhen possible. - •This reduces payload and speeds up responses.
Domains & Performance
- •Prefer indexed fields and simple operators (
=,in) for large datasets. - •Avoid unbounded searches without domain on very big tables when possible.
🚀 Quick End-to-End Examples
Example 1: Check Connection & List Company Partners
- •Resolve context:
{{resolved_url}},{{resolved_db}},{{resolved_username}},{{resolved_secret}} - •Call
version()on{{resolved_url}}/xmlrpc/2/common - •Authenticate to get
uid - •Call
execute_kwonres.partnerwithsearch_readand domain[['is_company', '=', True]]
Example 2: Create a Partner, Then Read It Back
- •Authenticate via
common.authenticate - •
createa newres.partnerwith{"name": "New Partner", "is_company": True} - •
readthat ID with fields["name", "is_company", "country_id"]
Example 3: Work on Another Database for One Operation
- •Set
temporary_urland/ortemporary_dbto point to another Odoo environment. - •Authenticate and perform the requested operation using resolved context.
- •Temporary context is cleared automatically.
📚 References & Capabilities Summary
- •Official Odoo External API documentation (XML-RPC): https://www.odoo.com/documentation/18.0/fr/developer/reference/external_api.html
- •Requires an Odoo plan with External API access (Custom plans; not available on One App Free / Standard).
This skill can:
- •Connect to Odoo via XML-RPC using password or API key.
- •Switch dynamically between multiple instances and databases using context.
- •Perform generic CRUD (
search,search_count,read,search_read,create,write,unlink) on any Odoo model viaexecute_kw. - •Provide ready-made flows for
res.partner(contacts / companies). - •Inspect model structures using
fields_get,ir.model, andir.model.fields. - •Apply best practices regarding pagination, field selection, and error handling.