aethexDocs
Odoo logo
CRM

Odoo

Connect your Odoo CRM and your voice agents can recognise the caller, read their open leads, keep the contact current, and file answered calls on the contact, all during the call in your own Odoo. Works with Odoo 16 through 19.

How it works

Connect your Odoo instance once, choose which agents may use it, and give each one a read or a write grant. Aethex stores the credential encrypted and turns your CRM into a small set of actions an agent can call mid-call, choosing for itself when each is needed. The grant is the whole story: read lets an agent look things up, write lets it change your CRM and have its answered calls filed for you. Aethex talks to whichever version you run, so the same actions work the same way whether your instance is Odoo 16, 17, 18, or 19.

AETHEXyour agentREAD GRANTLook up the callerRead contacts and open leadsWRITE GRANTKeep a contact currentLog the callautoreadwriteWrite includes everything read can do. The call log follows the write grant.

Supported versions

Aethex speaks to each Odoo version over its own API. Odoo 19 uses the new External JSON-2 API; Odoo 16, 17, and 18 use the legacy JSON-RPC API. You pick the version, or let Aethex detect it, when you connect, and the connection records the version and transport it settled on so you can confirm it.

Odoo versionTransportWhat you send
19External JSON-2database, API key
18JSON-RPCdatabase, username, API key or password
17JSON-RPCdatabase, username, API key or password
16JSON-RPCdatabase, username, API key or password
Note
Set config.odoo_major_version to auto, 16, 17, 18, or 19. auto reads your server's reported version and picks the transport for you. Versions below 16 and untested versions above 19 are refused when you connect.
STEP 01

Connect your Odoo instance

Send the credential to POST /integrations. Aethex validates it against your instance before storing anything, so a wrong key, database, or version comes back as an error on this call rather than as a silent failure mid-call. Every connection needs your instance URL in base_url (public HTTPS only), the database name in config.database, the credential in access_token, and auth_type set to api_key. Odoo 19 identifies the API user by the key itself, so you do not send a username. Odoo 16, 17, and 18 authenticate with a login, so you also send config.username; the credential can be that user's API key or password. Not sure which version you run? Set odoo_major_version to auto and include the username.

Connect Odoo
bash
# Odoo 19 (JSON-2): no username, the API key identifies the user
curl -X POST https://api.aethexai.com/api/v1/integrations \
  -H "X-API-Key: $AETHEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "odoo",
    "auth_type": "api_key",
    "base_url": "https://your-company.odoo.com",
    "access_token": "<your-odoo-api-key>",
    "display_name": "Sales CRM",
    "config": {
      "database": "your-company",
      "odoo_major_version": 19
    }
  }'

# Odoo 16, 17, 18 (or "auto"): also send the user's login
curl -X POST https://api.aethexai.com/api/v1/integrations \
  -H "X-API-Key: $AETHEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "odoo",
    "auth_type": "api_key",
    "base_url": "https://crm.your-company.com",
    "access_token": "<your-odoo-api-key-or-password>",
    "display_name": "Sales CRM",
    "config": {
      "database": "your-company",
      "username": "[email protected]",
      "odoo_major_version": "auto"
    }
  }'
STEP 02

Grant it to agents

Connecting stores the credential; it does not hand it to anything. A connection with no agent is inert: no call sees its tools and nothing is written to your CRM. Grant it per agent, in the connect call or afterwards with PUT /integrations/{id}/agents. That PUT replaces the whole set, so send every agent that should keep access, not just the one you are adding. Start at read; move an agent to write only when you want it changing records and filing calls.

Grant an agent
bash
curl -X PUT https://api.aethexai.com/api/v1/integrations/$CONNECTION_ID/agents \
  -H "X-API-Key: $AETHEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agents": [
      { "agent_id": "<agent-uuid>", "access": "read" }
    ]
  }'
STEP 03

What an agent can do

What an agent can reach is decided when the call is assembled: an action outside its grant is never offered to the model, so a read-only agent has no way to change anything. These are the tools, and the grant each one needs.

