CRM migration

Migrate from EspoCRM to Odoo CRM

Field-level mapping, validation, and rollback between EspoCRM and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.

EspoCRM logo

EspoCRM

Source

Odoo CRM

Destination

Odoo CRM logo

Compatibility

67%

8 of 12

objects map 1:1 between EspoCRM and Odoo CRM.

Complexity

BStandard

Timeline

4-6 weeks

Rollback included Accuracy guarantee Field-level validation

Overview

What this migration involves

Moving from EspoCRM to Odoo CRM is both a platform switch and a data model redesign. EspoCRM uses separate Account and Contact entities with a link-multiple relationship, while Odoo CRM uses a single res.partner model where Contact is a subtype of Partner (viewed via Contacts or Addresses on an Account page). We split EspoCRM Accounts into Odoo res.partner records with address records, then link EspoCRM Contacts to those Partners using the email-address dedupe key. Custom entity types created via EspoCRM's Entity Manager have no direct Odoo equivalent — we export the entityDefs metadata, document the Odoo custom module structure needed to reproduce each entity, and migrate the records into staging tables for manual Odoo module deployment. Activity history (Calls, Meetings, Tasks, Emails) migrates via Odoo's XMLRPC API with the partner_id and calendar.event linkage resolved from the res.partner mapping built in the first phase. Odoo automations and Workflow actions do not migrate; we deliver a written inventory of EspoCRM workflows for Odoo Studio or developer rebuild.

Field-level fidelity

Every standard and custom field arrives verified.

Schema-aware mapping

AI proposes the map; you confirm before any record moves.

Relationships preserved

Parent–child, lookups, and ownership stay linked.

Full activity history

Calls, emails, meetings — with original timestamps.

Attachments & notes

Documents, uploads, and inline notes move with the record.

Why teams make this switch

Two sides of the same decision

Leaving

EspoCRM logo

EspoCRM

What's pushing teams away

  • Email and SMS follow-up functionality requires third-party integrations and does not work out of the box, frustrating teams expecting a complete CRM experience.
  • Integration ecosystem is narrow compared to HubSpot or Salesforce, with official integrations limited to Google Workspace, Outlook, Zoom, VoIP, and Stripe.
  • Customization depth requires increasing technical knowledge; complex entity relationships and custom PHP/Before-Save scripts become difficult to maintain across upgrades.
  • Performance degrades on large datasets without careful server configuration; teams with hundreds of thousands of records report slow list views and search.

Choosing

Odoo CRM logo

Odoo CRM

What's pulling them in

  • Teams choose Odoo CRM for its modular architecture — one base install with one-click app additions means they can adopt CRM alone and add accounting, inventory, or sales later as the business grows.
  • Small businesses pick Odoo because the Community edition is free and open-source, with no per-user or contact limits, allowing full evaluation before committing to a paid Enterprise tier.
  • The drag-and-drop Kanban pipeline and AI lead scoring are highlighted across G2 reviews as concrete features that make lead management faster and more visual than spreadsheet-based workflows.
  • Odoo's native integration with email, live chat, SMS, VoIP, and WhatsApp means inbound leads from multiple channels feed into a single pipeline without third-party middleware.
  • Companies in retail, supply chain, and construction value that Odoo's CRM module shares the same PostgreSQL database and UI as its ERP modules, eliminating data silos between sales and operations.

Object mapping

How EspoCRM objects map to Odoo CRM

Each row shows how a EspoCRM object lands in Odoo CRM, including any object-level transformations, lookup resolution, or schema-design dependencies.

Typical mapping — final map is confirmed during the sample migration step.

EspoCRM

Account

maps to

Odoo CRM

res.partner

1:1
Fully supported

EspoCRM Account records map directly to Odoo res.partner with partner_type = 'company'. The Account name becomes the partner display name, website URL migrates as website field, and industry classification maps to Odoo's industry_id field. We use the company domain as the dedupe key during import to prevent duplicate partner records when multiple EspoCRM Contacts reference the same Account. Address data (street, city, state, country, zip) migrates as partner address records (res.partner.address or the built-in address tab on the partner form).

EspoCRM

Contact

maps to

Odoo CRM

res.partner (contact subtype)

1:1
Fully supported

