
With Zendesk Sell approaching its end of life, many teams are now faced with the challenge of safely moving years of sales data, deals, and activities into a more scalable platform. Microsoft Dynamics 365 Sales, powered by the Microsoft Dataverse, offers a robust and extensible foundation for managing customer relationships, analytics, and automation. However, migrating from Zendesk Sell to Microsoft Dynamics 365 Sales isn’t a one-click process. It requires careful planning, precise data mapping, and a deep understanding of both platforms’ APIs. This guide walks you through a technical, step-by-step process of migrating from Zendesk Sell to Microsoft Dynamics 365 Sales to ensure a smooth, API-driven migration that preserves every entity, relationship, activity, and historical record.
Data Mapping
The following table outlines the essential mapping required for a complete migration, including solutions for non-direct mapping scenarios.
| Zendesk Sell Entities | Microsoft Dynamics 365 Sales Entities | Mapping 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. Note: Microsoft Dynamics 365 Leads must be associated with a Contact or Account. |
| Pipelines & Stages | Opportunity Pipeline (salespipelines or Custom Entity) & Status/Status Reason (Option Sets) | Workaround: Microsoft Dynamics 365 Pipeline management is extensive metadata. Recreate Pipelines using the Microsoft Dynamics 365 UI or configuration migration tools. Stages generally map to Option Set Values or Status/Status Reasons, matching Zendesk's likelihood and position. |
| 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). |
Auth
Authentication for both platforms must be set up to ensure secure and uninterrupted data transfer.
Zendesk Sell Authentication
Access to the Zendesk Sell Core API requires authentication via a valid Access Token.
- For migration scripts, it is typically easiest to generate a Single-User Access Token with an unlimited lifetime via the application dashboard, provided your user has administration management privileges.
- The token must grant sufficient scopes, primarily read and write access, to cover all necessary resources.
- All requests must include the token in the Authorization header: Authorization: Bearer $ACCESS_TOKEN.
Zendesk API URL Structure:
https://api.getbase.com/v2/{resource}
Microsoft Dynamics 365 Sales (Dataverse) Authentication
The Dataverse Web API uses OData v4 and relies on OAuth 2.0 via Microsoft Entra ID (formerly Azure Active Directory) for authentication.
- Application Registration: Register a client application in your Microsoft Entra ID tenant. This registration defines the necessary permissions.
- Permissions and Scopes: The application must be granted the "Access Common Data Service as organization users" delegated permission, typically referenced by the user_impersonation scope, to act on behalf of the user running the migration.
- Token Acquisition: The client application uses the Microsoft Authentication Library (MSAL) to acquire an access token. This token is then included in every Dataverse API request header:
- Authorization: Bearer <Access Token>
- OData-MaxVersion: 4.0
- OData-Version: 4.0
- Accept: application/json
Dataverse Web API URL Structure: [Organization URI]/api/data/v9.2/{entitySetName}
The Migration Process
The migration must be executed in phases based on dependency constraints: migrate foundational data (Users, Pipelines, Custom Fields) first, followed by core entities (Accounts/Contacts, Leads, Products), and finally transactional records and activities (Deals, Orders, Tasks, Notes). Use batch processing where feasible in Dataverse to manage performance and respect API limits.
Step 1: Migrate Users (Owners)
Start by establishing the user mapping, as correct ownership is crucial for setting up records in Dataverse.
- Fetch Zendesk Users: Retrieve all users and their metadata.
- Zendesk Endpoint:
GET /v2/users
- Zendesk Endpoint:
- Map IDs: Match the Zendesk id to the corresponding Microsoft Dynamics 365 systemuserid or Entra ID Object ID. This map will populate the _owninguser_value field on records in subsequent steps.
Step 2: Migrate Pipelines and Stages
These are structural elements that must exist before importing Deals.
- Fetch Zendesk Pipelines & Stages:
- Zendesk Endpoints:
- Pipelines:
GET /v2/pipelines - Stages:
GET /v2/stages?pipeline_id={id}
- Pipelines:
- Create Microsoft Dynamics 365 Pipelines (Configuration): Manually or programmatically create corresponding Pipelines and Stages in Dynamics 365 Sales. Pipelines typically map to Business Process Flows or Sales Stages in Microsoft Dynamics 365 metadata.
- Map Stage Metadata: Capture the numerical ID assigned to each stage in Microsoft Dynamics 365. The Zendesk likelihood (percentage probability) should be mapped to the Microsoft Dynamics 365 Opportunity statuscode or a custom numeric field on the Opportunity entity.
Step 3: Create Custom Fields (Columns)
Custom fields must be recreated as columns in Dataverse before data import.
- Fetch Zendesk Custom Field Definitions:
- Zendesk Endpoint:
GET /v2/:resource_type/custom_fields (for lead, contact, deal)
- Zendesk Endpoint:
- 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).
- Dataverse Endpoint (Example: Create Decimal Field):
POST [URI]/EntityDefinitions(LogicalName='account')/Attributes
- Dataverse Endpoint (Example: Create Decimal Field):
Step 4: Migrate Accounts and Contacts
This critical step separates Zendesk's unified Contact object into Microsoft Dynamics 365's distinct Account and Contact records.
- Migrate Accounts (Zendesk Contacts with is_organization: true):
- Zendesk Endpoint:
GET /v2/contacts?is_organization=true - Dataverse Endpoint (Create Account): Use
POST [URI]/accounts. Store all Zendesk IDs and the resulting Microsoft Dynamics 365 Account IDs for later association.
- Zendesk Endpoint:
- Migrate Contacts (Zendesk Contacts with is_organization: false):
- Zendesk Endpoint:
GET /v2/contacts?is_organization=false - Dataverse Endpoint (Create Contact): Use
POST [URI]/contacts
- Zendesk Endpoint:
- Establish Contact to Account Relationship: For contacts linked to an organization in Zendesk (contact_id field on the contact), establish the parent relationship in Microsoft Dynamics 365 using the primary contact field.
Dataverse Endpoint (Create Contact with Association): Perform a POST to /contacts using the @odata.bind annotation on the single-valued navigation property:
{
"firstname": "Mark",
"lastname": "Johnson",
"parentcustomerid_account@odata.bind": "accounts(<Microsoft Dynamics 365_ACCOUNT_GUID>)"
}
Step 5: Migrate Leads
Leads map directly to the Microsoft Dynamics 365 Lead entity set.
- Fetch Zendesk Leads:
- Zendesk Endpoint:
GET /v2/leads
- Zendesk Endpoint:
- Create Microsoft Dynamics 365 Leads:
- Dataverse Endpoint: Use
POST [URI]/leads. Link the lead owner using the mapped owninguser GUID, and map the status.
- Dataverse Endpoint: Use
Step 6: Migrate Products, Orders, and Line Items
Migrate the product catalog before migrating orders, which depend on them.
- Migrate Products:
- Zendesk Endpoint:
GET /v2/products - Dataverse Endpoint (Create Product): Use
POST [URI]/products.
- Zendesk Endpoint:
- Migrate Orders:
- Zendesk Endpoint (Retrieve Orders associated with a Deal):
GET /v2/orders?deal_id={dealId} - Dataverse Endpoint (Create Sales Order): Use
POST [URI]/salesorders. Link the new Sales Order to the corresponding Microsoft Dynamics 365 Opportunity (Deal) ID via @odata.bind.
- Zendesk Endpoint (Retrieve Orders associated with a Deal):
- Migrate Line Items (Order Products):
- Zendesk Endpoint (Retrieve Line Items):
GET /v2/orders/:order_id/line_items - Dataverse Endpoint (Create Order Product): Use
POST [URI]/salesorderdetails. This record links the Microsoft Dynamics 365 Product ID and specifies quantity/price, associating it back to the newly created Sales Order ID.
- Zendesk Endpoint (Retrieve Line Items):
Step 7: Migrate Deals (Opportunities)
Deals tie together many previously migrated entities (Owner, Account, Contacts, Products).
- Fetch Zendesk Deals:
- Zendesk Endpoint:
GET /v2/deals
- Zendesk Endpoint:
- Create Microsoft Dynamics 365 Opportunities:
- Dataverse Endpoint (Create Opportunity): Use
POST [URI]/opportunities.Map the primary associated Account/Contact IDs using the @odata.bind annotation.
- Dataverse Endpoint (Create Opportunity): Use
- Handle Loss/Unqualified Reasons: Retrieve the text reasons from Zendesk (e.g.,
GET /v2/loss_reasons/:id) and map them to the Opportunity's statuscode or a dedicated custom field in Microsoft Dynamics 365.
Step 8: Migrate Activities (Tasks, Calls, Notes)
Activities often relate to Leads, Contacts, or Deals (Opportunities). In Dataverse, all these map to the polymorphic regardingobjectid lookup.
- Tasks:
Zendesk Endpoint:GET /v2/tasks - Dataverse Endpoint (Create Task): Use POST [URI]/tasks. Link the Task to the parent record (Lead, Contact, or Opportunity) using the appropriate navigation property and @odata.bind:
{ "subject": "Contact Tom", "regardingobjectid_contact_task@odata.bind": "contacts(<Microsoft Dynamics 365_CONTACT_GUID>)"} - Calls:
- Zendesk Endpoint:
GET /v2/calls - Dataverse Endpoint (Create Phone Call): Use POST [URI]/phonecalls. Map fields like summary, duration, and call time (made_at field in Zendesk maps to Dataverse activity time field).
- Zendesk Endpoint:
- Notes:
- Zendesk Endpoint:
GET /v2/notes - Dataverse Endpoint (Create Annotation/Note): Use POST [URI]/annotations. Map the note content and link using the appropriate regardingobjectid_ lookup.
- Zendesk Endpoint:
Step 9: Migrate Documents and Files
- Fetch Zendesk Documents: Retrieve the metadata for documents attached to resources.
- Zendesk Endpoint:
GET /v2/documents?resource_type={type}&resource_id={id}
- Zendesk Endpoint:
- Download Files: Use the temporary download_url returned by Zendesk to fetch the file content.
- Upload to Dataverse: Create Annotation records (annotations) in Microsoft Dynamics 365, setting the file content in the documentbody field and associating the annotation back to the parent record (Account, Contact, Opportunity) using the regardingobjectid.
Post Migration and Best Practices
Completing the technical transfer is only half the battle. Thorough validation and a solid plan for cutover are necessary for a seamless transition.
Data Validation and Reconciliation
- 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 and that the required security roles and access privileges are intact.
Handling Deltas and Cutover
For a zero-downtime cutover, capture and migrate changes that occurred during the bulk migration phase:
- 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.
- Upsert in Microsoft Dynamics 365: Use the Dataverse PATCH method with alternate keys or the Upsert pattern to update existing records or create new ones identified in the delta sync. The Upsert functionality checks if the record exists based on keys; if so, it updates (PATCH); otherwise, it creates (POST).
Managing API Rate Limits
Migrating large datasets requires respecting API limits imposed by both platforms:
- Zendesk Rate Limits: Zendesk Sell API limits are typically high (up to 36,000 requests per hour, or 10 requests per second per token). Monitor these closely.
- Dataverse Limits: While Dataverse typically handles requests efficiently, queries returning aggregate values are limited to 50,000 records, and standard API limits apply.
- 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.
- Dataverse Endpoint (Batch Processing): POST [URI]/api/data/v9.2/$batch
- 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.
Migrating from Zendesk Sell to Microsoft Dynamics 365 Sales isn’t just a data transfer. It’s a full model transformation across two fundamentally different platforms. ClonePartner has completed dozens of Zendesk Sell to Dynamics 365 Sales migrations with precision. We handle complex mappings between Contacts, Accounts, and Opportunities, rebuild your pipelines inside Dataverse, migrate custom fields and relationships, and even preserve activities, notes, and files. Our team executes a zero-downtime migration with delta syncs so your sales operations continue without interruption.
"ClonePartner has empowered our sales team to target our competition, and they are excited! We’ve also freed up engineering resources, allowing us to focus more on our product while ClonePartner handles migrations for us."