CRM migration

Migrate from Function 365 to Twenty CRM

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

Function 365 logo

Function 365

Source

Twenty CRM

Destination

Twenty CRM logo

Compatibility

86%

12 of 14

objects map 1:1 between Function 365 and Twenty CRM.

Complexity

BStandard

Timeline

48–72 hours of clock time

Rollback included Accuracy guarantee Field-level validation

Overview

What this migration involves

Function 365 runs on a Dynamics 365 data model: Account, Contact, Lead, Opportunity, and Task objects with standard Dynamics field names and custom fields managed through the Xrm toolbox or Power Apps maker portal. Twenty CRM uses a simpler object graph — People (unifying contacts and leads), Companies (accounts), Opportunities (deals), Notes, and Tasks — with field types that include Text, Select, Multi-Select, Relation, Currency, Date, and Number. The migration carries everything Function 365 stores natively into Twenty's schema. The structural difference is Dynamics 365's separate Lead and Contact objects collapsing into Twenty's single People object, Dynamics owner-resolution by GUID becoming Twenty's user lookup by email, and Dynamics 365 business rules and Power Automate flows having no Twenty equivalent and requiring manual rebuild using Twenty's workflow builder. We sequence the migration: export Function 365 data via the Dynamics Web API or KingswaySoft CSV export, transform field names and pick-list values, create Twenty custom fields for Dynamics custom attributes, then import in dependency order (Companies first, then People, then Opportunities, then Notes and Tasks). A delta-pickup window captures in-flight changes during cutover.

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

Function 365 logo

Function 365

What's pushing teams away

  • Functional Medicine + private-healthcare niche means general medical practices, NHS-primary settings, or non-UK clinics often have a tighter fit with Cliniko, Pabau, or country-specific PMS.
  • Implementation requires a paid specialist session (£55/session) plus optional onsite training (£350) — small clinics that expected pure self-serve may find the onboarding gate frustrating.
  • Smaller installed base than Cliniko, Pabau, or Halaxy means fewer integrations, fewer third-party services, and less peer benchmarking for procurement.
  • No public API documentation surfaced in research; integration with lab vendors, payment processors, or downstream EHRs may require vendor coordination.
  • Solo Practitioner tier (£132/month) is steeper than freemium-style PMS competitors; smallest practices may find the entry price hard to justify against single-clinician alternatives.

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 Function 365 objects map to Twenty CRM

Each row shows how a Function 365 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.

Function 365

Account

maps to

Twenty CRM

Company

1:1
Fully supported

Function 365 Account maps directly to Twenty CRM Company. The Account.Name becomes Company.name (text). Account.website maps to Company.domainUrl. Account.industry maps to a Twenty Select-type Industry field if configured. Parent account hierarchies (Account.ParentAccountId) map to a self-referential Company relation in Twenty. Multi-address accounts (Address fields) are consolidated into Twenty's primary address fields.

Function 365

Contact

maps to

Twenty CRM

People

1:1
Fully supported

Function 365 Contact maps to Twenty CRM People as a primary mapping. Contact.firstname → People.firstName, Contact.lastname → People.lastName, Contact.emailaddress1 → People.email, Contact.mobilephone → People.phone (or a custom phone field if multiple phone types exist). The Contact's parent CustomerId (Account lookup) maps to the People.company relation by email-domain matching or explicit Account.Name lookup.

Function 365

Lead

maps to

Twenty CRM

People

many:1
Fully supported

Function 365 Lead and Contact both map into Twenty CRM People. The distinction is preserved via a People.type field (set to 'Lead' or 'Contact' during import) and a status flag. If your Function 365 has closed/won leads that represent existing customers, those records receive a 'Converted Lead' tag in Twenty so your sales team can filter them separately without losing history.

Function 365

Opportunity

maps to

Twenty CRM

Opportunity

1:1
Fully supported

Function 365 Opportunity maps to Twenty CRM Opportunity with minimal transformation. Opportunity.name → Opportunity.name, Opportunity.estimatedvalue → Opportunity.amount (Currency type), Opportunity.closeprobability → Opportunity.probability (Number 0–100), Opportunity.estimatedclosedate → Opportunity.closeDate. The Dynamics StageCode (option set) maps to a Twenty Select-type Stage field value-by-value.