EspoCRM Contact records map to Odoo res.partner with partner_type = 'contact' and parent_id pointing to the mapped Account partner. We deduplicate by email address during import so that multiple EspoCRM Contacts with the same email produce one Odoo partner record with multiple address records. Title (Mr/Ms/Dr), phone, email, and function fields migrate to the partner contact record. Any Contact linked to a deleted or null Account in EspoCRM creates an orphan-free res.partner with parent_id = null and is flagged for manual Account assignment review.

EspoCRM

Lead

maps to

Odoo CRM

crm.lead

1:1
Fully supported

EspoCRM Leads map to Odoo crm.lead records with type = 'lead'. The EspoCRM lead status and conversion date fields map to Odoo's stage_id and date_closed. If the EspoCRM Lead was converted to a Contact before migration, we follow the conversion link and map the resulting Contact to res.partner (as above) rather than a crm.lead. Unconverted Leads retain full lead-specific fields including source, rating, and campaign attribution.

EspoCRM

Opportunity

maps to

Odoo CRM

crm.lead (type = 'opportunity')

1:1
Fully supported

EspoCRM Opportunity records map to Odoo crm.lead with type = 'opportunity'. The opportunity amount field migrates to Odoo's planned_revenue, probability maps to probability (rescaled if EspoCRM used a different probability scale), and expected_close_date maps to date_deadline. Stage mapping requires a configuration step: we extract EspoCRM's stage names and probabilities, create matching Odoo stage records in the CRM pipeline via XMLRPC, and assign stage_id on each Opportunity during import. partner_id on the Odoo opportunity is resolved from the Account mapping.

EspoCRM

Case

maps to

Odoo CRM

helpdesk.ticket

1:many
Fully supported

EspoCRM Cases map to Odoo Helpdesk tickets (helpdesk.ticket) if the destination Odoo instance includes the Helpdesk app. If Helpdesk is not in the current Odoo subscription scope, Cases map to crm.lead records with a custom case flag for admin review. Case priority maps to Odoo ticket priority (1-5 scale), case status maps to ticket stage, and the conversation thread migrates as mail.message records linked to the ticket. The parent Account partner_id resolves from the Account mapping.

EspoCRM

Campaign

maps to

Odoo CRM

crm.tracking.m Campaign + utm.mixin fields

lossy
Fully supported

EspoCRM Campaign records migrate as Odoo utm.campaign records, which provide the source attribution infrastructure for Leads and Opportunities in Odoo CRM. Campaign targeting lists (linked Leads and Contacts) do not migrate as Odoo mailing lists; instead, the campaign name and medium/source attribution links are recorded on each migrated Lead so that pipeline reporting can filter by campaign. Full mailing list migration requires Odoo Marketing Campaigns app separately.

EspoCRM

Activity: Meeting

maps to

Odoo CRM

calendar.event

1:1
Fully supported

EspoCRM Meeting records map to Odoo calendar.event. Start datetime, stop datetime, location, and description migrate directly. The partner_ids on the Odoo calendar.event are resolved by matching EspoCRM's linked Contact and Account IDs to the mapped res.partner records via our ID remapping table. If a linked Contact has no matching partner (unresolved Account), we create a minimal partner record or skip the partner linkage with a flag for manual review. Meeting attendees also include mapped EspoCRM Users resolved to Odoo res.users.

EspoCRM

Activity: Call

maps to

Odoo CRM

calendar.event + crm.phonecall

lossy
Fully supported

EspoCRM Call records have two migration paths depending on the Odoo apps installed. Calls where duration and outcome are primary map to Odoo crm.phonecall (a standard CRM module in Odoo). Calls that represent scheduled meetings map to calendar.event with event_type set to 'call'. We assess the EspoCRM call record's purpose field during discovery and assign each to the appropriate Odoo object. The partner and user resolution follows the same remapping table as meetings.

EspoCRM

Activity: Task

maps to

Odoo CRM

project.task

1:1
Fully supported

EspoCRM Task records map to Odoo project.task. Task status maps to Odoo stage_id (within a default project pipeline), priority maps to priority, and assigned user resolves via the User mapping table. Tasks linked to specific CRM records (Opportunities, Cases) attach via the Odoo project.task record's related_opportunity_id or ticket_id cross-reference. If Odoo Project is not in the subscription scope, Tasks map to crm.lead tasks or are flagged for project app activation.

EspoCRM

Activity: Email

maps to

Odoo CRM

mail.message

1:1
Fully supported

