CRM migration
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
Source
Twenty CRM
Destination
Compatibility
7 of 11
objects map 1:1 between Odoo CRM and Twenty CRM.
Complexity
BStandard
Timeline
4-6 weeks
Try the reverse
Overview
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.
Every standard and custom field arrives verified.
AI proposes the map; you confirm before any record moves.
Parent–child, lookups, and ownership stay linked.
Calls, emails, meetings — with original timestamps.
Documents, uploads, and inline notes move with the record.
Source platform
Odoo CRM platform overview
Scorecard, SWOT, gotchas, and pricing for Odoo CRM.
Destination platform
Twenty CRM platform overview
Scorecard, SWOT, gotchas, and pricing for Twenty CRM.
Data migration guide
The complete Twenty CRM migration guide
Data model, import mechanisms, field mapping strategy, pitfalls, and cutover — by the engineers running it.
Source platform guide
Odoo CRM migration guide
Understand the data you're exporting from Odoo CRM before mapping it.
Destination checklist
Twenty CRM migration checklist
Pre- and post-cutover tasks for moving onto Twenty CRM.
Source checklist
Odoo CRM migration checklist
Exit checklist for unwinding your Odoo CRM setup cleanly.
Why teams make this switch
Leaving
What's pushing teams away
Choosing
What's pulling them in
Object mapping
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)
Twenty CRM
Opportunity
1:1Odoo'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)
Twenty CRM
Opportunity
1:1Odoo 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)
Twenty CRM
Company
1:1Odoo 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)
Twenty CRM
Person
1:1Odoo 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
Twenty CRM
Pipeline Stage (configuration)
lossyOdoo 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
Twenty CRM
Workspace or Team (configuration)
lossyOdoo 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
Twenty CRM
Tag (custom field or label)
lossyOdoo 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
Twenty CRM
Task
1:1Odoo 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
Twenty CRM
File Attachment (URL or external storage)
1:1Odoo 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)
Twenty CRM
Opportunity (with note or custom field)
1:1Odoo 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)
Twenty CRM
Custom fields on Opportunity
lossyOdoo 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.
| Odoo CRM | Twenty CRM | Compatibility | |
|---|---|---|---|
| crm.lead (type=lead) | Opportunity1:1 | Fully supported | |
| crm.lead (type=opportunity) | Opportunity1:1 | Fully supported | |
| res.partner (company=True) | Company1:1 | Fully supported | |
| res.partner (company=False) | Person1:1 | Fully supported | |
| crm.stage | Pipeline Stage (configuration)lossy | Fully supported | |
| crm.team | Workspace or Team (configuration)lossy | Fully supported | |
| crm.tag | Tag (custom field or label)lossy | Fully supported | |
| mail.activity | Task1:1 | Fully supported | |
| ir.attachment | File Attachment (URL or external storage)1:1 | Fully supported | |
| sale.order (linked to crm.lead) | Opportunity (with note or custom field)1:1 | Fully supported | |
| Custom fields on crm.lead (via Studio or custom addon) | Custom fields on Opportunitylossy | Fully supported |
Gotchas + challenges
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 gotchas
Odoo.sh version gating blocks assisted migrations from trial
Enterprise modules fail to install on Community after database restore
Custom module view inheritance breaks between Odoo major versions
Custom fields risk losing their application context on Community
API access for Community is gated behind the Custom Plan
Twenty CRM gotchas
Import order is enforced and critical
Export limited to 20,000 records and visible columns only
Soft-deleted records count toward uniqueness and trigger restores
API rate limits cap at 200 req/min on Organization tier
No native email sequences — follow-up cadences require external tools
Pair-specific challenges
Migration approach
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.
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.
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.
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.
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.
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
Odoo CRM
Source
Strengths
Weaknesses
Twenty CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. 1 of 8 objects need a mapping; the rest are 1:1.
Overall complexity
Standard migration
Derived from compatibility, mapping clarity, API constraints, and data volume across Odoo CRM and Twenty CRM.
Object compatibility
1 of 8 objects need a mapping; the rest are 1:1.
Field mapping clarity
Field mapping is derived from defaults — final spec confirmed during the sample migration.
Timeline complexity
8-object category — typical timelines run 2–7 days end-to-end.
API constraints
Odoo CRM: Not publicly documented; no published rate limit found in Odoo's official developer documentation.
Data volume sensitivity
Odoo CRM doesn't expose a bulk API — REST + parallelization used for high-volume runs.
Estimator
Rule-based pricing — no per-record fees, no manual quotes. Migrations over 2M records are scoped individually.
Step 1
Pick a category, then your source and destination platforms.
Category
FAQ
Answers to the questions buyers ask most during Odoo CRM to Twenty CRM migration scoping. Not seeing yours? Book a call.
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 consultationAdjacent paths
Other ways to leave Odoo CRM
Other ways to arrive at Twenty CRM
Ready when you are
Tell us record counts and timeline. We'll come back with a written quote inside 1 business day — no commitment, no sales pitch.