There is no native migration path from Zendesk Sell to Microsoft Dynamics 365 Sales, requiring a fully custom, API-driven approach to transfer data between the two platforms. The fundamental data model differences are significant: Zendesk Sell uses a unified Contact object that must be split into separate Account and Contact entities in Dynamics 365, pipelines and stages must be recreated as Business Process Flows or Option Sets, and activities leverage polymorphic lookups in Dataverse rather than simple parent references. Custom work is required for OAuth 2.0 authentication setup via Microsoft Entra ID, recreating custom field definitions through the Dataverse Metadata API, re-encoding document files from expiring Zendesk download URLs, and executing a carefully sequenced, dependency-ordered migration across multiple phases.
Read this first
Pair-specific gotchas that catch teams out. Each one has cost somebody a weekend.
API versions and deprecation timelines
This guide references Dataverse Web API v9.2 and the Zendesk Sell Core API (api.getbase.com/v2). Both APIs evolve; verify against the official Zendesk Sell API reference and Dataverse Web API documentation before implementing. Zendesk Sell is approaching end of life — confirm your data export window directly with Zendesk support.
Business Process Flow activation
Creating a Business Process Flow definition in Dynamics 365 is not sufficient. Each migrated Opportunity record requires the BPF to be explicitly activated at the record level. If you skip this step, records will appear in the pipeline UI without a valid BPF stage, causing display and automation failures. Activate BPFs per record programmatically after Opportunities are created in Step 7.
Dataverse solution layers
Custom columns created outside a Dataverse solution are unmanaged. Unmanaged customizations are ungoverned and cannot be cleanly deployed, versioned, or rolled back. Create all custom columns inside a dedicated solution before running the migration. This also makes cleanup straightforward if the migration needs to be re-run.
Ambiguous Contact records
A Zendesk Contact can have is_organization=true while also being the parent of person contacts. When you encounter a Contact record that is both an organization and directly linked to person contacts, migrate it as an Account first, then resolve the person contacts against that Account GUID. Do not attempt to create the person Contact records before the parent Account exists — the @odata.bind will fail with a missing GUID error.
Timezone normalization
Zendesk Sell stores timestamps in UTC. Dataverse time handling depends on your organization's timezone settings and the column type (UserLocal vs. TimeZoneIndependent). If your Dataverse org is configured for UserLocal, timestamps written as UTC may be shifted on read. Before bulk-importing activity timestamps (made_at, created_at, updated_at), confirm your Dataverse org timezone setting and normalize all source timestamps accordingly. Silent timestamp drift is one of the harder data quality issues to detect post-migration.
The runbook
Work top to bottom. Tick steps as you go — your progress is saved in this browser.
01 Discovery Agree scope, pipeline design and success criteria before touching data.
Objective A scope covering every object in the revenue model, with sales leadership signed up to the pipeline design.
Keep these open
-
Inventory every object in Zendesk Sell
Count accounts, contacts, leads, deals or opportunities, activities, notes, emails, files, products and quotes. Activity and email history is usually the largest object by an order of magnitude and the one people forget to size.
Data Profiler Get real record counts instead of estimating from memory -
Map the current pipeline and agree the target model
Document every pipeline, stage, probability and required field, then agree the Dynamics 365 Sales model with sales leadership. Migrating a broken pipeline faithfully is a wasted opportunity; redesigning it mid-migration is a wasted migration.
Stage changes alter historical conversion rates. Agree how you will restate reporting before you change the model.
-
Catalogue integrations and automation
List every system touching Zendesk Sell: marketing automation, quoting, billing, ERP, data enrichment, dialler, BI. Each is a separate cutover task with its own owner, and each can silently write bad data into your new CRM.
-
Capture the reporting that must survive
Interview the people who actually live in the dashboards — forecast, pipeline coverage, rep activity, cohort conversion. Every one of these needs the underlying fields to migrate, which frequently expands the scope.
-
Build the business case and pick the go-live date
Model licence delta, implementation effort and productivity dip. Then choose the date deliberately: mid-quarter is disruptive, quarter-end is worse. Most teams land on the first week of a new quarter.
COI & ROI Calculator Build the 36-month business case you will need for sign-off
Don't move on until
- Object counts confirmed for accounts, contacts, deals and activities
- Target pipeline and stage model agreed with sales leadership
- Reporting requirements captured from the people who use the reports
02 Data Audit CRM data is usually dirtier than anyone expects. Find out how dirty.
Objective A profiled export with duplicates, ownership gaps and relationship integrity all quantified and triaged.
Keep these open
-
Export and profile every object
Profile null rates, distinct values and type consistency across all objects. Pay attention to fields sales reps were supposed to fill in: an 80% null rate on a field your forecast depends on is a business finding, not a data one.
Data Profiler Profile the Zendesk Sell export for nulls, outliers and type drift -
Quantify duplicates and agree the merge policy
Measure duplicate accounts (same company, different spellings and suffixes) and contacts (same email, or same person at a renamed company). Agree survivorship rules before merging: which record wins, which fields, and what happens to the activity history on the loser.
Merging before you have agreed survivorship rules destroys history irreversibly. Decide first, merge second.
Data Cleaner Strip empty rows, stray whitespace and dead columns -
Verify relationship integrity
Check every deal has a valid account, every contact a valid account, every activity a valid parent. Referential breaks are the defect that turns a clean-looking load into a CRM where the pipeline report does not tie to the account list.
-
Resolve ownership and the user map
Build the Zendesk Sell user → Dynamics 365 Sales user map, including leavers. Every record needs a valid owner: records assigned to deactivated users either fail to load or land unassigned, and unassigned pipeline is pipeline nobody works.
Records owned by deactivated users are a top cause of load failures. Decide the reassignment target before you start.
-
Scan for PII and regional compliance
CRMs hold marketing consent, lawful basis and unsubscribe state. Confirm these fields migrate intact and that consent provenance survives — losing consent records is a regulatory problem, not a data-quality one.
PII & Compliance Scanner Find regulated fields before they land in a new system -
Clean, normalise and archive the pre-state
Normalise country and state values, phone formats, currencies and casing, and standardise all timestamps to UTC. Keep an untouched copy of the raw export — it is your only reference if a mapping decision turns out wrong.
-
Batch Operations
Use the Dataverse $batch endpoint for executing up to 1,000 individual operations (like creating activities or updating records) within a single HTTP request, significantly improving efficiency and managing connection overhead.
Zendesk Sell → Dynamics 365 Sales specifics
- Zendesk Rate Limits
- Zendesk Sell API limits are typically high (up to 36,000 requests per hour, or 10 requests per second per token). Verify current limits against the Zendesk Sell API rate limit documentation before running bulk operations, as these figures can change.
- Dataverse Limits
- While Dataverse typically handles requests efficiently, standard API service protection limits apply. Note that aggregate query operations using $count are subject to a 50,000 record cap — use these only for validation spot checks, not as the primary mechanism for driving pagination or migration logic.
- Throttling Implementation
- If you encounter HTTP 429 Too Many Requests errors from either API, implement logic to pause the processing immediately, respecting any Retry-After header provided in the response before attempting a retry. This ensures that your migration script does not trigger service protection limits.
Don't move on until
- Duplicate rate quantified per object with a merge policy agreed
- Every record has a valid owner mapped to a target user
- Relationship integrity verified — no orphaned deals or contacts
03 Field Mapping Map objects, relationships and picklists — in that order.
Objective A signed mapping spec that covers objects, relationships, picklist values and currency handling.
Keep these open
-
Map objects and their relationships first
Establish how Zendesk Sell objects correspond to Dynamics 365 Sales objects before any field work. Leads-versus-contacts, accounts-versus-organisations and deal hierarchies differ structurally between CRMs, and a field map built on the wrong object model has to be redone.
Schema Mapper Opens pre-loaded with the Zendesk Sell → Dynamics 365 Sales field pair -
Generate and then hand-review the field map
Auto-match both schemas, then review every row. Look hardest at anything named "type", "source", "stage" or "status", where names match and meanings do not.
-
Map every picklist value, including retired ones
Enumerate all values on both sides — deal stages, lead sources, industries, statuses — and map each explicitly, including values no longer offered but still present on old records. Unmapped values either fail the row or land as blanks that quietly break segmentation.
Historical records often carry picklist values that were retired years ago and no longer appear in the UI.
-
Decide currency, amount and date handling
If you sell in multiple currencies, confirm how Dynamics 365 Sales stores amounts, exchange rates and dated conversion rates. Recalculating historical deal values at today's rate silently rewrites your revenue history.
Multi-currency deals re-converted at current rates will not tie to your historical reporting or your finance system.
-
Determine the load order
Sequence the load so parents exist before children: users, then accounts, then contacts, then deals, then activities and notes. Keep source IDs in custom fields so relationships can be rebuilt by lookup rather than guesswork.
-
Freeze the spec and sign off
Version-control the mapping, walk RevOps and sales leadership through the decisions that change reporting, and get written sign-off before the pilot.
-
Create Microsoft Dynamics 365 Custom Columns
Use the Metadata API to create matching columns (e.g., String, Decimal, Choice) on the corresponding Microsoft Dynamics 365 tables (account, contact, lead, opportunity).
Zendesk Sell → Dynamics 365 Sales specifics
- Zendesk Endpoint
- GET /v2/:resource_type/custom_fields (for lead, contact, deal)
- Dataverse Endpoint (Example: Create Decimal Field)
- POST [URI]/EntityDefinitions(LogicalName='account')/Attributes
Don't move on until
- Object and relationship model mapped and reviewed
- Every picklist value explicitly mapped, including dead values
- Load order determined so parents always exist before children
04 Test Migration Pilot with related records, not a flat sample.
Objective A pilot load whose relationships, ownership and roll-up reporting all verify against source.
Keep these open
-
Configure the Dynamics 365 Sales sandbox to match the agreed model
Build the pipelines, stages, custom fields, record types and user roles first. Load into a default configuration and you will only discover the configuration defects at full scale.
-
Select a connected pilot slice
Take complete account trees — 50-100 accounts with all their contacts, deals, activities and files — rather than a flat random sample. Relationship defects are the whole point of a CRM pilot and a flat sample cannot expose them.
-
Run the load in dependency order with full logging
Load users, accounts, contacts, deals, then activities, logging every request against its source ID. Note which failures are transient (rate limits, timeouts) and which are structural (validation, missing parent).
-
Verify relationships and roll-ups
Confirm every deal sits on the right account with the right owner and stage, and that pipeline totals per account and per rep match Zendesk Sell exactly. Roll-up mismatches almost always mean a relationship or currency defect upstream.
Migration Validation Tool Diff the pilot batch against source before scaling up -
Measure throughput and project the full load
Record actual records-per-hour under Dynamics 365 Sales's API limits and extrapolate, remembering activity history usually dominates volume. If the projection exceeds your window, split the load or trim activity scope now.
-
Let reps work the pilot data
Put two or three reps in the sandbox against their own accounts. They spot missing context, wrong owners and unusable notes far faster than any reconciliation script, and their buy-in is what makes adoption work.
Don't move on until
- Relationships intact across the whole pilot slice
- Pipeline roll-ups match source for the pilot accounts
- Reps have worked pilot records and confirmed they are usable
05 Cutover Switch the revenue system with the pipeline intact.
Objective All in-scope CRM data live in Dynamics 365 Sales, integrations repointed, and reps selling on day one.
Keep these open
-
Pre-load history ahead of the freeze
Load closed deals, historical activities and inactive accounts while Zendesk Sell stays live. Only open pipeline and the final delta need to move inside the window.
-
Publish the cutover runbook
A timed sequence with owners and abort criteria: freeze, final export, delta load, integration switch, smoke test, go/no-go, rep enablement. Pick a window that avoids quarter-end and month-end close.
-
Freeze Zendesk Sell and take the final delta
Set Zendesk Sell read-only and export everything changed since the pre-load. Reps updating deals during an unenforced freeze is the classic way to lose the most recent — and most valuable — pipeline changes.
A rep who updates a deal in the old CRM during the freeze will lose that update permanently.
-
Load the delta and reconcile the pipeline
Run the delta, then verify open pipeline value and count per rep against Zendesk Sell before anything else. If the pipeline number is wrong on day one, reps stop trusting the system and never fully come back.
Migration Validation Tool Confirm the final delta landed before you reopen -
Repoint every integration and verify with real records
Switch marketing automation, quoting, billing, enrichment, dialler and BI connections, then push a live record through each path. An unrepointed integration writing into the old CRM causes divergence that gets harder to reconcile every day.
Two-way integrations left pointing at the old CRM will keep writing there. Disable them before you switch, not after.
Cron Expression Builder Schedule the delta syncs that run through the freeze -
Go/no-go, then enable the reps
Call the decision explicitly against the exit criteria. Run enablement on real data with their own accounts on screen, and keep Zendesk Sell read-only rather than cancelled until validation closes.
Zendesk Sell → Dynamics 365 Sales specifics
- Alternate keys
- Define an alternate key on each Dataverse table using the Zendesk source ID (e.g., a custom string column new_zendeskid). This allows you to use the Upsert pattern — PATCH [URI]/accounts(new_zendeskid='ZD-12345') — which creates the record if it does not exist and updates it if it does. This makes every step idempotent.
- Checkpoint logging
- After each successful batch, write the last-processed Zendesk record ID to a log. On re-run, skip records below that checkpoint. This prevents duplication without requiring a full re-query of Dataverse.
- Rollback scope
- If a step fails mid-run (e.g., Step 7 Opportunities partially created), do not manually delete partially created records. Instead, re-run from the checkpoint using upsert. Manual deletes risk leaving orphaned activity records that reference GUIDs you just removed.
- Initial Snapshot Timestamp
- Record the timestamp (last_updated from Zendesk) just before the bulk migration began.
- Delta Synchronization
- After the bulk migration, perform a final fetch from Zendesk using the updated_at filter to retrieve only recently modified or new records.
Don't move on until
- Historical load complete and reconciled before the freeze
- All integrations repointed and verified end to end
- Reps logged in with pipeline visible and correct
06 Validation Prove the revenue numbers tie out, then close the project.
Objective Reconciled data, forecast parity with pre-migration reporting, and signed acceptance.
Keep these open
-
Reconcile every object
Compare counts and values across accounts, contacts, deals, activities and files, plus field-level spot checks on a random sample. Produce a single report suitable for audit and for finance.
Migration Validation Tool Reconcile Zendesk Sell and Dynamics 365 Sales record-for-record -
Tie the pipeline and forecast to baseline
Rebuild forecast, pipeline coverage and win-rate reporting and compare to pre-migration figures. Every variance needs an explanation — most trace back to stage or currency mapping decisions made in Field Mapping.
Stage-model changes will legitimately shift historical conversion rates. Restate the baseline rather than chasing the difference as a defect.
-
Verify ownership, visibility and permissions
Confirm every record has the right owner and that sharing rules, territories and role hierarchy give each rep exactly the visibility intended. Over-broad visibility in a CRM is a data-protection issue as much as a config one.
-
Re-profile for field completeness
Compare loaded null rates against the source profile per field. A field that arrived empty while counts matched is the failure counts alone cannot detect.
Data Profiler Prove field completeness held up through the load -
Test automation and integration write-back
Fire every workflow, assignment rule, sequence and notification, and confirm each integration writes correctly in both directions. Automation is rebuilt rather than migrated, so it is unproven until observed.
-
Sign off and schedule decommission
Get written acceptance against the Discovery criteria, keep Zendesk Sell read-only for 30-90 days with a final archive export taken, then diarise cancellation so the contract does not auto-renew.
Zendesk Sell → Dynamics 365 Sales specifics
- Count Verification
- Confirm record counts for core entities (Accounts, Contacts, Leads, Opportunities) match the counts retrieved from Zendesk Sell to ensure no loss occurred.
- Spot Checks
- Manually verify complex records (e.g., an Opportunity linked to an Account, multiple Contacts, and containing activities) to ensure all relationships were correctly established using the new Microsoft Dynamics 365 GUIDs.
- Owner and Role Verification
- Confirm that ownership fields (owninguser) are correctly populated. Also verify that migrated users have been assigned the appropriate Dynamics 365 security roles. Record ownership does not automatically grant the owning user access — security role assignment is a separate step performed in Settings > Security > Users.
Don't move on until
- Full reconciliation report complete across all objects
- Forecast and pipeline reports tie to pre-migration baselines
- Acceptance signed and Zendesk Sell decommission scheduled
Field mapping reference
The field-by-field mapping for each object. Use this as the starting point for your mapping spec.
Entities Microsoft Dynamics 365 Sales Entities
| Zendesk Sell field | Dynamics 365 Sales field | Notes |
|---|---|---|
| User | User (systemusers) | Retrieve Zendesk User IDs via GET /v2/users. These IDs must be mapped to existing Microsoft Dynamics 365 User IDs (systemuserid or Microsoft Entra ID Object ID) for setting ownership and managing access. |
| Contact (is_organization: true) (Organization) | Account (accounts) | Retrieve filtered by is_organization=true. Map Zendesk name and address details to the Account entity. |
| Contact (is_organization: false) (Person) | Contact (contacts) | Retrieve filtered by is_organization=false. Map individual fields (first_name, last_name, email). Link to parent Account using the parentcustomerid_account lookup column. |
| Lead | Lead (leads) | Direct entity mapping. Use POST [URI]/leads. Leads are pre-qualification records and exist independently in Dynamics 365 — association to a Contact or Account happens at the point of qualification, not creation. |
| Pipelines & Stages | Business Process Flow (BPF) or Status Reason (Option Sets) | See the Pipeline Mapping Decision Rule below for when to use each approach. |
| Deal | Opportunity (opportunities) | Map primary fields like name, value, currency, and estimated_close_date. Link to the correct Account/Contact IDs using @odata.bind. |
| Product | Product (products) | Map core product details (name, sku, description). Zendesk's prices array requires mapping to Microsoft Dynamics 365 Price Lists/Price List Items, which is complex metadata setup. |
| Order and Line Item | Sales Order (salesorders) and Order Product (salesorderdetails) | Workaround: Zendesk explicitly uses separate Order and Line Item objects attached to a Deal. In Microsoft Dynamics 365 Sales, migrate these as Sales Orders and associate Line Items (Order Products) to the newly created Sales Order. |
| Task | Task (tasks) | Tasks are an Activity entity. Map using polymorphic lookups (e.g., regardingobjectid_lead_task, regardingobjectid_contact_task, regardingobjectid_account_task) to link to the correct parent record. |
| Call | Phone Call (phonecalls) | Calls are also an Activity entity. Map properties like summary, duration, phone_number, and made_at. Map Zendesk Call Outcomes (/v2/call_outcomes) to an appropriate custom Option Set in Microsoft Dynamics 365. |
| Notes | Annotation (annotations or notes) | Notes are saved as Annotation records in Dataverse. Map content and associate via the polymorphic lookup to the parent record (lead, contact, or deal). |
| Document | Annotation (annotations) or SharePoint/OneDrive integration | Zendesk provides expiring download_url links. Files must be downloaded and then uploaded to Microsoft Dynamics 365. Use Annotation records with the file content encoded if migrating files directly, or leverage built-in Microsoft Dynamics 365 Document Management integrations (SharePoint/OneDrive). |
| Custom Fields | Custom Properties (Columns) | Fetch custom field definitions (GET /v2/:resource_type/custom_fields). Recreate matching columns in Dataverse using metadata APIs, maintaining type consistency where possible (e.g., Zendesk number maps to Microsoft Dynamics 365 Decimal or Integer columns). |
Risk matrix
Per-object risk for this pair. Plan extra validation around anything marked high.
| Object | Risk | Notes |
|---|---|---|
| Users | medium | Users cannot be created via API in Dynamics 365 and must be pre-provisioned in Microsoft Entra ID, requiring manual ID mapping between Zendesk Sell user IDs and existing systemuserids. |
| Accounts (Organizations) | medium | Zendesk Sell organizations must be filtered from the unified Contact object and field-mapped to the Account entity, with all generated GUIDs stored for downstream contact and deal association. |
| Contacts (Persons) | medium | Individual contacts must be separated from organizations, mapped to the Contact entity, and linked to parent Accounts using parentcustomerid_account lookups, adding relational complexity. |
| Leads | low | Leads map directly between platforms with straightforward field correspondence, though owner assignment and status mapping require the pre-established user and custom field mappings. |
| Pipelines and Stages | high | There is no direct API import path for pipeline structures; they must be recreated through Dynamics 365 configuration tools or metadata APIs, with stage IDs and probability values carefully mapped to Option Sets or Business Process Flows. |
| Deals (Opportunities) | high | Deals depend on nearly every other migrated entity—Accounts, Contacts, Users, Pipelines, and Products—making them highly sensitive to any upstream mapping errors or missing references. |
| Products and Price Lists | high | Zendesk Sell's prices array must be decomposed into Dynamics 365 Price Lists and Price List Items, which involves complex metadata setup beyond simple product record creation. |
| Orders and Line Items | medium | Zendesk Sell's separate Order and Line Item objects must be migrated as Sales Orders and Order Products in Dynamics 365, requiring correct association to both Opportunities and Products. |
| Tasks and Calls (Activities) | medium | Activities require polymorphic regardingobjectid lookups with entity-type-specific navigation properties, and call outcomes must be mapped to custom Option Sets that don't exist natively in Dynamics 365. |
| Documents | high | Zendesk Sell provides only temporary expiring download URLs, requiring files to be downloaded during migration and re-uploaded as Base64-encoded Annotations or routed to SharePoint, with risk of data loss if URLs expire mid-migration. |
| Custom Fields | medium | Custom field definitions must be fetched per resource type, recreated as Dataverse columns with appropriate type mappings via the Metadata API, and validated before any data records referencing them can be imported. |
The hard parts
What makes this specific migration difficult, beyond the mechanics.
Contact-to-Account/Contact Separation
Zendesk Sell's unified Contact object must be filtered by is_organization and split into distinct Account and Contact records in Dynamics 365, with parent relationships re-established using @odata.bind annotations.
Pipeline and Stage Recreation
Zendesk Sell pipelines and stages have no direct API-importable equivalent in Dynamics 365 and must be manually or programmatically recreated as Business Process Flows, Option Sets, or Status Reasons before deal migration can begin.
Polymorphic Activity Lookups
Tasks, calls, and notes in Dataverse use polymorphic regardingobjectid lookups that require entity-type-specific navigation properties, making activity association more complex than Zendesk Sell's simple resource_type/resource_id references.
Document and File Migration
Zendesk Sell provides only expiring download URLs for documents, requiring files to be downloaded in transit and re-uploaded as Base64-encoded Annotation records or routed to SharePoint/OneDrive integrations in Dynamics 365.
Custom Field Type Consistency
Custom field definitions must be fetched from Zendesk Sell per resource type and recreated as matching Dataverse columns via the Metadata API, with careful attention to type mapping between Zendesk field types and Dynamics 365 column types.
Dependency-Ordered Execution Sequencing
The migration must follow strict entity dependency ordering—Users, Pipelines, and Custom Fields first, then Accounts/Contacts, Leads, and Products, and finally Deals, Orders, and Activities—to ensure all lookup references resolve correctly.
Tools used in this playbook
All free, all run entirely in your browser — nothing is uploaded.