EspoCRM email engagement records (tracking sent emails) migrate as Odoo mail.message records linked to the relevant crm.lead, res.partner, or helpdesk.ticket via model, res_id, and message_type fields. The email body and subject transfer as message_body and subject fields. Full email conversation threads (reply chains) do not migrate because EspoCRM stores the sent email record but not the recipient's reply chain within the CRM; we document this boundary in the scope document.

EspoCRM

Document / Attachment

maps to

Odoo CRM

ir.attachment

1:1
Fully supported

EspoCRM Document records and file attachments require separate handling for self-hosted instances where files live on the filesystem under data/files/. We extract the referenced files during discovery, transfer them to Odoo's filestore path (typically under filestore/ based on the database UUID), and re-register each as ir.attachment records linked to the parent model (crm.lead, res.partner) via res_model and res_id. Cloud-hosted EspoCRM instances expose attachments via the API, which we stream directly to Odoo via XMLRPC binary upload.

EspoCRM

Custom Entity (Entity Manager)

maps to

Odoo CRM

Custom Module + ir.model / ir.model.fields

lossy
Fully supported

EspoCRM custom entity types created via Entity Manager have no direct Odoo equivalent that can be created without developer involvement. We export the entityDefs metadata (field names, types, relationships, ACL rules) and document the Odoo custom module structure needed to reproduce each entity. The migration includes a staging-phase upsert of custom entity records into a temporary Odoo table for manual module deployment by the customer's Odoo developer. We do not deploy Odoo custom modules within the migration scope; we deliver the metadata specification and the staged record data.

Gotchas + challenges

What specifically takes care here

Platform-specific issues from each side, plus the pair-specific challenges that don't show up on either platform's page on its own.

EspoCRM logo

EspoCRM gotchas

Medium

Default 200-record API GET ceiling requires pagination

High

Server migration leaves WebSocket references pointing to old domain

Medium

Multi-enum field option cap of 20 limits data fidelity

High

Custom entity import ordering creates chicken-and-egg reference problems

Medium

Attachments on self-hosted instances are filesystem-stored

Odoo CRM logo

Odoo CRM gotchas

High

Odoo.sh version gating blocks assisted migrations from trial

High

Enterprise modules fail to install on Community after database restore

Medium

Custom module view inheritance breaks between Odoo major versions

Medium

Custom fields risk losing their application context on Community

Low

API access for Community is gated behind the Custom Plan

Pair-specific challenges

  • EspoCRM Entity Manager types have no direct Odoo counterpart

    EspoCRM's Entity Manager lets administrators create custom entity types with custom fields and relationships entirely through the UI. Odoo has no equivalent: custom entities require an Odoo custom module with Python model definitions and XML data files, which is developer work rather than admin configuration. We export the entityDefs metadata for each custom EspoCRM entity, document the Odoo model structure needed to reproduce it, and stage the record data in a temporary table for post-migration Odoo module deployment. The customer's Odoo developer or an Odoo partner implements the custom module; we do not write Odoo Python code inside the migration scope.

  • res.partner deduplication requires email-based upsert logic

    Odoo's res.partner model conflates Accounts and Contacts into a single table. EspoCRM's separate Account and Contact entities produce multiple Odoo partner records if imported naively — one for the Account and one for each Contact — which can fragment the partner tree. We deduplicate on email address during the Contact import phase: if an EspoCRM Contact shares an email with a previously imported Account partner (same email on the Account), we treat it as a contact address record on the existing partner rather than a new partner. This requires a pre-import dedupe pass and a two-phase partner insert (Accounts first, then Contacts with parent_id resolution). Skipping this step produces duplicate partner trees that break Opportunity and Ticket linkages in Odoo.

  • Odoo access rights and record rules can silently block API inserts

    Odoo's access control model (ir.model.access) and record rules (ir.rule) restrict which records the API user can create by default. A migration running under a standard Odoo API user may pass field validation but be silently rejected by record rules that restrict creation to the record's responsible user or team. We run the XMLRPC migration with a service account that has admin-equivalent access for the CRM and Helpdesk apps, and we temporarily disable restrictive record rules during the bulk load phase. Record rules are re-enabled after migration, and we validate that the inserted records are visible to the expected Odoo user and team.

  • Multi-enum field option cap of 20 in EspoCRM may lose data fidelity

    EspoCRM multi-enum fields are capped at 20 selectable options by default, which means any EspoCRM instance using a multi-select field with more than 20 options has records that were constrained on creation. When migrating to Odoo, those constrained values are the complete dataset — there is no unreported option data to recover. We detect multi-enum fields during discovery, report the option counts, and advise whether to split into multiple Odoo multi-select fields or leave as-is. If the customer chooses a split strategy, we apply it during the transform phase before Odoo import.

  • Filesystem attachment extraction is required for self-hosted EspoCRM

    Self-hosted EspoCRM instances store uploaded files on the server filesystem under data/files/ rather than in the database. A standard API extraction does not capture these files. We identify all Attachment records during discovery, extract the referenced files from the filesystem archive, transfer them to the Odoo filestore directory, and re-register each file as an ir.attachment record linked to the parent CRM record via res_model and res_id. This step is skipped for cloud-hosted EspoCRM instances where attachments are served via the API and streamed directly to Odoo's XMLRPC binary endpoint.

