n8n serveix per a allò que passa al voltant d'Odoo: recollir un formulari extern, avisar un xat, muntar un informe diari. Allò que canvia l'estat de negoci a dins (preus, reserves d'estoc, validacions d'albarà, conciliació de pagaments) demana un mòdul. La frontera es veu en executar les dues coses contra un Odoo de debò: aquest article surt d'un laboratori propi amb Odoo 19.0-20260630 i n8n 2.30.6.
Idea clau: un flux que crida l'API d'Odoo no executa la seva interfície. Pot rebre un 200 amb un assistent a dins i creure's que ha validat un albarà que continua obert.
Com parla n8n amb Odoo: quatre camins, no un
- XML-RPC:
/xmlrpc/2/commonper autenticar i/xmlrpc/2/objectper aexecute_kw. El de la documentació. - JSON-RPC: un
POST /jsonrpcambservice,methodiargs. El mateix servei en JSON. - Sessió web:
/web/session/authenticatei/web/dataset/call_kwamb la cookie. Allò que fa el teu navegador. - L'API JSON-2, només a Odoo 19:
POST /json/2/<modelo>/<método>ambAuthorization: bearer <clave API>iX-Odoo-Database. Sense sessió: els arguments van com a camps amb nom,contextinclòs.
Els tres primers continuen funcionant a la 19, però el servidor avisa al log: «The /xmlrpc, /xmlrpc/2 and /jsonrpc endpoints are deprecated in Odoo 19 and scheduled for removal in Odoo 22». Un flux nou contra una 19 hauria d'anar per /json/2; si serveix també a 17 i 18, per la sessió.
# Sesion + call_kw: funciona igual en 17, 18 y 19.
POST /web/session/authenticate
{"params":{"db":"lab","login":"n8n_bot2","password":"..."}}
-> 200 {"result":{"uid":6,"server_version":"19.0-20260630"}} # + cookie
POST /web/dataset/call_kw # con esa cookie
{"params":{"model":"crm.lead","method":"create",
"args":[{"name":"Lead web","type":"lead"}],
"kwargs":{"context":{"lang":"es_ES"}}}}
-> 200 {"result": 10}
# Clave API en la cabecera, sin sesion: solo Odoo 19.
POST /json/2/crm.lead/create
Authorization: bearer <clave> X-Odoo-Database: lab
{"vals_list":[{"name":"Lead via /json/2 (API key)"}]}
-> 200 [9]
# Sin cabecera -> 401 "User not authenticated, use an API Key with a
# Bearer Authorization header."
# Metodo privado -> 403 "Private methods (such as
# 'crm.lead._compute_display_name') cannot be called remotely." El node Odoo de n8n 2.30.6 tria per tu segons la credencial: la de clau API va per /json/2; la clàssica va per /jsonrpc amb execute, posicional, que no accepta kwargs i no pot passar context. La credencial de clau API avisa, a més, que requereix «Odoo 19+ and a Custom pricing plan».
El node HTTP Request, tal com va quedar
Fixa't en on no és la clau: el cos només porta dades i la capçalera Authorization la posa una credencial de n8n, que es xifra i es rota a part.
// Lineas del nodo real, copiadas de `n8n export:workflow`.
"type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2,
"url": "http://odoo:8069/json/2/crm.lead/create",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": { "parameters": [
{ "name": "X-Odoo-Database", "value": "lab" } ] },
"jsonBody": "={{ JSON.stringify({ vals_list: [{ name: 'Web: ' + $json.body.empresa, contact_name: $json.body.nombre, email_from: $json.body.email, description: $json.body.mensaje, type: 'lead' }] }) }}",
"credentials": { "httpHeaderAuth": { "name": "Odoo lab - API key (header)" } }
// La clave no esta aqui: la pone esa credencial.El flux tampoc entra com a administrador, sinó com a usuari tècnic amb els grups justos i el topall que Odoo 19 posa a la seva clau:
# Usuario tecnico nuevo (solo grupo de usuario interno):
POST /json/2/crm.lead/create
-> 403 "No puede crear informes 'Lead' (crm.lead) ...
- Sales/Administrator
- Sales/User: Own Documents Only"
# Tras anadirle sales_team.group_sale_salesman: -> 200
# Su clave API tampoco dura lo que uno quiera:
_generate(..., +200 dias) -> "No puede exceder 90.0 dias."
_generate(..., sin fecha) -> "La clave API debe tener una
fecha de vencimiento"Aquest topall de 90 dies és la data en què el teu flux deixarà de funcionar sense que ningú toqui res. Apunta-la el dia que crees la clau.
Cinc automatitzacions que sí caben en un flux
Totes comparteixen el mateix: el fet ja ha passat i el flux només el mou de lloc.
| Cas | Disparador | Crida a Odoo |
|---|---|---|
| Lead des d'un formulari extern | Webhook de n8n | crm.lead / create |
| Avís al xat en validar un albarà | Automatització d'Odoo amb acció de webhook | Cap: Odoo empeny |
| Contactes cap a una eina de màrqueting | Programat, cada nit | res.partner / search_read per write_date |
| Alerta d'estoc baix | Programat, cada matí | product.product / search_read de qty_available |
| Informe diari a direcció | Programat | read_group sobre sale.order |
El segon cas té lletra petita. L'acció de webhook envia _model, _id i el nom de l'acció, i és «llançar i oblidar»: un POST d'un segon, sense reintent. A la 19 surt després del commit; a la 17 i la 18 va dins de la transacció, de manera que un n8n lent retarda l'usuari.
# Accion de servidor state='webhook' al crear un lead.
INFO Webhook call to http://n8n:5678/webhook/odoo-lead
INFO Webhook call to http://n8n:5678/webhook/odoo-lead - succeeded
# Apuntando a un flujo borrado (el lead se crea igual):
WARNING Webhook call failed: 404 Client Error: Not Found for url:
http://n8n:5678/webhook/flujo-borrado
WARNING Webhook call timed out after 1s - it may or may not have failed.Cinc que no, i el motiu exacte
- Calcular preus o descomptes. Odoo els calcula des de la tarifa; si el flux els calcula fora, en canviar la tarifa hi ha dues veritats.
- Reservar estoc per a diversos canals. Odoo resol la cursa dins d'una transacció; un flux que llegeix, decideix i escriu per separat sobrevèn.
- Conciliar pagaments. Aparellar i assentar van junts: si un es fa fora i l'altre dins, un tall de xarxa deixa mitja conciliació.
- Allò que algú vagi a auditar. L'historial de n8n no és el chatter: «Qui ha canviat això i per què?» es respon al document.
- Allò que depengui d'un camp que canvia entre versions. Ho vam comprovar escrivint a Odoo 19 allò que funcionava a la 18.
El cas que ho resumeix millor: validar un albarà des d'un flux. Vam preparar-ne un de 10 unitats amb 4 reservades i vam cridar button_validate, el mateix que el botó.
# WH/OUT/00003: 10 pedidas, 4 reservadas.
POST /web/dataset/call_kw
{"model":"stock.picking","method":"button_validate","args":[[2]]}
-> 200 {"result": {"name": "¿Crear entrega parcial?",
"type": "ir.actions.act_window",
"res_model": "stock.backorder.confirmation", ...}}
read -> [{"name":"WH/OUT/00003","state":"assigned"}] # sigue abiertoHTTP 200, sense error, i un albarà en assigned. Per a n8n el pas ha sortit verd; per al magatzem, la comanda no ha sortit. Un mòdul reconeix l'assistent i ho deixa escrit al document: la mateixa frontera que a Odoo 19 davant dels connectors de middleware.
I els camps que canvien de nom entre versions, allò que trenca un flux estable després d'una migració:
# Odoo 19, escribiendo lo que funcionaba en 17 y 18:
res.users write {"groups_id": [[4, 1]]}
-> "Invalid field 'groups_id' in 'res.users'" # en 19 se llama group_ids
stock.move create {"name": "Latiguillo", "product_id": 1, ...}
-> "Invalid field 'name' in 'stock.move'" # en 17 y 18 era obligatorioMantenim el mateix codi a Odoo 19, 18 i 17 en 105 mòduls publicats: aquestes diferències les paga el mòdul, no el client. És també el límit d'Odoo Studio quan el procés deixa de ser un camp i passa a ser una regla.
Els quatre errors que veiem sempre
- El bucle. Una automatització avisa n8n en canviar un camp, el flux escriu a Odoo i allò escrit torna a disparar-la. Es talla amb una condició al disparador.
- La credencial en text pla. Ni al cos del node ni en un node de codi: en una credencial de n8n, amb un usuari tècnic de grups mínims.
- Sense idempotència. Un webhook reintentat des de l'altra banda crea el registre una altra vegada: tres
POSTidèntics al nostre flux van donar tres leads. - Sense reintent de debò. Un node no reintenta tret que activis Retry On Fail, i el motor limita els intents a cinc i l'espera a cinc segons. Sobra per a un xat; no per a un transportista: el nostre mòdul de Correos Express reintenta als 5, 15, 30, 60, 120 i 240 minuts.
La solució no és de n8n, és de disseny: guarda a Odoo la referència externa i comprova-la abans de crear. En un mòdul és una restricció única a la base de dades; en un flux, una lectura prèvia amb finestra de cursa.
Quan un flux es converteix en mòdul
El flux és una manera barata de descobrir si el procés mereix existir. El senyal que s'ha quedat curt és un d'aquests sis:
- Escriu un camp que Odoo calcula pel seu compte.
- Dues escriptures han de passar juntes o no passar.
- Algú auditarà dins d'Odoo què ha passat i quan.
- El procés ha de sobreviure a la propera migració.
- Cal poder repetir la crida sense duplicar res.
- L'usuari necessita reintentar des del propi document.
Arribats a aquest punt no es llença el flux: es porta al mòdul la part que toca l'estat de negoci i n8n es queda amb la vora. Passa igual a automatitzar tasques administratives i a la integració amb Signaturit.
Resum: si el flux cau i l'únic que passa és que algú no rep un avís, n8n serveix. Si cau i el magatzem o el llibre de factures queden en un estat que ningú sap llegir, allò era un mòdul.
Preguntes freqüents
Puc passar l'idioma o la companyia a la crida?
Pels tres camins moderns sí: kwargs.context a call_kw i a execute_kw, i un camp context a /json/2 (un país surt «Spain» amb en_US i «España» amb es_ES). No pots amb la credencial clàssica, que crida execute, posicional.
On guardo la clau API i quant dura?
En una credencial de n8n, mai al cos del node. A Odoo 19 la clau d'un usuari no administrador necessita caducitat, i el topall el fixa el grup: 90 dies per a un usuari intern.
Es trenca el meu flux en migrar d'Odoo 18 a 19?
Depèn dels camps que toqui, i no avisa: res.users.groups_id va passar a group_ids i stock.move.name, obligatori a la 17 i la 18, no existeix a la 19. Prova els teus fluxos contra una còpia.
Enllaços útils dins de FlexigoTech
Què fem sobre això
Tens fluxos de n8n contra Odoo i ja no saps quins aguanten?
Els revisem, et diem quins es queden com estan i quins cal portar a un mòdul, i fem el mòdul si cal. Escriu a comercial@flexigobe.com o truca al +34 616 809 504.

