CRM migration

Migrate from Odoo CRM to Twenty CRM

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

Odoo CRM logo

Odoo CRM

Source

Twenty CRM

Destination

Twenty CRM logo

Compatibility

64%

7 of 11

objects map 1:1 between Odoo CRM and Twenty CRM.

Complexity

BStandard

Timeline

4-6 weeks

Rollback included Accuracy guarantee Field-level validation

Try the reverse

Twenty CRM
Odoo CRM

Overview

What this migration involves

Moving from Odoo CRM to Twenty CRM is a focused migration from a broad ERP-first platform to a CRM-only, opinionated tool. Odoo stores both Leads and Opportunities in a single crm.lead table distinguished by a type field, while Twenty maintains separate Opportunity and related record objects. We split Odoo's crm.lead records into the correct Twenty destination records, preserve the original Lead/Opportunity type in a custom field for audit, and handle Odoo's foreign-key dependencies (stage_id, team_id, partner_id) by resolving them in the correct sequence. Odoo's Community edition provides no XML-RPC API access without an Enterprise plan, so we fall back to direct PostgreSQL read-only export when needed. Automation rules, Server Actions, AI scoring, and Enterprise-only modules do not migrate; we deliver a written inventory of these for the customer's admin to rebuild or reconfigure in Twenty.

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

Odoo CRM logo

Odoo CRM

What's pushing teams away

  • Mail and Gmail integration is repeatedly cited on Reddit and G2 reviews as unreliable — email threads drop, conversations scatter across inboxes, and the sync between Odoo and Gmail breaks under common configurations.
  • Setup complexity grows with business size: reviews note that advanced features require additional configuration and customization, and costs rise steeply once multiple paid apps and users are stacked together.
  • Support response times frustrate Enterprise customers who encounter bugs during version upgrades, with some noting that critical issues go unresolved for weeks.
  • Some teams migrate away from Odoo toward modern CRMs like Attio because they want a slicker, more opinionated UX rather than Odoo's broad-but-configurable interface.
  • Companies leave Odoo.sh specifically when they want more control over their infrastructure — alternatives like udoocloud.pro target users who want direct shell access and no platform vendor lock-in.

Choosing

Twenty CRM logo

Twenty CRM

What's pulling them in

  • Top open-source CRM on GitHub with 40.6K stars, giving teams full source code access and infrastructure ownership without per-feature licensing surprises.
  • Free self-hosting under AGPL-3.0 means unlimited users and custom objects for the cost of cloud infrastructure alone, typically $20–100/month.
  • Pricing page explicitly mocks competitors for charging add-on fees for API access, webhooks, and workflows — transparency that resonates with RevOps teams burned by Salesforce.
  • Unlimited custom objects and fields with no price impact, letting teams shape the data model to their business rather than forcing business into rigid schemas.
  • Modern TypeScript/React/PostgreSQL stack means developer-led teams can extend, self-host, or integrate without fighting legacy architecture.

Object mapping

How Odoo CRM objects map to Twenty CRM

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

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

Odoo CRM

crm.lead (type=lead)

maps to

Twenty CRM

Opportunity

1:1
Fully supported

Odoo's crm.lead table holds both Leads and Opportunities in one table, distinguished by the type field. We filter type='lead' and migrate these as Twenty Opportunities in a New or Prospect stage. The original Odoo type value is preserved in a custom field odoo_record_type__c for audit. Fields including name, email_from, phone, source_id, and description map to their Twenty equivalents. Stage resolution uses crm.stage.name mapped against Twenty's pipeline stage values.

Odoo CRM

crm.lead (type=opportunity)

maps to

Twenty CRM

Opportunity

1:1
Fully supported

Odoo Opportunities (crm.lead with type='opportunity') map directly to Twenty Opportunities. Key fields including expected_revenue, probability, date_closed, and partner_id migrate. The partner_id foreign key resolves to a twenty.person record that we create or match during the Partner/Contact migration phase. stage_id maps to the nearest Twenty pipeline stage; probability percentages are rounded to integer and stored on the Opportunity.

