CRM migration
Field-level mapping, validation, and rollback between Civicrm and Twenty CRM. We move data and schema; workflows are rebuilt natively in Twenty CRM.
Civicrm
Source
Twenty CRM
Destination
Compatibility
6 of 12
objects map 1:1 between Civicrm and Twenty CRM.
Complexity
BStandard
Timeline
3-5 weeks
Overview
Moving from CiviCRM to Twenty CRM is a model-aware migration, not a straightforward record copy. CiviCRM uses an Activity-based engagement model where calls, emails, meetings, and tasks are all Activity subtypes with type IDs, while Twenty separates these into native Task, Note, and Event objects. We resolve that translation during scoping and preserve the original CiviCRM Activity type as a tag on each imported record. CiviCRM's relationship network (household membership, spousal, employee-employer) maps to Twenty's People-to-Company relationship system, with Organizations handled as Company records. CiviCase statuses are defined per-case-type in XML or PHP entityType rather than as a global list — we enumerate every active case_type during discovery and map each status set individually. Custom fields (single-record and multi-record) land as Twenty custom fields with their original labels preserved. ECK (Entity Construction Kit) entities, CiviMail mailings, CiviRules automated processes, and any Drupal/WordPress/Joomla CMS user accounts do not migrate; we deliver a written inventory of these for the customer's admin to assess.
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 Civicrm 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.
Civicrm
Contact (Individual, Household, Organization)
Twenty CRM
People or Company
1:manyCiviCRM Individual contacts map to Twenty People. Organization contacts map to Twenty Company. Household contacts — a CiviCRM-specific subtype representing multi-person family units — present a mapping challenge because Twenty has no Household equivalent. We map Household contacts to Twenty Company records (using the Household name as displayName) and store the member relationship in a custom relationship field on each People record. The contact network relationship chain (spousal, employee-employer, household membership) is preserved via Twenty's relationship system, with the original CiviCRM relationship type ID stored as a tag for audit.
Civicrm
Activity
Twenty CRM
Task, Note, or Event
1:manyCiviCRM Activity is a unified engagement record with activity_type_id distinguishing call, email, meeting, and other types. We split Activities at migration time: type_id 1 (Meeting) maps to Twenty Event, type_id 2 (Email) maps to Note with an is_email flag, type_id 3 (Call) maps to Task with TaskSubtype=Call, and all other types map to Task. The original CiviCRM activity_type_id is preserved in a custom tag on the target record. Activity assignments (assignee_id) map to Twenty workspace members by email match.
Civicrm
Group and GroupContact
Twenty CRM
People segment or tag
lossyCiviCRM Groups with static membership (GroupContact records) map to Twenty tags on People records. We preserve the group hierarchy where it exists. Dynamic (smart) groups that use CiviCRM search-based filtering cannot be fully reproduced because Twenty does not have a smart-group equivalent — we document each dynamic group's criteria as a written filter definition for the customer to recreate manually in Twenty's filter system.
Civicrm
Contribution
Twenty CRM
Custom Object (financial_payment) or Opportunity
lossyCiviCRM Contributions include financial_type, total_amount, currency, receive_date, payment_instrument, and source. We create a financial_payment custom object in Twenty (via Settings → Data Model) with fields for amount, currency, payment date, payment method, and contribution source. Organizations that prefer Opportunity tracking for donations map Contributions to Opportunity records using a donation record type. Price Sets on Contributions introduce variable line-item complexity that must be flattened — we flatten to a single total_amount unless the customer requests line-item preservation.
Civicrm
Membership
Twenty CRM
Custom Object (membership)
lossyCiviCRM Membership records include type, status, start_date, end_date, and source. Membership Price Sets allow complex tier structures. We create a membership custom object in Twenty with fields for membership_type, status, start_date, end_date, and source, linked to the Company record representing the member organization or the People record for individual members. Membership status transitions (e.g., New → Current → Expired) are preserved as tags rather than native status workflows.
Civicrm
Event
Twenty CRM
Event
1:1CiviCRM Events map to Twenty Event records. We map event title, start_date, end_date, location, description, and event_type. Price sets and participant fees flatten to a single registration_fee field. Participant roles (Attendee, Speaker, Volunteer) migrate as tags on the Event. Online registration profiles are not migrated as forms — we document the registration configuration for the customer's admin to recreate in Twenty's form or external registration tool.
Civicrm
Case (CiviCase)
Twenty CRM
Custom Object (case) or Task chain
lossyCiviCase stores case_id, case_type, status, start_date, and a set of related activities. Case statuses are defined per case_type in XML or PHP entityType — they are not a global status list. We enumerate every active case_type during discovery, extract its status option values, and map each set individually to a status picklist on the Twenty case custom object. The activity chain (case timeline) migrates as a sequence of Task records with a custom case_id reference. Cases that reference specific case_type XML files require us to parse those files during scoping.
Civicrm
Custom Fields (single-record Custom_*)
Twenty CRM
Custom object fields
1:1Single-record custom groups appear as fields on the parent entity via the custom.* selector in CiviCRM APIv4. We map each custom field to a typed Twenty custom field (text, number, date, picklist, multi-select picklist) on the corresponding object. Field labels and help text are preserved. Picklist options migrate as picklist values in Twenty's field settings.
Civicrm
Custom Fields (multi-record Custom_*)
Twenty CRM
Custom object with line items
1:1Multi-record custom groups appear as separate entities prefixed Custom_ in CiviCRM. We create a custom object in Twenty for each multi-record group and link it to the parent contact record via a lookup relationship. For sites with more than approximately 20 custom groups, we query individual records entity-by-entity rather than using APIv4's custom.* wildcard selector to avoid exceeding MySQL's maximum join count (61 joins). The multi-record table structure is flattened into a standard custom object row format.
Civicrm
Relationship
Twenty CRM
Relationship
1:1CiviCRM Relationships connect contacts (household member, employee-employer, spousal, etc.) with a relationship_type_id and bidirectional flag. We map each Relationship to Twenty's relationship system, resolving both contact references by their CiviCRM contact_id. The original relationship_type_id is preserved as a tag for audit. Bidirectional relationships are mapped as a single relationship in Twenty.
Civicrm
Tag
Twenty CRM
Tag
1:1CiviCRM Tags are flat labels attached to any entity via the EntityTag join table. We map tag names to Twenty tags, preserving the entity_id link on each migrated record. Multi-entity tagging (a tag applied to both a Contact and an Event) is handled with separate tag associations per record.
Civicrm
Grant (CiviCRM extension)
Twenty CRM
Custom Object (grant)
1:1Grants is an optional CiviCRM extension module. Where present, we create a grant custom object in Twenty with fields for grant_type, amount_requested, amount_granted, status, and deadline. Grant data often has tight coupling to the CMS user that submitted it — we map the CiviCRM contact associated with the grant rather than the CMS user account.
| Civicrm | Twenty CRM | Compatibility | |
|---|---|---|---|
| Contact (Individual, Household, Organization) | People or Company1:many | Fully supported | |
| Activity | Task, Note, or Event1:many | Fully supported | |
| Group and GroupContact | People segment or taglossy | Fully supported | |
| Contribution | Custom Object (financial_payment) or Opportunitylossy | Fully supported | |
| Membership | Custom Object (membership)lossy | Fully supported | |
| Event | Event1:1 | Fully supported | |
| Case (CiviCase) | Custom Object (case) or Task chainlossy | Fully supported | |
| Custom Fields (single-record Custom_*) | Custom object fields1:1 | Fully supported | |
| Custom Fields (multi-record Custom_*) | Custom object with line items1:1 | Fully supported | |
| Relationship | Relationship1:1 | Fully supported | |
| Tag | Tag1:1 | Fully supported | |
| Grant (CiviCRM extension) | Custom Object (grant)1:1 | 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.
Civicrm gotchas
Server-to-server migration requires CMS settings file portability
Multi-record custom groups can hit MySQL's 61-join limit
No native bulk export — data portability is API- or database-dependent
CiviCase statuses are per-case-type — not a global status list
Hosted Spark tier has no documented API rate limit — performance varies by 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 export path assessment
We audit the source CiviCRM instance across CMS integration (Drupal, WordPress, Joomla, Backdrop), PHP/MySQL version, CiviCRM version, CiviSpark tier (if hosted), API credentials, direct database access, and extension set. We identify ECK entity schemas, count custom groups, enumerate active case_types and their status option values, and assess the activity volume by type. We also determine the export path: REST API-first for Contacts, Activities, Groups, and Events with direct MySQL reads for large-volume custom tables and multi-record Custom_ groups where API pagination would be prohibitively slow.
Data model design and custom object creation
We design the destination schema in Twenty. This includes creating custom objects for Contributions (financial_payment), Memberships (membership), Cases (case), Grants (grant), and any ECK entities. We configure custom fields on People and Company for migrated single-record custom fields. We establish the CiviCase status picklist with values drawn from the per-case-type enumeration. Schema is built via Twenty's Settings → Data Model UI, with field types matched to CiviCRM data types (text, number, date, picklist, multi-select). Workspace members are invited before any record import so that assignee lookups can resolve during migration.
Test migration in Twenty sandbox
We run a representative subset migration (1,000-2,000 contacts, a sample of activity types, one case_type with its full status set) into a test Twenty workspace. The customer's team reconciles record counts, spot-checks 20-30 records against the CiviCRM source, validates the Activity-to-Task/Note/Event split, verifies case status mapping, and confirms relationship chain integrity. Mapping corrections are made here before the production migration begins.
Contact and Organization migration
We migrate in strict dependency order: Organization contacts (to Company) first, then Individual contacts (to People with Organizations resolved via CompanyId lookup), then Household contacts (to Company with member relationship tags). Each phase emits a row-count reconciliation report. Any contact without an email or phone is flagged for customer review. Duplicate detection uses email as the dedupe key with a secondary check on displayName for organizations.
Activity history migration
We split Activities by type during the transform phase. Meetings land as Twenty Event records with start/end times. Emails land as Note records with a custom email flag. Calls land as Task records with TaskSubtype preserved. All other Activity types land as Task records. The original CiviCRM activity_type_id is stored as a tag. Activities are imported after all People and Company records exist so that the assignee and target record lookups resolve without orphaned references.
Financials, memberships, cases, and custom data
Contributions migrate to the financial_payment custom object linked to the contributing contact. Memberships migrate to the membership custom object linked to the member contact. Cases migrate to the case custom object with the per-case-type status mapping applied and the activity chain reconstructed as Task records with a case_id reference. Multi-record custom groups migrate to their corresponding custom objects with parent lookups resolved to the CiviCRM contact_id preserved as a legacy_id field. ECK entities migrate as their own custom objects with explicit customer sign-off on each schema before import.
Cutover, validation, and scope handoff
We freeze writes in CiviCRM during cutover, run a final delta migration of records modified during the migration window, then enable Twenty as the system of record. We deliver the ECK entity inventory, CiviMail and CiviRule inventory, dynamic group criteria definitions, and CiviRules rebuild recommendations. We do not rebuild CiviRules as Twenty workflows inside the migration scope; that work is documented for the customer's admin. We support a one-week post-cutover window for reconciliation issues.
Platform deep dives
Civicrm
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 Civicrm 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
Civicrm: Not publicly documented — Spark tier has no published limit; self-hosted performance is infrastructure-dependent.
Data volume sensitivity
Civicrm 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 Civicrm to Twenty CRM migration scoping. Not seeing yours? Book a call.
Walk through your Civicrm 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 Civicrm
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.