Migration approach

Six steps for a successful EspoCRM to Odoo CRM data migration

  1. Discovery and subscription scope confirmation

    We audit the source EspoCRM instance across API configuration, active entity types (standard and custom), record counts per entity, custom field definitions from entityDefs metadata, active workflow rules, and attachment volume. We pair this with a review of the target Odoo database: installed apps (CRM, Helpdesk, Project), existing res.partner records, current stage configuration, and user count. The discovery output is a written migration scope document including the res.partner deduplication strategy, the custom entity metadata export list, and the Odoo app activation recommendation if Helpdesk or Project is not yet active.

  2. Custom entity metadata export and documentation

    We export the entityDefs metadata for every EspoCRM Entity Manager custom entity type before any record extraction. The export includes field names, field types, relationship definitions (link-multiple, foreign-key), ACL rules, and panel layout. We translate this into an Odoo custom module specification document: the Python model class, field definitions, menu entries, and access control XML for each custom entity. The customer takes this specification to their Odoo developer or an Odoo partner for custom module implementation. In parallel, we stage the custom entity records in a temporary migration table that the developer imports once the module is deployed.

  3. res.partner deduplication pass and Account-first import

    We run a two-phase partner import. Phase one inserts EspoCRM Account records as Odoo res.partner with partner_type = 'company', producing a remapping table of EspoCRM Account ID to Odoo partner ID. Phase two imports EspoCRM Contact records, deduplicates by email against the phase-one partner set, and inserts remaining Contacts as res.partner with partner_type = 'contact' and parent_id pointing to the resolved Account partner. Any Contact with a null or deleted EspoCRM Account is inserted as a standalone partner and flagged for manual parent assignment review. The dedupe report is delivered to the customer's admin before phase two begins.

  4. Sandbox migration and reconciliation

    We run a full migration into an Odoo Sandbox or staging database using production-like data volume. The customer reconciles record counts (Accounts in, Contacts in, Leads in, Opportunities in, Cases in, Activities in), spot-checks 25-50 random records against the EspoCRM source, and reviews the custom entity metadata documentation. Any mapping corrections, stage probability mismatches, or res.partner dedupe issues are resolved in staging before production migration begins. Sign-off from the customer's Odoo admin is required before production cutover.

  5. Production migration in dependency order

    We run production migration in record-dependency order: res.partner (Accounts), res.partner (Contacts with dedupe), crm.lead (Leads), crm.lead (Opportunities), calendar.event (Meetings and Calls), project.task (Tasks), mail.message (Emails), ir.attachment (Documents and Files), helpdesk.ticket (Cases if Helpdesk is active), then custom entity staging tables (pending Odoo module deployment). Each phase emits a row-count reconciliation report before the next phase begins. We use Odoo's XMLRPC execute_kw with batch chunking of 500 records per call and exponential backoff on rate-limit responses.

  6. Cutover, delta migration, and automation handoff

    We freeze EspoCRM writes during cutover, run a final delta migration of records modified during the migration window, then enable Odoo as the system of record. We deliver the workflow inventory document listing every EspoCRM workflow rule with its trigger, conditions, and actions, plus a recommended Odoo Studio or ir.actions.server equivalent. We do not rebuild EspoCRM workflows as Odoo automated actions inside the migration scope; that work is handled by the customer's Odoo developer. We support a one-week hypercare window for reconciliation issues raised by the customer's team during the first business week in Odoo.

Platform deep dives

Context on both ends of the pair

EspoCRM logo

EspoCRM

Source