Odoo CRM

res.partner (company=True)

maps to

Twenty CRM

Company

1:1
Fully supported

Odoo res.partner records where is_company=True map to Twenty Company records. Fields including name, street, city, country_id, phone, website, and email map directly. The company's domain from website is used as a dedupe key. Company records are created before any Person or Opportunity import so that lookup relationships are satisfied at insert time.

Odoo CRM

res.partner (company=False)

maps to

Twenty CRM

Person

1:1
Fully supported

Odoo res.partner records where is_company=False map to Twenty Person records. The linked company partner (company_id field) becomes the Person's company link in Twenty. Fields including name, email, phone, function (job title), and address fields migrate. We match existing Persons by email during import to avoid duplicate records.

Odoo CRM

crm.stage

maps to

Twenty CRM

Pipeline Stage (configuration)

lossy
Fully supported

Odoo crm.stage records per team map to Twenty pipeline stage values. Each stage has a name, sequence, and probability. We extract all custom stages the customer has added and map their names and probabilities to Twenty's pipeline stage configuration. The Odoo team-stage relationship (crm.team.stage.rel) is documented but simplified in Twenty because Twenty supports a single pipeline per workspace by default.

Odoo CRM

crm.team

maps to

Twenty CRM

Workspace or Team (configuration)

lossy
Fully supported

Odoo Sales Teams (crm.team) have members and independent pipeline stage configurations. Twenty does not replicate Odoo's per-team pipeline model. We document each Odoo team's member list and map team ownership to Opportunity assignment. If the customer requires team scoping in Twenty, we configure Twenty's workspace permissions as the equivalent structure.

Odoo CRM

crm.tag

maps to

Twenty CRM

Tag (custom field or label)

lossy
Fully supported

Odoo crm.tag records are a flat list applied via many2many to crm.lead. Tag names migrate as a tag array on Twenty Opportunities. If the customer uses tags for segmentation, we map them to a multi-select custom field on Opportunity rather than Twenty's native label system, depending on use case.

Odoo CRM

mail.activity

maps to

Twenty CRM

Task

1:1
Fully supported

Odoo mail.activity records linked to crm.lead migrate to Twenty Tasks attached to the corresponding Opportunity. Fields including activity_type_id, date_deadline, user_id (mapped to Person or Team Member), and note body migrate. Activity ordering is preserved by setting the task's created_at timestamp to the original Odoo activity date. High-volume activity exports are chunked to avoid overwhelming the Twenty REST API.

Odoo CRM

ir.attachment

maps to

Twenty CRM

File Attachment (URL or external storage)

1:1
Fully supported

Odoo ir.attachment records linked to crm.lead are stored in the Odoo filestore. We export attachments as files, preserve their original filenames and MIME types, and generate a mapping of ir_attachment.id to file path. In Twenty, we store the file URL or re-upload to the customer's chosen storage. Large attachment volumes are exported separately and sequenced after parent Opportunity records to prevent orphaned references.

Odoo CRM

sale.order (linked to crm.lead)

maps to

Twenty CRM

Opportunity (with note or custom field)

1:1
Fully supported

Odoo sale.order records linked to Opportunities carry line items, pricing, and taxes. Twenty does not have a native Quotation or Order object in the standard CRM scope. We export sale.order as a structured JSON payload and store it as a custom field on the linked Twenty Opportunity, preserving order lines, totals, and currency. The customer's admin can review and recreate quotes in Twenty or a connected tool if needed.

Odoo CRM

Custom fields on crm.lead (via Studio or custom addon)

maps to

Twenty CRM

Custom fields on Opportunity

lossy
Fully supported

Odoo custom fields on crm.lead are stored as columns in the database. We export field definitions (name, field type, selection options) and map them to Twenty custom fields on Opportunity. Selection fields map to Twenty dropdown custom fields; many2one and many2many fields require schema review to determine the correct lookup relationship in Twenty. We pre-create the destination custom field schema before data import.

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.

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