Function 365

Lead (lead_to_opportunity_process)

maps to

Twenty CRM

Opportunity

1:many
Fully supported

Function 365 Lead records that have been converted into Opportunities retain both the original LeadId and the new OpportunityId. We map the converted Opportunity to Twenty Opportunity directly. The original Lead fields (lead quality score, source campaign) are preserved as custom fields on the Twenty Opportunity record so closed-loop reporting remains intact.

Function 365

Task

maps to

Twenty CRM

Task

1:1
Fully supported

Function 365 Task (activity) maps to Twenty CRM Task. Task.subject → Task.title, Task.scheduledend → Task.dueDate, Task.statuscode → Task.status, Task.ownerid → Task.assignee (resolved by email to a Twenty Workspace Member). Task regarding fields (RegardingObjectId pointing to Account, Contact, or Opportunity) map to the Task's relation field in Twenty.

Function 365

Email (EmailSendPointer / EmailHash)

maps to

Twenty CRM

Task

1:1
Fully supported

Function 365 email activities are stored as EmailSendPointer records with body text in EmailRouterMailboxTrackingPolicy-linked tables. We extract the subject, body text, sender, recipients, and sent-on timestamp and represent each email as a Twenty Task with Type='Email', subject from the email subject line, and the original timestamp preserved. Email threading is not maintained as a native feature in Twenty but can be reconstructed from a related-email custom field.

Function 365

PhoneCall

maps to

Twenty CRM

Task

1:1
Fully supported

Function 365 PhoneCall activity maps to Twenty Task with Type='Call'. Subject is built from the CallDirection (Incoming/Outgoing) and the regarding record name. Duration (PhoneCall.duration) is stored as a custom Number field (CallDurationMinutes__c) since Twenty's native Task does not include duration.

Function 365

Appointment

maps to

Twenty CRM

Task

1:1
Fully supported

Function 365 Appointment maps to Twenty Task with Type='Meeting'. Subject, scheduledstart, scheduledend, and location are mapped to the corresponding Twenty Task fields. The Appointment's requiredattendees (PartyList of Contact/Lead records) are stored as a custom Multi-Select relation field on the Twenty Task since Twenty does not have native attendee tracking.

Function 365

Annotation (Note / Attachment)

maps to

Twenty CRM

Note

1:1
Fully supported

Function 365 Annotation (notes and file attachments) maps to Twenty CRM Notes attached to People, Companies, or Opportunities by the RegardingObjectId reference. The note text maps to Note.body; the createdon timestamp and notetext (if a string field) maps to a custom datetime field. File attachments are downloaded from Dynamics SharePoint/Note locations and re-uploaded to Twenty's file storage.

Function 365

Custom Entity (Dynamics custom table)

maps to

Twenty CRM

Custom Object

1:1
Fully supported

Function 365 custom entities (tables created via Power Apps maker or Xrm tooling) map to Twenty CRM Custom Objects. We create the matching Twenty custom object in Settings → Data Model before import, then map each custom entity attribute to the corresponding Twenty field. Custom entity relationships (many-to-many via intersect tables) map to Twenty relation fields.

Function 365

Connection (AccountCustomerContacts / ContactCustomerAccounts)

maps to

Twenty CRM

People → Company relation

1:1
Fully supported

Function 365's N:N contact-to-account relationship via Connection or the inverse CustomerAddress role is flattened into Twenty's People.company relation (a single relation per person). The most recently modified or primary company per contact is designated as the Twenty relation; any additional companies are stored in a custom Multi-Relation field.

Function 365

SalesOrder / Invoice

maps to

Twenty CRM

Custom Object

1:1
Fully supported

Function 365 SalesOrder and Invoice entities have no direct Twenty CRM equivalent. We preserve them as a custom object (e.g., Orders) with fields for order number, amount, status, date, and related Opportunity. Order line items are stored as a custom relation sub-object. This gives you a reference record even though Twenty does not natively support order management.

Function 365

SystemUser

maps to

Twenty CRM

WorkspaceMember

1:1
Fully supported

Function 365 SystemUser records map to Twenty Workspace Members. Resolution is by email address: each SystemUser.internalemailaddress is matched to a Twenty user account. Unmatched SystemUser records are flagged before migration so your team can either invite them to Twenty first or assign their records to a fallback user. User business unit hierarchies do not translate to Twenty's flat permission model.

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.

