
EspoCRM Connector
Sync EspoCRM accounts, contacts, leads and opportunities into Odoo over the REST API, with a sync control tower and field-mapping studio.
Available for Odoo 16.0, Odoo 17.0, Odoo 18.0, Odoo 19.0. Technical name bambooforge_espocrm_connector.
EspoCRM Connector
A read-only, import-first bridge from EspoCRM into Odoo 18: pull your Accounts, Contacts, Leads and Opportunities into native Odoo contacts and CRM records over the EspoCRM REST API — with a resilient job queue, dry-run safety, pre-flight validation and rollback so you stay in control. The connector never writes back to EspoCRM.
This page is the complete manual. If you follow it top to bottom you can install, connect, run your first import, automate it, and fix the common issues without contacting support.
Overview
The connector reads from EspoCRM over its REST API and creates/updates the matching Odoo records. It is one-way only — Odoo ← EspoCRM (pull). Nothing is ever written back to EspoCRM.
It covers four EspoCRM source entities mapped onto two Odoo models:
EspoCRM entity |
Lands in Odoo as |
Notes |
|---|---|---|
Account |
res.partner (company) |
Imported as a company contact (is_company = True). |
Contact |
res.partner (individual) |
Imported as an individual, linked to its account where available. |
Lead |
crm.lead (type Lead) |
The raw EspoCRM status is kept on the record. |
Opportunity |
crm.lead (type Opportunity) |
Amount, close date, probability and the raw EspoCRM stage are kept. |
Because four EspoCRM entities map onto only two Odoo models, the connector is keyed by the EspoCRM entity end to end. Accounts and Contacts never collide on res.partner, and Leads never collide with Opportunities on crm.lead.
Every remote call goes through a queue: nothing is written to Odoo until a job runs, jobs retry with back-off on transient failures, and a circuit breaker pauses an instance that keeps failing.
Requirements
Odoo: 18.0, Community or Enterprise. The connector depends on Contacts (contacts) and CRM (crm), which install automatically.
EspoCRM: any EspoCRM server exposing the standard REST API at /api/v1. The connector uses only generic list and single-record endpoints, so it is version-tolerant. EspoCRM must be reachable from the Odoo server.
Python: no extra libraries beyond a standard Odoo 18 install.
Odoo access: any internal user can open the connector screens. The EspoCRM API key and webhook secret are stored in system-only fields, so only a Settings / Administration user can read or change them.
Network: outbound HTTPS from Odoo to your EspoCRM server. By default the connector refuses internal/loopback/private hosts as an SSRF safeguard (see Safety features).
Installation
Copy bambooforge_espocrm_connector into your Odoo addons path.
Restart the Odoo service.
Open Apps, click Update Apps List, search for EspoCRM, and press Activate / Install. Dependencies (Contacts, CRM) install automatically.
No EspoCRM server is required to evaluate the connector: a mock EspoCRM REST API ships inside the module, so you can install, explore and run the full import flow against sample data before pointing it at a real server. To use it, set the instance Base URL to <your-odoo-url>/espocrm/mock_api with API Path api/v1 and any non-empty API key.
Step 1 — Create an API key in EspoCRM
The connector authenticates with a single API key, sent as the X-Api-Key header on every request. There is no OAuth and no token exchange. Create a read-capable API user in EspoCRM:
Sign in to EspoCRM as an administrator.
Go to Administration ▸ API Users.
Click Create API User.
Set Authentication Method to API Key. EspoCRM generates the key for you.
Give the API user read access to Accounts, Contacts, Leads and Opportunities via its Roles (read-only is enough — the connector never writes to EspoCRM).
Save, then copy the generated API Key. You will paste it into Odoo in Step 2.
Keep the key somewhere safe; EspoCRM shows it on the API user record, but treat it like a password.
Step 2 — Create the connection in Odoo
Open EspoCRM Connector ▸ Configuration ▸ Instances and create a record. The fields that matter:
Field |
What to enter |
|---|---|
Name |
A label for this server, e.g. Production EspoCRM. Must be unique per company. |
Company |
The Odoo company that owns the imported records. |
Base URL |
Your EspoCRM server root, e.g. https://crm.example.com. For the bundled demo, use <your-odoo-url>/espocrm/mock_api. |
Authentication |
API Key — the only supported method. The key is sent as the X-Api-Key header. |
API Key |
The key from Step 1 (visible to administrators only). |
API Path |
The REST path. Leave the default api/v1 unless your server differs. |
Verify SSL |
Keep on for production. Turn off only for self-signed test certificates. |
Allow internal host |
Off by default. Turn on only to reach an EspoCRM on localhost or a private network (this lowers the SSRF guard — see Safety features). |
Timeout (s) |
HTTP timeout per request. Default 30. |
Then click Test Connection. The connector calls a lightweight EspoCRM endpoint (App/user, falling back to a one-row Account list) and, on success, sets the state to Connected and seeds default Lead/Opportunity status mappings. If it fails, the exact error is stored on the form (last connection error) and recorded in Logs (see Troubleshooting).
Tip: use Quick Setup (button on the instance) to pick a use case and apply a recommended set of auto-import toggles and a safety profile in one step.
Step 3 — First import (dry-run, then live)
New instances start with Dry-run ON. In dry-run, import jobs simulate writes: instead of creating or changing Odoo records they produce Validation Results you can review under EspoCRM Connector ▸ Operations ▸ Validation Results. This lets you confirm what would happen before anything is written.
To run a first import:
On the instance, open the Import tab and click Queue Account Import (and/or Queue Contact Import, Queue Lead Import, Queue Opportunity Import). This enqueues jobs; it does not block the UI.
Jobs are processed by the Espocrm Queue Processor scheduled action (every minute), or immediately if you trigger it from Operations ▸ Queue Jobs.
Review Validation Results while still in dry-run.
When satisfied, open the instance, turn Dry-run OFF, and run the imports again to write the records for real.
Imported records land in the standard Odoo apps:
Accounts and Contacts → Contacts (companies and individuals).
Leads and Opportunities → CRM (the pipeline; Leads as type Lead, Opportunities as type Opportunity).
Each imported record is tracked by a binder keyed on the EspoCRM entity and remote id, so re-imports update the same Odoo record instead of duplicating it.
Field mapping & customization
Field Mappings (Configuration ▸ Field Mappings) map EspoCRM source fields to Odoo target fields, per EspoCRM entity, in the EspoCRM → Odoo direction. Click Suggest Mappings on the instance to seed the business-critical defaults, then adjust. Out of the box the suggested mappings include, for example:
Entity
EspoCRM source path
Odoo target field
Account
name / website / emailAddress / phoneNumber
name / website / email / phone
Contact
name / emailAddress / phoneNumber
name / email / phone
Lead
name / emailAddress / accountName
name / email_from / partner_name
Opportunity
name / amount / probability
name / expected_revenue / probability
Per-field transforms: each mapping can coerce values (String, Integer, Float, Boolean, Uppercase/Lowercase, JSON String, CSV Join, First Item) or run a safe Python Expression (variables value and default_value). A Preview shows the result on a sample input before you save.
Schema Fields (Configuration ▸ Schema Fields) lists the discovered EspoCRM fields per entity alongside the Odoo target fields. Click Schema Introspection on the instance to refresh it; the connector samples a live record (or the bundled mock when the API is unreachable) and flattens its shape.
Suggest Mappings vs Schema Introspection: run introspection first if you want the intelligent suggestions (name/type similarity scored) rather than only the core blueprint set.
Automation (scheduled actions)
The module ships these scheduled actions (Settings ▸ Technical ▸ Scheduled Actions):
Scheduled action |
Default |
Purpose |
|---|---|---|
Espocrm Queue Processor |
every 1 min |
Processes queued import jobs. |
Espocrm Reconciliation |
every 15 min |
Pulls recent remote records for entities with auto-reconcile enabled and re-queues any that are missing or previously failed. |
Espocrm Maintenance |
every 1 hr |
Recovers stale/locked jobs and trims old jobs and logs (per the retention settings). |
Espocrm Flow Scheduler |
every 5 min |
Runs scheduled import flows. |
Espocrm Flow Metrics |
every 1 hr |
Aggregates flow-run metrics. |
Espocrm Auto Recover |
every 15 min |
Reopens a tripped circuit breaker once the cooldown passes and no blocking validation issues remain. |
Turn on Auto-import / Auto-reconcile per entity on the instance (Accounts, Contacts, Leads, Opportunities) to let the scheduled actions keep things in sync hands-free.
Webhooks (not used in this version). EspoCRM can emit outbound webhooks, but wiring them into the import queue is a roadmap item. A route at /espocrm/webhook exists only so that existing subscriptions or health checks do not return 404 — it acknowledges with HTTP 200 and imports nothing. Do not rely on it to bring in data; use the scheduled import and reconciliation actions instead.
Safety features
Dry-run mode (dry_run_mode, ON by default) — import jobs simulate writes and produce Validation Results to review before you go live.
Pre-flight validation (validation_enabled) with Minimal / Standard / Strict business-validation profiles — gates risky writes; Standard is the default. Use Run Validation Sweep to validate pending/failed jobs on demand, and Auto-fix Open Issues to clear the ones the connector can resolve.
Resilient queue — every import is a job with retry and configurable back-off (fixed or exponential), plus a dead-letter state for jobs that exhaust their retries.
Circuit breaker (circuit_state: Open / Protected / Tripped) — after repeated failures (default threshold 5), or immediately on an auth/configuration error, the instance auto-pauses (Tripped). The Espocrm Auto Recover action reopens it after a 30-minute cooldown, or click Resume Sync.
Rollback snapshots (rollback_enabled) — imports capture a snapshot so you can undo a batch from Operations ▸ Rollback Snapshots.
SSRF guard — the connector refuses internal/loopback/private/reserved hosts (including cloud metadata addresses) for the Base URL unless Allow internal host is explicitly enabled.
Safety profiles — Conservative / Balanced / Aggressive presets set dry-run, validation, batch size and delete strategy together; click Apply Safety Profile.
Credential isolation — the API key and webhook secret live in system-only fields, readable and editable only by administrators.
Troubleshooting
Symptom |
Cause and fix |
|---|---|
Test Connection fails with 401/403 |
Wrong or missing API key, or the API user lacks read access. Re-check Step 1 and the API Key field; confirm the API user's role grants read on Accounts/Contacts/ Leads/Opportunities. |
"Base URL is not allowed … non-public address" |
The Base URL points at localhost or a private/reserved IP. For a self-hosted server, enable Allow internal host on the instance (test/self-hosted only). |
SSL errors on Test Connection |
Self-signed or invalid certificate. Use a valid cert, or turn off Verify SSL for testing only. |
"EspoCRM resource not found" / 404 |
Wrong API Path or Base URL. The default path is api/v1; the connector calls <base>/api/v1/<Entity>. Confirm the server root has no trailing path. |
"EspoCRM rate limit reached" (429) |
The server is throttling. The client retries with back-off automatically; lower the per-entity import limit or raise API retry settings if it persists. |
Jobs stay in Pending |
The Queue Processor is off or the instance is paused. Confirm the Espocrm Queue Processor scheduled action is active and the instance is not Paused. |
Instance shows Tripped |
The circuit breaker tripped after repeated failures (or an auth error). Fix the server/credentials; Auto Recover reopens it after cooldown, or click Resume Sync. |
Nothing is written after Import |
You are in Dry-run. Review Validation Results, then turn dry-run off and re-run the import. |
Records imported twice |
Imports are keyed by EspoCRM entity + remote id via the binder, so this should not happen. If it does, check that two instances do not point at the same EspoCRM server. |
Jobs end up Dead |
They exhausted their retries. Open Operations ▸ Queue Jobs (Dead Letter), read the error on the job and in Logs, fix the cause, then re-queue. |
For anything else, Operations ▸ Logs records every API call, payload and error with a timestamp.
Frequently asked questions
Does this connector write anything back to EspoCRM? No. It is read-only and import-first: it lists and fetches records from EspoCRM and creates or updates the matching Odoo records. Write-back to EspoCRM is a roadmap item.
Which versions are supported? Odoo 18.0 on the Odoo side. On the EspoCRM side the connector uses only the generic /api/v1 list and single-record endpoints, so it is version-tolerant. Validate your exact build with the bundled mock first.
Do I need an EspoCRM server to evaluate it? No. A mock EspoCRM REST API ships inside the module. Point the Base URL at <your-odoo-url>/espocrm/mock_api (API Path api/v1, any API key) to run the full import flow against sample data.
How does authentication work? A single API key sent as the X-Api-Key header. Create an API user with API-Key authentication under Administration ▸ API Users in EspoCRM. No OAuth, no tokens.
Where do Leads and Opportunities end up? Both land in the CRM pipeline as crm.lead records — Leads as type Lead, Opportunities as type Opportunity (with amount, close date, probability and the raw EspoCRM stage).
Is it safe to run against production data? Yes. Dry-run is ON by default, validation gates risky writes, and rollback snapshots let you undo a batch. You decide when to go live.
Does it support real-time webhooks? Not in this version. Real-time inbound webhooks are a roadmap item; the /espocrm/webhook route exists only to avoid 404s and imports nothing. Use the scheduled import and reconciliation actions instead.
What support and refund policy do I get? Every request is answered within 24 hours, setup help included. If you report a bug within 2 months of purchase and it is not resolved within 15 days, you are entitled to a full money-back refund.
Data, privacy & limits
The connector reads Accounts, Contacts, Leads and Opportunities from your EspoCRM server and writes the corresponding Odoo res.partner and crm.lead records. It never writes, updates or deletes anything in EspoCRM.
Credentials (API key, webhook secret) are stored in administrator-only fields.
In scope today: read-only import of the four entities, per-field mapping with transforms, schema introspection, dry-run + validation + rollback, a resilient queue, and scheduled import/reconciliation.
Out of scope / roadmap: write-back to EspoCRM, inbound webhooks (the route is a no-op), and incremental modifiedAt filtering (v1 lists by maxSize/offset).
Support & updates
Support: support@bambooforge.dev — answered within 24 hours, setup help included.
Refund: report a bug within 2 months of purchase; if unresolved within 15 days, full refund.
Full source is included. Updates track the supported Odoo 18 / EspoCRM REST api/v1 line.
Upgrading & version compatibility
This build targets Odoo 18.0. Each Odoo major series (17.0, 18.0, 19.0) has its own dedicated build of this module — always install the build that matches your Odoo version. Mixing a build with a different Odoo series is not supported.
Patch upgrades (same series, e.g. 18.0.1.0.0 → later)
Back up your database and filestore first.
Replace the module folder with the newer build.
Restart Odoo with the module updated:
./odoo-bin -c your.conf -u bambooforge_espocrm_connector -d your_db
Odoo applies any schema/data changes automatically. Your existing records and configuration are preserved.
Cross-version migration (e.g. Odoo 17 → 18)
Upgrading Odoo itself is a database migration handled by Odoo's standard upgrade tooling. When you migrate the database to the next Odoo series, install the matching build of this module for that series. Data created by this module carries over with the database migration.
After any upgrade the module's scheduled actions resume on their normal cadence — no manual re-activation is required.
Uninstallation
You can remove this module at any time from Apps → (this module) → Uninstall, or from the command line. Uninstalling is clean and reversible by reinstalling — but note what is and is not deleted.
What is removed
The module's own tables and every record in them (20 models, prefixed espocrm.*) — this is the data this module created.
The menus, actions, views and reports this module installed.
Its scheduled actions (cron jobs) — they stop immediately on uninstall.
Connection records, credentials, field mappings, queue jobs and sync logs stored in Odoo.
What is preserved
Your remote platform is never touched. Uninstalling only removes the Odoo-side connector; products, customers and orders on the external store/service are untouched.
Records already imported into standard Odoo models (e.g. contacts, products, sales orders) remain — they are ordinary Odoo records once created.
Attachments and chatter messages on standard records are kept.
As always, take a database backup before uninstalling in production.
Changelog
18.0.1.0.0
Current release for Odoo 18.0. This build includes:
Sync EspoCRM accounts, contacts, leads & opportunities into Odoo 18 over the REST API.
Now with a live Sync Control Tower, bulk dead-letter & conflict consoles and a field-mapping studio. Read-only against EspoCRM, resilient queue, dry-run safety, auditable source.
Feature additions and fixes ship as new builds on the Odoo Apps store; this page and the module's version reflect the current published release. Always keep the build matched to your Odoo series (see Upgrading & version compatibility).
Screens