Twenty CRM logo

Twenty CRM gotchas

High

Import order is enforced and critical

High

Export limited to 20,000 records and visible columns only

Medium

Soft-deleted records count toward uniqueness and trigger restores

Medium

API rate limits cap at 200 req/min on Organization tier

Low

No native email sequences — follow-up cadences require external tools

Pair-specific challenges

  • Odoo crm.lead holds both Lead and Opportunity records

    Odoo does not have separate Lead and Opportunity objects — both live in crm.lead distinguished by a type field. If a customer has not maintained consistent naming or has converted Leads in-place, determining the correct migration split requires reviewing the type field, stage_id, and date_closed values. We query crm.lead filtered by type and stage progression to build a split matrix before migration, and preserve the original type in a custom field on each Twenty record for reconciliation.

  • Community edition has no guaranteed XML-RPC API

    Odoo's External API (XML-RPC) is only supported for customers on the Custom Plan (Enterprise). Community edition users on Odoo Online or self-hosted Community do not have reliable API access. We fall back to direct PostgreSQL read-only connection for Community edition exports, querying the crm.lead, res_partner, crm_stage, crm_team, mail_activity, and ir_attachment tables directly. We request read-only credentials during discovery and never write to the source database.

  • Enterprise-only modules break on Community restore

    When migrating from Odoo.sh Enterprise to Twenty, the Odoo.sh PostgreSQL dump marks Enterprise-only modules (CRM Enterprise, Documents, WhatsApp, AI, Social) as installed. These modules are not installable on a non-Odoo target. We strip all ir_module_module rows for Enterprise-only modules before the migration proceeds, and document which Enterprise features (automation rules, AI scoring) are unavailable in Twenty so the customer can plan rebuilds.

  • Odoo custom fields require schema pre-creation in Twenty

    Odoo custom fields on crm.lead defined via Studio or in custom addons are stored as columns with specific PostgreSQL types (varchar, integer, jsonb for many2one/many2many). We export the field definitions alongside the data, but Twenty's custom field schema must be created before the data import. We run a schema-first phase: create the custom field in Twenty via the API, verify the field is accessible, then load the data. If a custom field references an Odoo-specific object (e.g., a many2one to a module not migrated), we flag it and store the raw value in a text field.

  • Activity history requires parent-record resolution before import

    Odoo mail.activity records are loosely coupled to crm.lead via res_id and res_model. Before loading activity records into Twenty Tasks, we must resolve each activity's parent Opportunity in Twenty using the crm.lead id mapping table generated during the opportunity migration phase. Without this resolution step, tasks are orphaned with no linked Opportunity. We chunk activity exports in batches of 500 and resolve parent IDs per batch to keep API call volume manageable.

Migration approach

