CRM migration
Field-level mapping, validation, and rollback between OPEX 365 CRM and Twenty CRM. We move data and schema; workflows are rebuilt natively in Twenty CRM.
OPEX 365 CRM
Source
Twenty CRM
Destination
Compatibility
7 of 11
objects map 1:1 between OPEX 365 CRM and Twenty CRM.
Complexity
BStandard
Timeline
3-5 weeks
Overview
Moving from OPEX 365 CRM to Twenty CRM is a structural migration across fundamentally different platforms: OPEX 365 CRM runs on Microsoft Dataverse with Azure Active Directory integration, tiered per-user licensing, and separate Sales and Customer Service modules; Twenty CRM is an open-source TypeScript application backed by PostgreSQL with a per-seat cloud tier at $9-$19/user and a free self-hosted option. The migration requires extracting from the Dataverse API (with its service protection limits, polymorphic activityparty relationships, and base64-encoded annotation records), transforming the data to Twenty's Company, Person, and Opportunity objects, and importing through the GraphQL API with a 100/minute rate limit on Pro tier. Workflows, automations, and security roles from OPEX 365 CRM do not migrate; we deliver a written inventory of every active workflow and automation requiring manual rebuild in Twenty. Implementation cost relief is immediate: OPEX 365 CRM implementations routinely cost $5,000 to over $150,000 in partner consulting fees; Twenty eliminates that entire category.
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.
Why teams make this switch
Leaving
What's pushing teams away
Choosing
What's pulling them in
Object mapping
Each row shows how a OPEX 365 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.
OPEX 365 CRM
Contact
Twenty CRM
Person
1:1OPEX 365 CRM Contact records map to Twenty Person objects. We map fullname, emailaddress1, telephone1, address fields, and lifecycle-related properties to their Twenty equivalents. Owner assignment resolves by email match against Twenty workspace members. If the customer's OPEX 365 CRM uses custom contact properties (e.g., custom fields from Dataverse), we create matching custom fields in Twenty's Data Model before import using the Metadata API. The Person.recordId is stored for use as a lookup target by activity and opportunity records.
OPEX 365 CRM
Account
Twenty CRM
Company
1:1OPEX 365 CRM Account records map to Twenty Company objects. We map name, website, industry, numberofemployees, annualrevenue, address fields, and custom account properties. Account is created before any Person import so that the Person.companyId lookup is satisfied at the moment of Person insert. The company record is also the dedupe key for parent-child hierarchy preservation.
OPEX 365 CRM
Opportunity
Twenty CRM
Opportunity
1:1OPEX 365 CRM Opportunity records map to Twenty Opportunity objects. Pipeline stage labels and deal amount migrate directly. We preserve estimatedclosedate, closeprobability (computed from stage mapping), and opportunity type. The opportunity.personId and opportunity.companyId lookups resolve to the migrated Person and Company records respectively. Stage names are stored as-is; the customer's admin recreates pipeline stages in Twenty after migration.
OPEX 365 CRM
Lead
Twenty CRM
Person (merged)
many:1OPEX 365 CRM Lead records that are unqualified prospects (not yet converted to Contact) map to Twenty Person records. Leads that have already converted in OPEX 365 CRM are handled as their target object type (Contact or Account). Where the customer uses Leads as a separate queue, we flag the unconverted Lead count during scoping and either migrate them as Person records with a lead_source property or recommend recreating them as a custom Pipeline view. The choice is made during scoping based on the customer's process.
OPEX 365 CRM
Cases (Incident)
Twenty CRM
Custom Object or Comment
lossyOPEX 365 CRM Cases (incident entity) have no direct standard equivalent in Twenty CRM. We map the incident to a custom object (e.g., SupportCase) created via Twenty's Metadata API before migration, with fields for case number, status, priority, subject, originating contact, and account. If Twenty's standard Comments model on Person or Company is sufficient for the customer's support workflow, we migrate Cases as Comments linked to the corresponding Person or Company record instead.
OPEX 365 CRM
Activity (emails, calls, tasks, appointments)
Twenty CRM
Task
1:1OPEX 365 CRM Activities (activitypointer records) map to Twenty Task objects. The polymorphic activityparty references are resolved to the target Person or Company ID during transformation. We preserve activity type (email, call, task, meeting), timestamp, subject, description, duration (for calls), and outcome. TaskAssignee resolves to the migrated Owner/User. Activity records with no resolvable target party are flagged for manual assignment review post-migration.
OPEX 365 CRM
Note (annotation)
Twenty CRM
Task (Note subtype)
1:1OPEX 365 CRM Notes stored as annotation entities with base64-encoded file content require separate extraction using the Dataverse RetrieveContent discharge API. We extract binary attachment bodies to staging blob storage, then remap them as file references on Twenty Task records of note type. The note body text migrates as the Task description, and any file attachments are uploaded to Twenty's attachment storage and linked to the parent record (Person, Company, or Opportunity). This is a distinct extraction step from the standard record migration.
OPEX 365 CRM
Product
Twenty CRM
Custom Object (Product)
lossyOPEX 365 CRM Product records with pricing linked via productpricelevel entities map to a custom Product object in Twenty created via the Metadata API. Product name, SKU, and pricing tier information migrate with pricing stored as custom fields. Bundle structures are noted as a separate reassembly task for the customer's admin post-migration since Twenty's standard Opportunity model does not include line-item bundles.
OPEX 365 CRM
User (owner)
Twenty CRM
Workspace Member
1:1OPEX 365 CRM User records map to Twenty workspace members by email address match. Owner assignments on Contact, Account, Opportunity, and Activity records resolve to the corresponding Twenty user. Any OPEX 365 CRM owner with no matching email in the Twenty workspace goes to a reconciliation queue; the customer provisions the missing user before record import resumes. Inactive OPEX 365 CRM users can be migrated as inactive Twenty members or archived with their record assignments reassigned.
OPEX 365 CRM
Custom Entity (Dataverse)
Twenty CRM
Custom Object
1:1OPEX 365 CRM custom entities discovered via the Dataverse EntityDefinitions endpoint map to Twenty custom objects created via the Metadata API. We enumerate all custom entity schemas during pre-migration discovery, generate matching field definitions with type conversion (Dataverse OptionSet becomes Twenty select, Dataverse Lookup becomes Twenty relation, etc.), then import the data. Lookup relationships between custom entities and standard entities (Contact, Account) are preserved by resolving the parent record ID before the child record inserts.
OPEX 365 CRM
Pipeline and Stage configuration
Twenty CRM
Pipeline configuration
lossyOPEX 365 CRM pipeline and stage metadata does not migrate automatically and is not transferred as code. We document the source pipeline structure (pipeline names, stage labels, stage order, stage probabilities) in a written configuration guide. The customer's admin recreates these in Twenty's pipeline settings after migration. Closed-won and closed-lost stage mappings are preserved in the documentation for accurate reporting continuity.
| OPEX 365 CRM | Twenty CRM | Compatibility | |
|---|---|---|---|
| Contact | Person1:1 | Fully supported | |
| Account | Company1:1 | Fully supported | |
| Opportunity | Opportunity1:1 | Fully supported | |
| Lead | Person (merged)many:1 | Fully supported | |
| Cases (Incident) | Custom Object or Commentlossy | Fully supported | |
| Activity (emails, calls, tasks, appointments) | Task1:1 | Fully supported | |
| Note (annotation) | Task (Note subtype)1:1 | Fully supported | |
| Product | Custom Object (Product)lossy | Fully supported | |
| User (owner) | Workspace Member1:1 | Fully supported | |
| Custom Entity (Dataverse) | Custom Object1:1 | Fully supported | |
| Pipeline and Stage configuration | Pipeline configurationlossy | 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.
OPEX 365 CRM gotchas
Dataverse API rate limits vary by license tier
Custom entity schemas require manual enumeration
Activity Party relationships are polymorphic and fragile
Legacy attachment storage requires separate extraction
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
Schema discovery and scoping
We audit the OPEX 365 CRM environment through the Dataverse API, enumerating all standard and custom entity schemas via the EntityDefinitions endpoint. We capture object counts, custom field definitions, owner assignments, pipeline and stage configurations, and activityparty relationship patterns. We pair this with a Twenty workspace readiness assessment: verifying the self-hosted or cloud deployment URL, confirming workspace creation, and checking that the Data Model settings page is accessible for custom field creation. The discovery output is a written migration scope with object counts, a preliminary field mapping document, and a Twenty Data Model creation checklist.
Twenty Data Model preparation
We create all custom objects and custom fields in Twenty via the Metadata API before any data moves. This includes custom objects for Support Cases, any Dataverse custom entities, and all custom fields on Person, Company, and Opportunity. We also invite all team members to the Twenty workspace at this stage so that owner lookups (personId, companyId) can resolve at migration time. Workspace members must accept their invitations and exist in Twenty before the record import begins; otherwise owner assignments cannot be mapped and records land unassigned.
Field mapping and data quality pass
We build the complete field mapping document mapping every OPEX 365 CRM Dataverse attribute to its Twenty equivalent, with type conversion rules (OptionSet values become select options, lookup fields become relation IDs, date fields normalized to ISO 8601). We run a data quality pass identifying duplicates, incomplete records, and orphaned activityparty references. Records that fail quality thresholds are flagged for customer review before migration. The mapping document is validated against Twenty's actual schema (created in Step 2) to catch any missing field definitions before extraction begins.
Dataverse extraction with rate-limit handling
We extract records from OPEX 365 CRM using the Dataverse REST API with service protection limit handling. We pace extraction jobs using retry-after headers, chunk large record sets into batches of 200-300 records per request, and back off exponentially when rate limits are hit. Annotation records (Notes and attachments) are extracted separately using the RetrieveContent discharge API and stored in staging blob storage with a reference map linking each annotation to its parent record ID. The extraction sequence follows dependency order: Users and Owners first, then Accounts (as Company), then Contacts (as Person), then Opportunities, then Activities, then Custom Entities, then annotation files.
Transform, validate, and Twenty import
We transform extracted records to Twenty's GraphQL input format, applying field mapping rules, type conversions, and owner ID resolution. Activity records are processed through the polymorphic party resolution logic, with any unresolved references held in a review queue. We import records in dependency order (Company before Person, Person before Opportunity, Person/Company before Task) using Twenty's REST or GraphQL API with chunking and exponential backoff to respect the 100/minute (Pro) or 200/minute (Organization) rate limit. Annotation files are uploaded to Twenty's attachment storage and linked to their parent Task records after the parent record is confirmed in Twenty.
Reconciliation, cutover, and handoff
We run a row-count reconciliation comparing source object counts to destination record counts for every object class. We spot-check 25-50 records against the OPEX 365 CRM source to validate field accuracy, relationship integrity, and timestamp preservation. Any orphaned activityparty records held in the review queue are resolved manually with the customer's input. We deliver the Pipeline and Stage configuration guide for the customer's admin to recreate in Twenty's settings. We do not rebuild OPEX 365 CRM workflows, plugins, or security roles; these are documented in the migration scope and rebuilt by the customer's admin or a Twenty implementation partner as a separate engagement.
Platform deep dives
OPEX 365 CRM
Source
Strengths
Weaknesses
Twenty CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. 3 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 OPEX 365 CRM and Twenty CRM.
Object compatibility
3 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
OPEX 365 CRM: Varies by license tier and environment; not publicly documented for all tiers.
Data volume sensitivity
OPEX 365 CRM exposes a bulk API — large-volume migrations stream efficiently.
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 OPEX 365 CRM to Twenty CRM migration scoping. Not seeing yours? Book a call.
Walk through your OPEX 365 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 OPEX 365 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.