ToolWhat it doesGrant
odoo_lookup_contactFinds the caller by exact phone or email and returns their open CRM leads in one result, so the agent can greet them by name and pick up where the last conversation left off. Matches on both the phone and mobile fields.read
odoo_list_stagesLists your CRM stages, optionally for one sales team, so the agent knows the pipeline it is working in.read
odoo_record_callerUpdates the one matched contact, or creates a new contact when none matches. Returns a disambiguation list rather than guessing when more than one contact matches.write
odoo_move_lead_stageMoves one lead or opportunity to an unambiguous stage, by id or exact label. Off by default; enable it per connection with config.enabled_tools.write
Note
odoo_move_lead_stage is opt-in. To turn it on, send config.enabled_tools with the full list of tools you want the connection to expose (it is an exact allowlist, not an addition to the defaults). Include odoo_log_call in that list if you want automatic call logging to keep working.

Automatic call logging

When a call ends, Aethex looks the caller up and files it on their Odoo chatter as an internal note: the call id, direction, start time, duration, and outcome, plus a short summary. It needs no tool call and no prompt.

Tip
It follows the write grant, so it happens for agents you granted write and not for read-only or ungranted ones. And it only writes on an unambiguous match, so a number that matches two contacts is left for a human rather than filed on the wrong record.

Import contacts as call lists

An Odoo connection can also seed call lists from your contacts, without exposing your whole database. List what you can import with GET /integrations/{id}/imports/entities, see the safe fields with GET /integrations/{id}/imports/fields?entity=res.partner, preview a validated sample with POST /integrations/{id}/imports/preview, and read paged rows with POST /integrations/{id}/imports/page. Sensitive fields such as bank details, tokens, and national ids are never offered, and the flow is read-only until you run the import.

Security

Your Odoo instance must be reachable over public HTTPS; private, loopback, and internal-only hosts are refused, and the hostname is resolved and pinned immediately before each request to close DNS-rebinding gaps. Redirects are not followed, requests time out at 15 seconds, and responses are capped. During a call the agent still answers within 9 seconds whatever Odoo does. The credential is encrypted at rest and never returned by any endpoint, and neither is the database name or username. Read and write grants are enforced per agent, and your Odoo user's own access rules still apply on top, so a grant can only narrow what that user could already do.

Errors

Connect and tool calls surface these statuses.

401Credential rejected

Odoo rejected the API key or password.

Check the credential, the username (Odoo 16 to 18), and the database, then reconnect.

403Access denied

The Odoo user is not allowed the operation or record.

Give the integration user CRM read or write rights in Odoo, then retry.

404Not exposed

The instance does not expose the required model or method.

Confirm CRM is installed and the version is one Aethex supports.

422Unusable request

Unknown provider, a version that does not match the server, an unsupported version, a missing username for Odoo 16 to 18, a username sent for Odoo 19, or an unsupported config field.

Read the message; it names the specific problem.

429Too Many Requests

The per-tenant connect or import rate limit was exceeded.

Back off and retry after the Retry-After window.

503Odoo unreachable

Odoo was unreachable, the encryption key is not configured, or the rate limiter is offline.

Transient; retry shortly.

Notes and limits

A few things worth knowing before you plan around them.

LimitDetail
One connection per workspaceA workspace holds one active Odoo connection, so connecting a second instance replaces the first and takes its agent grants with it.
Phone is not a unique keyAethex matches on the phone and mobile fields using both the E.164 form and the number as dialed, and returns ambiguous matches as candidates for the agent to disambiguate rather than guessing.
Optional fields are detected onceThe version and the optional fields your database exposes are detected at connect and cached on the connection. Reconnect to pick up a field you add later, such as after installing a module.
Version is recordedThe detected version and transport are stored on the connection and returned by GET /integrations/{id}, so you can confirm what Aethex is talking to.
Recording is not an atomic upsertRecording a caller is a search-then-write, so two concurrent callers with the same new number can create duplicate contacts.