Six steps for a successful Odoo CRM to Twenty CRM data migration

  1. Discovery and Odoo edition assessment

    We audit the source Odoo instance across edition (Community vs Enterprise vs Odoo.sh), Odoo version (16, 17, 18, 19), installed modules, custom fields defined in Studio or custom addons, and the record counts across crm.lead, res_partner, crm_stage, crm_team, mail_activity, and ir_attachment. We confirm whether XML-RPC API access is available or whether we will use direct PostgreSQL read-only export. The discovery output is a written migration scope, record-count estimate, and an Odoo edition recommendation for API access if the customer plans to maintain bidirectional sync post-migration.

  2. Schema pre-creation in Twenty

    We create the destination schema in Twenty before any data migration begins. This includes provisioning all custom fields on Opportunity and Company, configuring pipeline stages to match the Odoo crm.stage names and probabilities, and setting up any custom objects the customer requires. If Odoo custom fields reference many2one relationships that cannot map directly to Twenty (e.g., to an Odoo-specific module), we create placeholder text fields and document the mapping gap. Schema is validated via Twenty's API before data loads begin.

  3. Lead-Opportunity split analysis

    We run a pre-migration analysis query against the Odoo PostgreSQL database or XML-RPC endpoint to categorize every crm.lead record by type and stage progression. Records with type='lead' and no closed date are migrated as Twenty Opportunities in a Prospect stage; records with type='opportunity' and a stage_id mapping are migrated as Opportunities with their expected probability. The split matrix is reviewed with the customer's admin before migration runs to catch any edge cases where Odoo's type field is inconsistently maintained.

  4. Parent-record dependency resolution

    We export and load records in strict dependency order: Companies (from res.partner where is_company=True), Persons (from res.partner where is_company=False with company link resolved), then Opportunities (from crm.lead with partner_id resolved to the new Person ID). Each phase emits a reconciliation row count before the next phase begins. Any Odoo partner_id that does not resolve to a new Twenty Person triggers a hold record in the reconciliation queue for the customer admin to resolve.

  5. Activity and attachment migration

    We export mail.activity records in batches of 500, resolve each activity's parent Opportunity via the crm.lead id mapping table, and load into Twenty as Task records with the original date preserved. Attachment exports run in parallel using the ir.attachment id-to-file mapping, with filenames and MIME types preserved. Attachments are re-uploaded to the customer's chosen storage and referenced by URL in Twenty. Both phases run after the Opportunity migration is complete and validated.

  6. Cutover, validation, and automation handoff

    We freeze writes to the Odoo CRM during the cutover window, run a final delta migration of any records modified during the migration, then mark Twenty as the system of record. We deliver a written inventory of all Odoo automation rules, Server Actions, and AI scoring rules with a recommendation for how to implement equivalent logic in Twenty or a connected tool. We support a one-week hypercare window for reconciliation issues raised by the sales team. We do not rebuild Odoo automations inside the migration scope; that work is documented for the customer's admin.

Platform deep dives

Context on both ends of the pair

Odoo CRM logo

Odoo CRM

Source

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.
Twenty CRM logo

Twenty CRM

Destination

Strengths

  • AGPL-3.0 open-source license with full source code on GitHub — no vendor lock-in, no sunset risk.
  • Unlimited users and unlimited custom objects on self-hosted, with no feature gating based on headcount.
  • REST and GraphQL APIs available on all paid tiers, not locked behind an enterprise add-on fee.
  • MCP server and webhooks shipped as standard features, not premium upgrades.
  • Modern PostgreSQL-backed data model that developer teams can query, extend, and self-host.

Weaknesses

  • Recent v1.0 release means limited production hardening compared to CRMs with multi-year operational track records.
  • No native email sequencing or sales engagement tools — follow-up cadences require a separate platform.
  • No native two-way email sync or inbox integration, requiring third-party connectors for full activity logging.
  • Self-hosting 'free' pricing hides real infrastructure and DevOps costs that stack up over time.
  • Workflow automation is functional but lacks the complexity needed for sophisticated multi-step sales motions.

Complexity grading

How hard is this migration?

Standard CRM migration. 1 of 8 objects need a mapping; the rest are 1:1.

B

Overall complexity

Standard migration

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

  • Object compatibility

    B

    1 of 8 objects need a mapping; the rest are 1:1.

  • 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

    Odoo CRM: Not publicly documented; no published rate limit found in Odoo's official developer documentation.

  • Data volume sensitivity

    B

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

Estimator

Estimate your Odoo CRM to Twenty 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 Odoo CRM to Twenty CRM data migrations

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

Can't find your answer?

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

Book a free 30 minute consultation

Most migrations land between four and six weeks for accounts with fewer than 20,000 crm.lead records, standard pipeline stages, and no custom addons beyond Odoo Studio fields. Migrations with multiple custom addons, large activity histories (over 300,000 mail.activity records), multiple Odoo Sales Teams requiring workspace mapping, or Enterprise-to-Community edition transitions move to eight to fourteen weeks because of PostgreSQL schema analysis, custom field type resolution, and Enterprise module flagging.

Adjacent paths

Related migrations to explore

Ready when you are

Move from Odoo CRM.
Land in Twenty 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