Function 365 logo

Function 365 gotchas

High

AI-assisted notes are proprietary — verify clinical-record export coverage

High

NHS Number format must be preserved exactly

Medium

Implementation specialist time is paid extra at £55/session

Medium

GDPR consent timestamps are regulatory artefacts

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

  • Dynamics 365 workflows and Power Automate flows do not migrate to Twenty's workflow builder

    Function 365 (Dynamics 365) workflows execute server-side on entity events (create, update, assign, status change) and can include condition branches, field updates, child workflow calls, and CDS action steps. Twenty CRM's workflow builder handles basic field updates, task creation, and time-based triggers but does not natively execute a Dynamics-formatted flow file. Any Power Automate cloud flows or classic Workflow processes in Function 365 — including lead scoring flows, territory assignment workflows, or approval routing — must be documented and rebuilt manually in Twenty's workflow UI or exported as JSON for a developer to port. FlitStack delivers a workflow inventory export as part of every migration engagement.

  • Dynamics 365 custom fields must be pre-created in Twenty before CSV import runs

    Function 365 custom fields are stored as columns in the Dynamics database (new_* schema prefix) with option set values defined in the Dynamics solution. Twenty's CSV import creates records but not fields — the Data Model must be configured first. Dynamics option sets with 10+ values require Twenty Select fields with each option entered manually in Settings → Data Model. Dynamics picklist integer values (stored as integers in the export) must be mapped to Twenty's option labels during transformation. FlitStack generates a Twenty Data Model setup plan listing every custom field to create, its type, and its option values before data is loaded.

  • Import dependency order (Companies → People → Opportunities) is enforced by Twenty's relation model

    Twenty CRM requires that related records exist before their foreign key is populated. A People record cannot reference a Company via the companyId field if that Company has not yet been imported. The same applies to Opportunities referencing People and Companies. Function 365 exports all three object types simultaneously in most data export tools, but the migration must sequence them correctly. FlitStack runs the import in three phases: (1) Companies, (2) People, (3) Opportunities with People and Company IDs resolved from phase 1 and 2. Skipping this sequence produces orphaned Opportunities with no linked Company or People.

  • Dynamics 365 owner resolution by GUID has no direct equivalent in Twenty

    Function 365 stores record owners as OwnerId (a PartyList type referencing a SystemUser or Team GUID in the Dynamics database). OwnerId is a compound field that cannot be directly written to Twenty's assignedTo field (which expects a WorkspaceMember ID). FlitStack resolves each OwnerId by querying the SystemUser record's internalemailaddress and matching it to a Twenty Workspace Member's email. If a SystemUser has no corresponding Twenty account (e.g., the user left before migration), their records are flagged and assigned to a fallback owner you designate. Teams must invite all active Dynamics users to Twenty before the migration runs.

  • Twenty's API rate limits cap at 50–100 calls/minute on cloud plans — bulk CSV import is the primary path for large datasets

    Function 365 exports frequently produce datasets of 50,000+ records across Accounts, Contacts, Leads, Opportunities, and Tasks. Twenty's REST and GraphQL API limits (50/min on Pro, 100/min on Organization) make API-based migration too slow for large datasets. The standard approach is CSV bulk import via Twenty's native Import records feature, which handles up to 20,000 records per file with validation. FlitStack prepares multiple CSV files in the correct import order, pre-validates field types, and runs the import in batches to stay within Twenty's error-handling window.

Migration approach

