Skip to main content

Odoo

Read, create and update records in any Odoo model - contacts, leads, orders, invoices, projects, stock.

Connect an Odoo database (Odoo Online, Odoo.sh or self-hosted) over its External API and work any model it has installed: search with Odoo's own domain syntax, read and count records, create and update them, post to a record's chatter, and call model methods like confirming an order or posting an invoice. Because Odoo is a database with an ORM rather than a fixed API, the tools are generic - List models and Describe model are how you find what this particular database has. Everything runs as the connected Odoo user, whose access rights are the ceiling.

Connect

Credential fieldRequiredWhere it comes from
Login (USERNAME)YesThe Odoo user's login - their email address in almost every deployment.
API key (API_KEY)YesAn API key from Preferences → Account Security → Developer API Keys. Odoo shows it once. A password works only on a server with no two-factor authentication.
SettingRequiredWhat it is
Base URL (BASE_URL)YesThe instance's full address, e.g. https://acme.odoo.com or a self-hosted https://erp.acme.com - every request stays on this host. Not a bare subdomain.
Database (DATABASE)YesThe Odoo database name. Usually the subdomain on Odoo Online; on a self-hosted server, whichever of the host's databases this connection is for.

Tools

ToolAccessWhat it does
Who am I (yekar.odoo.whoami)ReadThe Odoo identity this connection acts as - uid, name, login, company and server version. EVERY action this integration takes runs as this user, so their access rights and record rules are the ceiling for everything: a record they cannot see does not exist as far as these tools are concerned, and a count they cannot see is smaller than the truth. Call this first to verify a connection.
List models (yekar.odoo.list-models)ReadThe models installed in this database - the index for every other tool here, since a model name is what they all take. Search by technical name or label. total is Odoo's own count over the whole filter, so a page never has to be mistaken for the census. Common starting points: res.partner, res.users, res.company, crm.lead, sale.order, sale.order.line, purchase.order, account.move.
Describe model (yekar.odoo.describe-model)ReadA model's fields - technical name, label, type, required/readonly, the related model for relational fields, and the accepted values for selection fields. Read this before searching or writing: field names are technical (partner_id, not "Customer"), and a selection field rejects any value outside its list. Mature models carry hundreds of fields, so at most 200 are returned and total says how many exist - narrow with q rather than assuming the list is complete.
Search records (yekar.odoo.search-records)ReadRead records from any Odoo model with a search domain - the workhorse of this integration. total is Odoo's own count over the same domain as the rows, so a page is never mistaken for the whole set, and hasMore says whether to raise the offset. Two Odoo conventions come back untranslated and matter when reading the values: an empty field is false (not null), and a many2one is [id, "Display Name"] while an x2many is a list of ids. ARCHIVED records are excluded unless you ask for them - a count that ignores them is smaller than the database.
Count records (yekar.odoo.count-records)ReadHow many records match a domain, counted by Odoo in SQL - no rows fetched, no page size involved. Use this whenever the answer is a number: counting a page of Search records would saturate at the page size and report a cap as a fact. The count is what the CONNECTED USER can see (record rules apply) and excludes archived records unless you ask for them.
Get records (yekar.odoo.get-record)ReadRead specific records by id. Ids that no longer exist - or that the connected user's record rules hide - are returned in missingIds rather than silently dropped, so a short result is never mistaken for a complete one. Values use Odoo's own encoding: an empty field is false, a many2one is [id, "Display Name"], an x2many a list of ids. Long text values are capped and named in truncatedFields.
Create record (yekar.odoo.create-record)WriteCreate one record on any model. Odoo runs the model's own onchange-free create logic, so required fields, selection values and record rules all apply - a rejection comes back as an invalid-input error naming the field. The new id is returned even if reading the record back afterwards fails, because the create itself already committed.
Update records (yekar.odoo.update-record)WriteWrite field values to one or more records. Only the fields you name change. The same values go to every id, so this is a bulk edit when several are passed. To ARCHIVE rather than delete - Odoo's usual answer for a record that must stop appearing but cannot be destroyed - write {"active": false}. Note that Odoo's write returns nothing but success, so the result echoes what was requested; read the records back if you need the stored values.
Delete records (yekar.odoo.delete-record)WriteDelete records PERMANENTLY. There is no undo and no recycle bin: Odoo drops the rows. Prefer archiving instead - Update records with {"active": false} hides a record everywhere while keeping its history, and it is what Odoo itself does for customers, products and employees that are no longer in use. Many records cannot be deleted at all: anything a posted invoice, a confirmed order or an accounting entry references is protected by the database, and that refusal comes back as a conflict rather than a silent no-op. Deletion is all-or-nothing - if one id fails, none are removed.
Post message (yekar.odoo.post-message)WritePost to a record's chatter - the message log on leads, orders, invoices, partners and tasks. The audience is an explicit argument, not a default: notifyFollowers: false writes an internal log note that only people with access to the record can see, while notifyFollowers: true posts a message that notifies the record's followers, which on a customer-facing record means EMAIL LEAVES THE SYSTEM. Choose deliberately, and never repeat an internal note to a customer.
Call model method (yekar.odoo.call-method)WriteThe escape hatch: call any public method on any model. This is how Odoo work that is not plain CRUD gets done - confirming a sale order, posting an invoice, running read_group for aggregates, resolving a name to an id with name_search. It is UNRESTRICTED: whatever the method does, it does, and the connected user's rights are the only limit - so it is treated as a write regardless of the method named. Prefer the specific tools (Search records, Update records, Post message) when they fit; use this when they do not. Results over 60000 characters are withheld and flagged rather than truncated silently.

Notes

  • In Odoo open Preferences (your avatar) → Account Security → Developer API Keys → New API Key, and paste it here together with the login (your email). The key is shown once.
  • Set the Base URL to the instance's full address - https://acme.odoo.com, or a self-hosted https://erp.acme.com (including a subdirectory if it is served under one). A bare subdomain will not work.
  • Set the Database to the database name. On Odoo Online it is normally the subdomain of your URL; on a self-hosted server one host can serve several databases and only you know which. Getting it wrong looks exactly like a wrong password - Odoo reports an unknown database as a denied login.
  • An API key carries the FULL rights of the user who created it and cannot be scoped further, so create it on a user whose access rights match what these automations should be able to do. That user's record rules are what every search, count and write is limited to.
  • If the account has two-factor authentication enabled, an API key is the only thing that will authenticate - the password will not.
  • The tools take technical model and field names (res.partner, res.users, res.company, crm.lead; partner_id, not "Customer"). Use List models and Describe model to find them rather than guessing.
  • Deleting is permanent and has no undo. Odoo's own convention is to ARCHIVE instead - set the record's active field to false with Update records.
  • Posting to a record's chatter with notifyFollowers = true sends email to that record's followers, including customers. An internal log note (false) does not.