Strengths

  • AGPLv3 open-source license with no per-contact or per-seat recurring fee on self-hosted deployments.
  • Entity Manager provides a UI for creating custom entity types, fields, and relationships without writing code.
  • Full REST API covers all standard CRUD operations plus stream, metadata, and currency rate endpoints.
  • Cloud plans include daily backups with 7-day retention and all official extensions at no additional cost.
  • Both cloud-hosted and fully self-hosted deployment options with explicit customer data ownership.

Weaknesses

  • Email and SMS functionality requires third-party integrations and does not work natively out of the box.
  • Official integrations are limited to Google Workspace, Outlook, Zoom, VoIP, and Stripe.
  • Multi-enum field type is capped at 20 options by default and requires configuration changes to extend.
  • Large record volumes without server-side performance tuning cause slow list views and degraded search performance.
  • WebSocket domain references can persist after server migration if internal URL configuration is not fully updated.
Odoo CRM logo

Odoo CRM

Destination

Strengths

  • Modular open-source architecture lets teams start with CRM and add ERP apps as needs grow, all sharing one PostgreSQL database.
  • Free Community edition with no contact limits and full source code access means zero licensing cost for evaluation and small deployments.
  • Drag-and-drop Kanban pipeline with AI lead scoring gives a visual, prioritized view of the sales funnel without requiring custom configuration.
  • Native integrations with email, live chat, SMS, VoIP, WhatsApp, and social media feed all inbound leads into a single unified inbox.
  • Active Odoo Community Association (OCA) maintains dozens of community-maintained modules on GitHub for extended functionality.

Weaknesses

  • Gmail and email integration reliability is a recurring complaint — threads drop and conversations scatter across inboxes, disrupting sales team workflows.
  • Enterprise edition pricing stacks quickly: multiple apps at per-user rates ($25–$50/user/month) plus Odoo.sh hosting costs more than many SMBs anticipate.
  • Setup and configuration complexity increases significantly once custom fields, automation rules, and multiple installed modules are in play.
  • Odoo.sh trial databases run on a version (e.g., 18.3) that is not directly migratable to Odoo.sh, blocking the assisted migration path Odoo advertises.
  • Version upgrades between major Odoo releases (e.g., 17→18) frequently break custom module view definitions and XPath expressions, requiring manual remediation.

Complexity grading

How hard is this migration?

Standard CRM migration. All 8 core objects map 1:1 between EspoCRM and Odoo CRM.

B

Overall complexity

Standard migration

Derived from compatibility, mapping clarity, API constraints, and data volume across EspoCRM and Odoo CRM.

  • Object compatibility

    A

    All 8 core objects map 1:1 between EspoCRM and Odoo CRM.

  • Field mapping clarity

    C

    Field mapping is derived from defaults — final spec confirmed during the sample migration.

  • Timeline complexity

    B

    8-object category — typical timelines run 2–7 days end-to-end.

  • API constraints

    B

    EspoCRM: Not publicly documented; rate limits can be configured server-side in the EspoCRM config file.

  • Data volume sensitivity

    B

    EspoCRM doesn't expose a bulk API — REST + parallelization used for high-volume runs.

Estimator

Estimate your EspoCRM to Odoo CRM migration cost

Rule-based pricing — no per-record fees, no manual quotes. Migrations over 2M records are scoped individually.

Step 1

What are you migrating?

Pick a category, then your source and destination platforms.

Category

FAQ

Frequently asked questions about EspoCRM to Odoo CRM data migrations

Answers to the questions buyers ask most during EspoCRM to Odoo CRM migration scoping. Not seeing yours? Book a call.

Can't find your answer?

Walk through your EspoCRM to Odoo CRM migration with a real engineer — 30 minutes, free, written quote within 24 hours.

Book a free 30 minute consultation

Migrations under 20,000 Contacts and 4,000 Opportunities with no custom Entity Manager entities land between four and six weeks. Migrations with multiple custom entity types, large activity histories (over 300,000 records), Helpdesk ticket migration, or Odoo Community Edition destinations requiring custom module packaging move to eight to twelve weeks because of Odoo developer involvement for custom modules, res.partner deduplication pass, and XMLRPC batch sizing constraints.

Adjacent paths

Related migrations to explore

Ready when you are

Move from EspoCRM.
Land in Odoo CRM, intact.

Tell us record counts and timeline. We'll come back with a written quote inside 1 business day — no commitment, no sales pitch.

Accuracy guarantee Rollback included Quote in 1 business day