Six steps for a successful Function 365 to Twenty CRM data migration

  1. Audit and export Function 365 data via Dynamics Web API or KingswaySoft CSV export

    FlitStack connects to your Function 365 instance via the Dynamics 365 Web API (OAuth 2.0 with an app registration) or uses KingswaySoft SSIS to extract Account, Contact, Lead, Opportunity, Task, Annotation, and custom entity records. We run a pre-export data quality scan identifying duplicate records, missing required fields (e.g., Contact with no email or lastname), and inactive users whose records should be filtered. The output is a structured CSV file per entity with GUIDs preserved as source_system_id for traceability.

  2. Build Twenty CRM Data Model — create custom fields and objects before import

    We generate a Data Model setup plan listing every custom field from Function 365 that has no direct Twenty equivalent, the field type to create (Select, Multi-Select, Number, Date, Relation), and option values for pick-list fields. Your Twenty admin (or FlitStack on your behalf) creates these fields in Settings → Data Model before data is imported. Custom objects for Function 365 entities with no standard Twenty equivalent (e.g., Orders, Contracts) are created at this stage. Workspace Members matching your Dynamics SystemUsers are invited and confirmed active before step 3.

  3. Transform and sequence the CSV files for dependency-order import

    FlitStack transforms every exported CSV: Dynamics field names mapped to Twenty field names, Dynamics option set integer values mapped to Twenty Select labels, Dynamics OwnerId GUIDs resolved to Twenty Workspace Member IDs by email match, and Dynamics date formats (UTC ISO 8601) accepted natively by Twenty. The files are sequenced: Companies first (dependency anchor), then People (referencing Companies by name or domain), then Opportunities (referencing Companies and People), then Notes and Tasks (referencing all parent objects). Any record with an unresolvable owner or missing required field is flagged in a separate exceptions file for your team to clean before the full run.

  4. Run a sample migration with field-level diff against a representative slice

    A representative slice (typically 200–500 records spanning all entity types, including a few with custom fields) is imported into Twenty first. FlitStack generates a field-level diff report comparing source Dynamics values against the imported Twenty values for every mapped field. You verify that pick-list mappings, owner resolution, company-people associations, and amount/currency fields all landed correctly. Any mapping errors are corrected in the transformation logic before the full run is scheduled. This sample is your last chance to adjust field mapping without touching production data.

  5. Execute full migration with delta-pickup window for in-flight records

    The full CSV import runs against your Twenty instance with FlitStack monitoring for duplicate-key errors, validation failures, and import rate throttling. Once the initial load completes, a delta-pickup window (typically 24–48 hours) captures any records created or modified in Function 365 during the cutover window. FlitStack produces an audit log of every operation (insert, update, skip) and a reconciliation summary comparing record counts by entity between the source and destination. One-click rollback is available if reconciliation identifies missing records or incorrect associations.

  6. Deliver workflow inventory export and post-migration support checklist

    FlitStack exports your Function 365 workflow definitions (Power Automate flow JSON, classic Workflow XAML) and business rule configurations as a structured reference document for your Twenty admin. The deliverable includes a rebuild priority list (critical workflows first, then nice-to-have), a screen-by-screen mapping of Dynamics entities to Twenty objects, and a post-migration validation checklist covering record counts, custom field completeness, owner resolution rate, and note attachment verification. We schedule a 30-day follow-up call to address any data issues discovered after users begin working in Twenty.

Platform deep dives

Context on both ends of the pair

Function 365 logo

Function 365

Source

Strengths

  • Integrated PMS (booking, notes, prescriptions, billing, lab orders, telehealth) in one product.
  • GDPR and HIPAA support built into the data model.
  • Transparent per-licence published pricing on the vendor shop.
  • AI-assisted clinical note generation reduces practitioner admin time.
  • Tiered licence pricing rewards larger practices with lower per-seat cost.

Weaknesses

  • Niche fit (UK private healthcare + Functional Medicine) — not suited for NHS-primary or non-UK general practice.
  • Implementation specialist time billed separately (£55/session) plus £350 onsite training.
  • Smaller installed base than Cliniko/Pabau means thinner integration ecosystem.
  • No public API documentation visible in research.
  • Solo Practitioner price (£132/month) higher than some freemium-style PMS competitors.
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 Function 365 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

    Function 365: Not publicly documented.

  • Data volume sensitivity

    B

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

Estimator

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

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

Can't find your answer?

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

Book a free 30 minute consultation

Most Function 365 to Twenty CRM migrations complete in 48–72 hours of clock time for datasets under 50,000 total records. Migrations between 50,000 and 500,000 records, or setups with more than 30 Dynamics custom fields, extend to 5–10 business days. The longest single step is pre-creating Twenty custom fields in the Data Model — this requires manual configuration before any CSV import can run, and cannot be shortened without sacrificing data completeness.

Adjacent paths

Related migrations to explore

Ready when you are

Move from Function 365.
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