Odoo · API · Seguridad
En JSON-RPC / XML-RPC vimos cómo integrar sistemas externos con los endpoints clásicos. Odoo 19 los marca como obsoletos (con retirada prevista para Odoo 22) y ofrece una API nueva, /json/2, que se autentica únicamente con API keys. Esta entrada repasa cómo funciona, cómo gestionar las claves y qué hay que saber sobre su scope antes de confiar en él.
JSON-2
Un endpoint por modelo y método
La API nueva expone cada método público del ORM en POST /json/2/<modelo>/<método>. Los argumentos van por nombre en el cuerpo JSON, los ids del recordset en su propia clave, y la autenticación en una cabecera Authorization: bearer. No hay sesión, ni login previo, ni el sobre execute_kw de la API antigua.
import requests
BASE = "https://erp.ejemplo.com"
HEADERS = {"Authorization": f"bearer {API_KEY}"}
# Buscar y leer en una sola llamada
resp = requests.post(
f"{BASE}/json/2/res.partner/search_read",
headers=HEADERS,
json={
"domain": [["is_company", "=", True]],
"fields": ["name", "vat"],
"limit": 10,
},
timeout=30,
)
resp.raise_for_status()
partners = resp.json()
# Llamar a un método sobre registros concretos
requests.post(
f"{BASE}/json/2/sale.order/action_confirm",
headers=HEADERS, json={"ids": [42]}, timeout=30,
).raise_for_status()
Las peticiones se ejecutan como el usuario dueño de la clave, con sus ACL y record rules, y solo pueden llamar a métodos públicos: los que empiezan por guion bajo no son accesibles.
Gestión
Un usuario técnico por integración, claves con caducidad
Las claves se crean desde Preferencias → Seguridad de la cuenta. Odoo solo guarda su hash, así que se muestran una única vez. En Odoo 19 tienen fecha de caducidad: un usuario normal está obligado a ponerla, con un máximo que define el campo api_key_duration de sus grupos; solo un administrador puede crear claves sin caducidad.
- Un usuario por integración, con los grupos mínimos que necesita. Si la tienda online solo crea pedidos, no necesita ver la contabilidad.
- Nunca la clave de un administrador en un sistema externo: una fuga daría acceso total.
- Caducidad y rotación: una clave sin fecha de caducidad es una clave que nadie rotará nunca.
- Archivar el usuario invalida todas sus claves de golpe si hay que cortar una integración.
El scope
Un scope vacío vale para cualquier scope
Las claves tienen un campo scope, y es tentador pensar que sirve para limitar una clave a un uso concreto. Hay que leer cómo se comprueba: la consulta acepta la clave si scope IS NULL OR scope = <el pedido>. Las claves creadas desde las preferencias del usuario tienen el scope vacío, así que valen para cualquier scope, incluido el tuyo.
# Comprobación en un controlador propio
uid = request.env["res.users.apikeys"]._check_credentials(
scope="fleet_trip_sync", key=token,
)
# Devuelve el uid tanto para una clave con scope "fleet_trip_sync"
# como para CUALQUIER clave normal del usuario (scope NULL).
Tus endpoints
auth='bearer' en tus controladores
No hace falta reimplementar la lectura de la cabecera. Odoo 19 incluye el tipo de autenticación bearer: si la petición trae una API key válida, el entorno pasa a ser el de su usuario; si no, devuelve un 401. Es el mismo mecanismo que usa /json/2.
from odoo import http
from odoo.http import request
class FleetTripApi(http.Controller):
@http.route("/api/fleet/trips", type="http", auth="bearer",
methods=["GET"], csrf=False)
def list_trips(self, **kwargs):
# request.env ya es el del usuario dueño de la clave
trips = request.env["fleet.trip"].search_read(
[("state", "=", "confirmed")], ["name", "date_start"], limit=100,
)
return request.make_json_response(trips)
| XML-RPC / JSON-RPC | JSON-2 | |
|---|---|---|
| Estado en Odoo 19 | Obsoleto, retirada prevista en Odoo 22 | Recomendado |
| Autenticación | Login + contraseña o API key en cada llamada | Cabecera Authorization: bearer |
| Forma de la llamada | execute_kw(db, uid, key, modelo, método, args, kwargs) | POST /json/2/modelo/método con argumentos por nombre |
| Errores | Fault genérico | Códigos HTTP (401, 404, 422…) |
Resumen
En Odoo 19, las integraciones nuevas deberían usar /json/2 con API keys en la cabecera Authorization, y las existentes, empezar a migrar antes de Odoo 22. Cada integración con su usuario técnico de permisos mínimos y claves con caducidad. El scope no protege nada si la clave tiene el scope vacío: quien limita el alcance de una fuga son los grupos del usuario.