# Why Odoo ACLs are necessary but not enough for AI writes

Published: 2026-08-18
Last updated: 2026-08-27
Canonical: https://erpipe.com/guides/human-approval-odoo-ai-writes
MCP endpoint: https://mcp.erpipe.com/mcp

## Answer first

Odoo ACLs answer whether the connected user may write. They do not answer whether an AI agent should make this change now, against these records, with this payload. ERPipe keeps writes off by default and can layer preview, field policy, validation, and a consume-once human decision in front of Odoo. Odoo's own access checks still run.

This is the hosted product at [https://erpipe.com/](https://erpipe.com/), not a change to the MCP URL. Clients still call `https://mcp.erpipe.com/mcp`.

## Environment and method

This field note describes ERPipe's hosted write path as implemented and tested through its preview, validation, approval, execution, audit, and ambiguous-outcome checks. The Odoo connection still uses a real Odoo user; the gateway does not use approval to bypass groups, ACLs, record rules, or field access.

The method was deliberately narrow: start with writes disabled, prepare the exact model, record ids, fields, and values, run policy checks, require one human decision, execute once, then inspect the recorded outcome. It is not a benchmark and it is not evidence that every Odoo business workflow is safe to automate.

## Why a person is still in the loop

An agent can draft a correct invoice date and a disastrous partner change with the same confidence. Human approval is the visible lock: the owner sees the model, method, ids, and payload, then approves once or rejects. The grant does not silently widen.

Chatter notes and allowlisted methods can use separate gates so a comment is not treated like a stock move.

## A finance-shaped path

- The agent searches overdue `account.move` rows the Odoo user can already see.
- It opens the PDF and compares a purchase order.
- It previews a write on a follow-up date.
- The owner approves that write once from the dashboard, from Approve/Reject in the email, or in Telegram.
- Audit stores redacted operational detail. The invoice stays in Odoo.

That is the loop. It is not “the model posts whatever it wants after you clicked Connect.”

## Result

The useful boundary is two independent decisions. Odoo answers **may this user perform the operation?** The gateway answers **should this client and proposed payload be allowed to attempt it now?** A human approval can narrow the second decision, but it cannot widen the first.

Keeping those decisions separate made revocation and failure handling clearer. A connection can be paused without changing Odoo groups. A rejected payload stays rejected. An uncertain result is recorded as unknown instead of being treated as a reason to post again.

## What the owner actually sees

| Step | What happens | What does not happen |
| --- | --- | --- |
| Preview | Payload and target ids are shown | Odoo is not changed |
| Validate | Field policy and allowlists run | A timeout is not retried blindly |
| Approve | One owner decision | Teammates do not inherit a wider grant |
| Execute | The approved write runs once | Ambiguous results are not auto-replayed |

If the Odoo call returns an unclear outcome, ERPipe marks it unknown. That is safer than posting twice because a gateway timed out.

## Example: change a follow-up date

1. Connection writes are still off. The agent can only search and read.
2. You enable writes and keep owner approval on for create/write/unlink.
3. The agent calls preview, then validate, then waits.
4. You tap Approve in email or open `/app/approvals`.
5. The write runs as the same Odoo user. Record rules still apply.

If you reject, nothing is posted. If you pause the connection, later tool calls get a paused error instead of a silent success.

## Failure modes

- A broad Odoo service user makes every later policy mistake more expensive. Use least privilege first.
- A chat message saying “yes” is not an owner decision bound to one previewed payload.
- Approval without field policy can still present a person with an unnecessarily broad mutation.
- A timeout after a mutation may hide a committed write. Blind retry can duplicate a business action.
- A permanent approval token quietly becomes another credential. The decision should be bounded and consumed once.

## How this differs from “the model asked me in chat”

Chat confirmation inside ChatGPT is not an owner inbox. Anyone with the chat can type “yes”. ERPipe’s approval is a workspace-owner decision on a specific previewed payload. The grant does not widen because the model sounded confident.

Original hosted fact (2026-08-18): when `execute_approved_write` returns `HITL_APPROVAL_REQUIRED`, that is a **short wait**, not a failure and not a reason to end the chat. Keep the same approval object, tell the owner to Approve, sleep 5–10 seconds, then poll `get_write_approval_status` (or retry execute with the same approval). After about 30–45 seconds if it is still pending, leave it in Approvals. The poll tool is hosted-only; it is named on [the catalog](/tools).

Ambiguous Odoo results are marked unknown. The gateway does not blindly retry a posting because a timeout happened.

## Limits

Human approval reduces one class of risk; it does not prove that the proposed accounting, inventory, or customer action is correct. Owners still need an appropriate Odoo user, backups, business controls, and a workflow-specific review. High-volume or time-critical processes may need a different control than approving each row.

This note covers ERPipe's current hosted behavior, not every MCP server. Inspect the implementation and test revocation, rejection, and unknown outcomes before enabling writes in another gateway.

## Sources and related evidence

- Odoo access rights, record rules, field access, and security pitfalls: https://www.odoo.com/documentation/19.0/developer/reference/backend/security.html
- [ERPipe security boundary](/security)
- [Hosted tool catalog](/tools)
- [Odoo MCP architecture field note](/guides/odoo-mcp-server-architecture)
- [Odoo AI Agents vs an external gateway](/guides/odoo-ai-agents-vs-mcp-gateway)

## FAQ

- **Do I have to use approval?** For create, write, and unlink you can require it. Leaving owner approval on is the conservative production setting.
- **Does approval bypass Odoo ACLs?** No. A denied Odoo user still cannot write the row. ERPipe adds a second gate, not a back door.
- **What if the result is uncertain?** The operation is marked unknown and is not retried automatically, so a timeout cannot duplicate a posting.
- **Can teammates approve?** No. Current approval actions are restricted to workspace owners; teammates do not inherit approval authority.
- **Does a webhook auto-approve?** No. Signed events enter a pull/ack queue. Nothing executes until a client lists and acknowledges them.
- **How do I turn writes off again?** Disable writes on the connection or pause it. Existing tokens do not invent a new write path.
