技術ログ

Read-only Odoo API key for ChatGPT, Claude or an MCP server (Odoo 20)

公開: 2026-10-09 · 著者: GRAMSHIFT

What a key can do by default, what access rights cannot express, and the write values that silently delete lines

Read-only Odoo API key for ChatGPT, Claude or an MCP server (Odoo 20)

Odoo 20 has no read-only API key. A key can do everything its user can do, through any public method. Here is what that means when you hand a key to ChatGPT, Claude, an MCP server or an n8n flow, based on tests on a local Odoo 20 Community database on 2026-10-09.

  • A key has no permissions of its own. In Odoo 20 Community the only scope offered for a new key is RPC. The scope decides where the key may sign in, not what it may do.
  • The JSON-2 API calls any public method. /json/2/<model>/<method> runs unlink, action_confirm or action_cancel as readily as search_read, within the rights of the key's user.
  • The free way to limit a key is a dedicated user with minimal access rights, as Odoo's documentation recommends. Access rights work per model and per operation: they cannot say "may confirm but not cancel" or "at most 10 records per call".
  • Watch the values an agent sends. On a linked list such as order lines, false, null, a plain list of ids and the command number 2.0 all removed lines on our test database.
  • A truly read-only call is one whose database transaction is read-only: PostgreSQL then refuses every write, including writes Odoo makes with elevated rights.

What a key can do by default

A user creates a key in My Preferences > Security. The key signs in as that user, with all of that user's access rights. In Odoo 20 Community, the scope field of a new key offers a single choice, RPC. Odoo's documentation describes an MCP scope for Odoo's own MCP server, which is not part of Community; we did not test it.

The JSON-2 endpoint looks the method up by name and calls it. On 2026-10-09 one admin key read sales orders with search_read, then confirmed one with:

POST /json/2/sale.order/action_confirm
Authorization: bearer <key>
{"ids": [3]}

The answer was true and the order moved to Sales Order. Nothing in the key said it was meant for reading.

Two smaller facts about keys in Odoo 20: a user who is not an administrator must give a key an expiry date ("The API key must have an expiration date"), and every call to /xmlrpc, /xmlrpc/2 or /jsonrpc logs that these endpoints are "deprecated in Odoo 19 and scheduled for removal in Odoo 22". They still accept keys.

The free way: a dedicated user

Odoo's documentation on the JSON-2 API recommends dedicated bot users for integrations, with the minimum permissions they need. For an AI assistant that only answers questions:

  1. Create a user for the assistant and give it only read access to the models it needs (a group of your own with read rights works).
  2. Create the API key while signed in as that user.
  3. Give the assistant that key, never a password.

Where this stops:

  • Each integration needs its own user; check what an extra user costs on your plan.
  • Groups add up. If one group of the user grants delete on a model, no other group takes it away.
  • A user who may write to orders may also call every button on them. Access rights have no list of allowed methods.
  • Access rights have no limit on how many records one call may change.

Write values that remove order lines

AI agents write JSON. Odoo reads some values of a linked list (one2many or many2many) in ways that are easy to miss. We wrote each value below to the order_line field of a sales order on our test database (2026-10-09), with a user allowed to edit order lines. The lines of a sales order are deleted when they lose their order.

Value of order_lineHow Odoo reads itResult
falseRemove all linesAll lines deleted
nullRemove all linesAll lines deleted
[8] (a plain list of ids)Replace the lines with line 8The other lines deleted
[[2.0, 10]]Delete line 10 (2.0 is compared as the number 2)Line 10 deleted
[[5.0]]Remove all linesAll lines deleted
[["2", 12]]Not a commandNothing deleted

We saw the same through XML-RPC (false and 2.0) and JSON-RPC (false). If you build a tool for an agent, refuse false, null and plain id lists on linked fields unless the agent is meant to replace them, and check command numbers with ==, as Odoo does, not by type.

Making a key read-only in the database

Checking method names ("allow search_read, refuse write") is not enough on its own: a method that looks like a read can write, and Odoo code often writes with elevated rights. What holds is a read-only transaction. After SET TRANSACTION READ ONLY, PostgreSQL refuses every INSERT, UPDATE and DELETE until the transaction ends.

On 2026-10-09 we ran calls of one key inside a read-only transaction. Ten kinds of reads worked (search_read, read, fields_get, name_search, search_count, formatted_read_group, web_search_read, web_read, context_get, and reading messages). Five kinds of writes failed, including action_confirm, which writes stock records with elevated rights:

cannot execute INSERT in a read-only transaction

The read-only mode has to be set again if Odoo retries the call after a serialization error, because a retry starts a new transaction.

Per-key rules as a module

We packaged these checks as an Odoo 20 module, AI API Key Permissions (paid, by GRAMSHIFT, which publishes this site). It adds a rule to each API key: read-only (the transaction above), no deletes (refused at Odoo's delete entry point during the key's calls), only the methods you list, and a limit on records per call. Its page lists exactly what each rule covers. A dedicated user for each tool, as above, works well together with it.

How this was tested: on a local Odoo 20 Community database (Odoo source as of 2026-10-06) with test sales orders, over JSON-2, XML-RPC and JSON-RPC, on 2026-10-09. The order-line results were read back from the database after each call. Statements about Odoo's MCP server and about dedicated bot users come from Odoo's documentation; the MCP server was not tested. This page is not affiliated with Odoo S.A. On the use of AI: the test scripts, the module and this article were written by Claude, an AI model, working for GRAMSHIFT; the results quoted here were compared against the saved outputs of those scripts.

よくある質問