Execution Layer for Odoo
Messaging: Discuss to WhatsApp.
Governed AI messaging in the Execution Layer, new in version 1.1: Discuss, record chatter, and WhatsApp through Odoo's own integration, with confirm-by-default previews, per-user levels, approvals, rate limits, and a message log of every send.
New in version 1.1, the Execution Layer lets an AI client send messages: Discuss channels, group chats and direct messages, notes and comments on any record’s chatter, and WhatsApp. Every send runs the same governed pipeline as every other write: identity, policy, the user’s messaging level, approval where configured, idempotency, atomic execution, read-back, evidence.
The default posture is the whole story: nothing sends until a person reads the exact words and says yes.
What a sent message looks like
The AI acts as the connected Odoo user: the message’s author is that person, direct messages and
@mentions resolve to internal users only, and it can post only into conversations that user is
a member of. An ambiguous name is refused with candidates (AMBIGUOUS_TARGET), a non-member
target with NOT_A_MEMBER, and @everyone is off by default and works only in channels.
The tools
Messaging adds five tools to the catalogue (part of the 20 total):
| Tool | Kind | What it does |
|---|---|---|
list_conversations | read | The user’s channels, group chats and DMs, plus open WhatsApp conversations with their 24-hour-window state. Metadata only, never message text. |
send_message | write | Send into a channel, group, or DM, or post a note or comment on a record’s chatter. |
list_whatsapp_templates | read | The WhatsApp templates and their placeholders; only Meta-approved, active templates are sendable. |
send_whatsapp | write | Send a template (or, if enabled, a free-form reply) through Odoo’s WhatsApp queue. |
get_whatsapp_status | read | Delivery state (queued, sent, delivered, read, or error) of the WhatsApp messages this connection sent. |
A chatter comment notifies the record’s followers, which Odoo may deliver by email; a note
stays internal. There is no email-send tool and no SMS: those are not messaging features.
Confirm each message: the default gate
Every user starts at the Confirm each message level. The first send_message call comes back
unsent, as CONFIRMATION_REQUIRED, carrying the exact rendered preview (text, destination,
mentions, recipient count) and a one-time cnf_ token. The token is a hash of the exact
arguments: single-use, expiring, bound to those exact words. The AI shows the person the
preview, the person says yes, and the AI retries the identical call with the token. If even one
word changed, the retry fails with CONFIRMATION_STALE and nothing sends. The message a person
confirmed is provably the message that went out.
Per-user levels, per channel
Four levels, set separately for Discuss and WhatsApp, admin-only to change:
- Off: the send tool does not exist for this user’s AI.
- Confirm each message (default): the preview-token gate above, on every send.
- Approval in Odoo: sends route through the approval queue
under the new
outbound_messagingandoutbound_whatsapprisk classes, and the approver reads the actual message body in the payload preview before releasing it. - Unrestricted: for the rare user you trust that far. Bulk WhatsApp sends above the threshold still require approval, even here.
Global switches (Configuration → Settings → Messaging) sit above all of this: Discuss on or off,
WhatsApp on or off (off by default), free-form WhatsApp on or off (off by default),
@everyone, the bulk-send threshold, and hourly rate caps that can also be overridden per user.
WhatsApp, the careful way
WhatsApp runs through Odoo’s own Enterprise WhatsApp integration and your own Meta business account. The Execution Layer never talks to Meta itself; it drives the same channel a person uses in Odoo. It requires Odoo Enterprise with the WhatsApp app installed and a configured account, and the status card in Settings tells you honestly which of those is missing.
- Template-first: only Meta-approved, active templates send (
list_whatsapp_templatesshows them); anything else isTEMPLATE_NOT_APPROVED. - Free-form replies work only inside an open 24-hour customer window, and only when their
separate switch is on; outside the window the AI gets
WHATSAPP_WINDOW_CLOSEDand is pointed back to templates. - Bulk (above the threshold, default 5 recipients) always needs approval.
- Sends are queued in Odoo’s WhatsApp queue: the receipt reports the message persisted and
queued, and never claims Meta delivered it;
get_whatsapp_statusreports the real delivery state later, for this connection’s own sends only.
The evidence trail
Every send lands three ways: a row in the message log (destination, kind, recipients reached,
a preview, the control it passed, the linked audit entry), the full before-and-after evidence on
the action log like any other write, and, if you want it, an “AI sent a message”
alert on every send. The receipt re-reads the
stored message after commit (body, author, conversation, mentions) and fails closed with
VERIFICATION_FAILED on any mismatch.
Honest limits
The messaging tools send; they do not read message contents (list_conversations is metadata
only, and get_whatsapp_status sees only this connection’s own sends). WhatsApp bodies carry no
AI mark to the customer: attribution lives in the Odoo-side conversation, the message log, and
the audit. And there is no side door: the generic write tools refuse mail.message, the Discuss
models, and every whatsapp.* model outright, so messages leave only through the governed
messaging tools.
Messaging behaviour is covered by its own adversarial cases in the test suite, including the payload-bound confirmation, the membership and mention refusals, the WhatsApp window, and the no-side-door rule.
