An In-Depth Guide to Migrating from Zendesk Sell to HubSpot
This guide provides an in-depth approach for migrating data from Zendesk Sell to HubSpot CRM. This process relies heavily on the RESTful APIs of both platforms, emphasizing object mapping, sequential migration, and robust handling of associated data and rate limits.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
Moving from Zendesk Sell to HubSpot is more than a simple export and import. Zendesk Sell organizes its data with key nuances you'll want to preserve. This guide walks through how to map each concept one-to-one, handle all the nuances, and execute a zero-downtime cutover with reconciliation and deltas.
This guide targets HubSpot CRM API v3 and the Zendesk Sell Core API v2. API behavior may change — always verify against the official documentation linked at the bottom.
Phase 1: Pre-Migration Planning, Mapping, and Setup
A successful migration requires understanding the inherent differences in how Zendesk Sell and HubSpot CRM structure their data, particularly around core objects and their relationships.
1.1 Data Model Comparison and Mapping
| Zendesk Sell Object (Source) | HubSpot CRM Object (Target) | Key Mapping Notes |
|---|---|---|
| Contact (is_organization: true) | Company | Map Zendesk name to HubSpot name and/or domain. Domain is the recommended primary unique identifier in HubSpot. |
| Contact (is_organization: false) | Contact | Map Zendesk first_name, last_name, and email to HubSpot contact properties. Email is the recommended primary unique identifier. |
| Lead | Lead | Requires association with an existing Contact (Person). Note: Zendesk and HubSpot conceptualize leads differently — in Zendesk Sell, a Lead is a standalone pre-qualified record, while in HubSpot, a Lead object must be associated with an existing Contact and represents a lifecycle qualification status. |
| Deal | Deal | Map pipeline, stages, and financial data. |
| Pipelines & Stages | Pipelines & Stages (e.g., for Deals) | Map likelihoods/probabilities for stages. |
| Product | Product | Requires name, price, hs_sku. |
| Order & Line Item | Order & Line Item | Zendesk Line Items link products to an Order, which is associated with a Deal. HubSpot Line Items are created and linked directly to the Deal or Order. |
| Call | Call (Activity) | Must be logged with an hs_timestamp. |
| Text Message | Communication (Activity) | Logged using hs_communication_channel_type (SMS, WHATS_APP, LINKEDIN_MESSAGE) and hs_timestamp. |
| Note | Note (Activity) | Requires hs_timestamp. |
| Task | Task (Activity) | Requires hs_timestamp. Must be associated with a Contact, Deal, or Lead. |
| Tags | Custom Property (multi-select) | Zendesk Sell supports tags on Contacts, Leads, and Deals. Create a multi-select custom property in HubSpot (e.g., zendesk_tags) and map tag values as options. |
| Custom Fields | Custom Properties | Must be created in HubSpot prior to data import. |
1.2 Field Type Mapping Reference
When recreating Zendesk Sell custom fields as HubSpot properties, use the following type mapping:
| Zendesk Sell Field Type | HubSpot Property Type | Notes |
|---|---|---|
| string | Single-line text | Direct mapping. |
| text | Multi-line text | Direct mapping. |
| number | Number | Direct mapping. |
| bool | Single checkbox | Direct mapping. |
| list | Dropdown select | Recreate option values in HubSpot before importing data. |
| address | Multiple single-line text properties | Lossy transformation — Zendesk stores address as a composite object (line1, city, state, zip, country). Create separate HubSpot properties for each component. |
| date | Date picker | Direct mapping. |
| datetime | Date picker | HubSpot date properties do not store time; time component is lost. |
1.3 Setting Up API Access
Your migration application requires valid authentication for both platforms.
Zendesk Sell Authentication
Authentication for the Sell Core API must include a valid Access Token. This token is typically generated as a Personal Access Token in the Sell account settings and must be securely stored. All API requests use the Authorization: Bearer $ACCESS_TOKEN header.
HubSpot CRM Authentication
HubSpot APIs typically use OAuth 2.0 or Private Apps (static auth tokens) for secure access.
- Generate Initial Tokens: If using OAuth, acquire the initial Access Token and Refresh Token by making a URL-form encoded POST request to /oauth/v1/token, supplying the client_id, client_secret, redirect_uri, and the authorization code.
- Token Refreshment: Access Tokens are short-lived (lifetime defined by expires_in). Use the Refresh Token to obtain a new Access Token via a POST request to /oauth/v1/token with grant_type=refresh_token, client_id, client_secret, and refresh_token.
1.4 Rate Limit Management (Crucial for Bulk Migration)
HubSpot imposes strict rate limits, which must be handled using batch processing and throttling to avoid 429 Too Many Requests errors.
- Standard Limits: Depending on the HubSpot subscription tier, the limit can range from 100 to 190 requests per 10 seconds per app (for Professional/Enterprise tiers, it may be 150 requests/10 seconds for association APIs). Daily limits also apply (e.g., 625,000 to 1,000,000 requests per day for Professional/Enterprise).
- Batch APIs: Utilize HubSpot's batch endpoints for creation, reading, and updating whenever possible. For example, batch operations for contacts are limited to 100 records per request.
-
- Example Batch Create: POST /crm/v3/objects/{objectType}/batch/create
- Example Batch Update: POST /crm/v3/objects/{objectType}/batch/update
- Example Batch Upsert: POST /crm/v3/objects/{objectType}/batch/upsert
1.5 ID Mapping Strategy
During migration, every Zendesk object ID must be mapped and linked to its corresponding newly created HubSpot Record ID (hs_object_id). This ID map is crucial for establishing cross-object associations (e.g., linking a Zendesk Deal ID to the correct HubSpot Contact and Company IDs).
Phase 2: The Migration Process
Execute the migration sequentially, ensuring foundational data structures and linking entities are created before dependent records are migrated.
Step 1: Migrate Users (Owners)
- Fetch Zendesk Users: Retrieve all users using GET /v2/users. Note the Zendesk User ID (id) for mapping purposes.
- Map to HubSpot Owners: While direct creation via API is limited, ensure corresponding users exist in HubSpot and map the Zendesk User ID to the HubSpot User/Owner ID. This ID will be used to populate the hubspot_owner_id property on contacts, companies, and deals.
Step 2: Migrate Pipelines and Stages
HubSpot supports pipelines for various objects, including Deals, Orders, and Leads.
- Fetch Zendesk Pipelines: Retrieve pipeline definitions using GET /v2/pipelines.
- Create HubSpot Pipelines: Create pipelines in HubSpot using POST /crm/v3/pipelines/{objectType}. For deals (CRM migration), use objectType=deals. Map the old pipeline ID to the new HubSpot Pipeline ID.
- Fetch Zendesk Stages: Retrieve stage definitions using GET /v2/stages, filtering by pipeline ID (pipeline_id). Note the stage name, position, category, and likelihood.
- Create HubSpot Stages: For each stage, create it in HubSpot using POST /crm/v3/pipelines/{objectType}/{pipelineId}/stages.
- Crucial Mapping: For Deal stages, the metadata object is required and must include the probability field (ranging 0.0 to 1.0), reflecting the Zendesk likelihood field.
Step 3: Recreate Custom Fields (Properties)
Custom fields from Zendesk Sell must be pre-defined as Custom Properties in HubSpot.
- Fetch Zendesk Custom Field Definitions: Retrieve definitions for relevant resource types (contact, lead, deal) using GET /v2/:resource_type/custom_fields. Note the field name and type (e.g., string, number, list, address).
- Create HubSpot Custom Properties: Use the HubSpot Properties API to recreate these fields, matching data types using the field type mapping in Section 1.2. Custom properties are created via a POST request to /crm/v3/properties/{objectTypeId} (e.g., /crm/v3/properties/0-1 for contacts).
- Map Field Keys: Map the original Zendesk custom field names to the new HubSpot internal property names (e.g., p_portalId_customfieldname).
Step 4: Migrate Organizations and Persons (Zendesk Contacts)
Use the Zendesk is_organization flag to split contacts into HubSpot Companies and Contacts.
- Migrate Companies (Organizations):
- Filter and fetch Zendesk Contacts where is_organization is true using GET /v2/contacts.
- Create HubSpot Company records using POST /crm/v3/objects/companies/batch/create (or upsert by domain: POST /crm/v3/objects/companies/batch/upsert). Store ID mappings.
- Migrate Contacts (Persons):
- Filter and fetch Zendesk Contacts where is_organization is false using GET /v2/contacts.
- Create HubSpot Contact records using POST /crm/v3/objects/contacts/batch/create (or upsert by email: POST /crm/v3/objects/contacts/batch/upsert?idProperty=email). Store ID mappings.
- Re-establish Contact-to-Company Association:
- For each migrated HubSpot Contact, check the original Zendesk Contact's contact_id (which links to the organization/company).
- Use the stored ID mappings to link the HubSpot Contact ID to the corresponding HubSpot Company ID using the Associations API (/crm/v3/objects/{contactId}/associations/{toObjectType}/{toObjectId}/{associationTypeId}). Use the default association ID for Contact to Company (279) or Company to Contact (280).
- Migrate Tags:
- For each Zendesk Contact (both organizations and persons), extract the tags array.
- Write the tag values into the corresponding HubSpot Company or Contact record's
zendesk_tagsmulti-select property (created in Step 3).
Edge case — Contacts without email: HubSpot uses email as the primary deduplication key for contacts. If a Zendesk Contact (person) has no email address, you cannot use the upsert-by-email endpoint. Use batch/create instead and store the returned HubSpot ID. Flag these records for manual review after migration, as they may create duplicates if the same person is later added with an email.
Step 5: Migrate Leads
- Fetch Zendesk Leads: Retrieve all leads using GET /v2/leads.
- Create HubSpot Leads: Create HubSpot Lead records using POST /crm/v3/objects/leads/batch/create or individually using POST /crm/v3/objects/leads.
- Mandatory Properties: HubSpot Leads require hs_lead_name and must be associated with an existing contact (person) at creation.
- Association: Use the mapped Contact IDs for association. Use association Type ID 578 for Lead to Primary Contact.
Step 6: Migrate Deals
- Fetch Zendesk Deals: Retrieve all deals using GET /v2/deals.
- Create HubSpot Deals: Use POST /crm/v3/objects/deals/batch/create or individual create calls (/crm/v3/objects/deals).
- Mandatory Properties: Include dealname, dealstage, and pipeline using the mapped internal IDs from Step 2.
- Ownership & Values: Populate hubspot_owner_id using the User ID map, and map Zendesk value (which can be string or number) to the HubSpot amount property.
- Loss/Unqualified Reasons: Fetch reason text from Zendesk (GET /v2/loss_reasons/:id or /v2/deal_unqualified_reasons/:id). Store this text in a custom HubSpot Deal property or as a Note associated with the deal, as HubSpot does not use explicit Loss Reason objects.
- Tags: Extract the tags array from each Zendesk Deal and write the values to a
zendesk_tagsmulti-select property on the HubSpot Deal (created in Step 3).
- Re-establish Deal Associations: Associate the new HubSpot Deal ID with the corresponding Contact (Person) and Company (Organization) IDs using the Associations API:
- Deal to Contact: Use Type ID 3.
- Deal to Company: Use Type ID 5 (Primary) or 341 (Unlabeled).
Step 7: Migrate Products and Line Items
This process involves moving the catalog first, then recreating the association to deals via line items.
- Migrate Products:
- Fetch Zendesk Products using GET /v2/products. Note properties like name, sku, price, and prices array (for currency data).
- Create HubSpot Products using POST crm/v3/objects/products. Map name, sku, and pricing information. Store Product ID mappings.
- Migrate Orders and Line Items:
- Fetch Zendesk Orders associated with deals using GET /v2/orders?deal_id={dealId}. Create corresponding HubSpot Orders (POST crm/v3/objects/order) and map the IDs.
- For each Zendesk Order, fetch associated Line Items (GET /v2/orders/:order_id/line_items). Note the product_id, quantity, and value.
- Create HubSpot Line Items (POST /crm/v3/objects/line_items). The request must include the new Line Item properties (name, quantity, price).
- Crucial Association: In the Line Item creation payload, associate the new Line Item with the corresponding HubSpot Deal ID and/or Order ID.
-
- Line Item to Deal associationTypeId: 20.
- Line Item to Order associationTypeId: 514.
- Note: Ensure Line Items are created as instances linked to a single parent object to prevent data loss or corruption.
Step 8: Migrate Activities (Notes, Calls, Communications/Text Messages)
Activities (Engagements) must be associated with the corresponding HubSpot records using the mapped IDs.
- Notes:
- Fetch Zendesk Notes (GET /v2/notes), noting the content, created_at, resource_type, and resource_id.
- Create HubSpot Notes (POST /crm/v3/objects/0-46). The body must include the note content and hs_timestamp.
- Associate the new Note ID with the corresponding HubSpot Contact, Company, or Deal ID.
- Calls:
- Fetch Zendesk Calls (GET /v2/calls), noting details like summary, duration, phone_number, and associated IDs.
- Create HubSpot Calls (POST /crm/v3/objects/0-48). The call record requires hs_timestamp. Map the call metadata and association information.
- Text Messages (Communications):
- Fetch Zendesk Text Messages (GET /v2/text_messages).
- Create HubSpot Communications (POST /crm/v3/objects/communications). Set properties like hs_communication_channel_type (e.g., "SMS"), hs_communication_body (content), and hs_timestamp.
- Associate the communication with the relevant Contact or Deal using the appropriate Type IDs (e.g., Company to Communication: 87, Contact to Communication: 81).
Email and communication history: Zendesk Sell stores email threads tied to contacts and deals. Migrating these is typically the most painful part of any CRM migration. If preserving email history is a requirement, expect to extract email records from Zendesk Sell and recreate them as HubSpot Email engagement objects (POST /crm/v3/objects/0-49) with appropriate timestamps and associations. This is a non-trivial effort that often warrants dedicated handling.
Step 9: Migrate Documents and Files
- Retrieve File URLs: Fetch document metadata from Zendesk (GET /v2/documents) to retrieve the expiring download_url for each file.
- Download and Upload: Download the file content using the temporary URL. Upload the file content to HubSpot CMS using the HubSpot CMS APIs.
Phase 2.5: Service Hub Migration — Tickets, Custom Fields, and Automations
If your Zendesk Sell environment includes support-adjacent data — cases, task automations, or notification triggers — you'll want to extend the migration to cover HubSpot Service Hub objects. This section covers ticket pipeline setup, Service Hub–specific custom field mapping, automation translation, and the workflow reconstruction process.
2.5.1 Ticket Pipeline and Custom Property Setup
Before migrating any ticket or support-related data, create the target pipeline and custom properties in HubSpot.
- Create Ticket Pipelines: Use
POST /crm/v3/pipelines/ticketsto create your ticket pipeline(s). Map each Zendesk Sell status or case stage to a corresponding HubSpot ticket pipeline stage usingPOST /crm/v3/pipelines/tickets/{pipelineId}/stages. - Create Service Hub Custom Properties: Zendesk Sell custom fields on support-related objects must be recreated as custom properties on the HubSpot Ticket object. Use the same field type mapping from Section 1.2 and create properties via
POST /crm/v3/properties/tickets.
Organize your custom properties into logical property groups so they remain manageable in the HubSpot UI. Common groupings:
- Ticket Classification — category, subcategory, source channel, priority level
- Customer Context — account tier, contract type, product area
- Resolution Metadata — root cause, resolution type, time to resolution
Property group naming: HubSpot lets you create custom property groups via the API or UI. Grouping Service Hub properties by function (classification, context, resolution) prevents the "wall of fields" problem that makes tickets harder to work in practice.
Service Hub Custom Field Mapping Reference
Map Zendesk Sell custom fields on support-adjacent objects to HubSpot Ticket properties using the same type-mapping rigor as Section 1.2:
| Zendesk Sell Source Field | HubSpot Ticket Property | Property Group | Mapping Notes |
|---|---|---|---|
| Category / Type (list) | Custom dropdown (e.g., ticket_category) |
Ticket Classification | Recreate all option values before import. |
| Priority (list) | hs_ticket_priority (built-in) or custom dropdown |
Ticket Classification | HubSpot has a built-in priority property — use it if the values align; create a custom property if they don't. |
| Tags (array) | Custom multi-select (e.g., zendesk_ticket_tags) |
Ticket Classification | Same pattern as CRM tag migration in Step 4. |
| Custom text fields (string/text) | Single-line or multi-line text | Customer Context | Direct mapping per Section 1.2. |
| Custom date fields (date/datetime) | Date picker | Resolution Metadata | Time component lost on datetime fields (same caveat as Section 1.2). |
| Custom number fields (number) | Number | Resolution Metadata | Direct mapping. |
| Custom boolean fields (bool) | Single checkbox | Customer Context | Direct mapping. |
2.5.2 Automation and Workflow Migration
Zendesk Sell uses automation rules — task auto-creation, auto-assignment, notification triggers, and field update automations — that have no direct API export path. These must be manually cataloged and reconstructed as HubSpot Workflows.
No programmatic export: Zendesk Sell automations cannot be exported via API and recreated in HubSpot programmatically. Every automation rule must be manually documented in the source system, translated to HubSpot's workflow model, and rebuilt. Budget time for this — it is the most labor-intensive part of the Service Hub migration.
Automation Translation Reference
| Zendesk Sell Automation | HubSpot Equivalent | Migration Approach |
|---|---|---|
| Task auto-creation on deal stage change | Ticket-based or deal-based workflow with "Create task" action | Recreate manually; map trigger conditions to enrollment criteria |
| Auto-assignment rules | HubSpot routing rules or workflow "Rotate owner" action | Rebuild using HubSpot's round-robin or conditional branching |
| Notification triggers | Workflow "Send internal notification" action | Map notification recipients and conditions |
| Field update automations | Workflow "Set property value" action | Map field names using the custom property mapping table above |
Workflow Reconstruction Methodology
Follow this sequence to systematically translate Zendesk Sell automations into HubSpot Workflows:
- Inventory all Zendesk Sell automation rules. Document each rule's trigger, conditions, and actions. There is no bulk export — walk through the Zendesk Sell automation settings screen by screen.
- Classify each automation by trigger type: time-based (e.g., "3 days after last activity"), event-based (e.g., "deal stage changes"), or property-based (e.g., "priority equals high").
- Map triggers to HubSpot enrollment criteria. HubSpot Workflows use enrollment triggers that correspond to property changes, form submissions, date-based conditions, or manual enrollment. Translate each Zendesk trigger to the closest HubSpot enrollment type.
- Map actions to HubSpot workflow actions. Common action translations: create task, send notification, set property value, rotate owner, send email, create record.
- Rebuild each workflow in HubSpot. Use the HubSpot Workflow editor (or the Workflows API for bulk creation if you have many similar workflows).
- Test with enrollment simulation. Use HubSpot's "Test" enrollment feature to run sample records through each workflow before activating it in production.
2.5.3 Service Hub Testing and Validation Checklist
Extend the Phase 3 checklist (Section 3.6) with these Service Hub–specific verification items:
- Ticket pipeline(s) and statuses created and match source configuration
- Ticket custom properties created with correct types, option values, and property groups
- Sample tickets migrated with all custom field values intact
- Ticket-to-Contact and Ticket-to-Company associations verified
- Routing rules tested — tickets assigned to correct owners/teams
- Each reconstructed workflow tested with sample record enrollment
- Workflow actions verified (notifications sent, properties updated, tasks created)
- SLA timers configured and validating correctly on test tickets (if applicable)
- Edge case: tickets with no associated contact handled gracefully
- Automation output cross-referenced against original Zendesk Sell behavior
Phase 3: Post-Migration & Best Practices
3.1 Preserving Historical Timestamps
By default, HubSpot sets createdate to the moment a record is created via API. To preserve the original Zendesk Sell creation dates, set the hs_createdate property explicitly during record creation. This allows backdating records so that historical reporting and timeline views in HubSpot accurately reflect when records were originally created in Zendesk Sell.
3.2 Data Validation and Reconciliation
After migrating data in batches, perform crucial verification steps:
- Count Verification: Compare the total count of key entities (Contacts/Persons, Companies/Organizations, Deals, Line Items) between Zendesk Sell and HubSpot.
- Spot Checks: Perform manual spot checks on complex records (e.g., deals associated with multiple contacts, several activities, and products) to ensure all links and property values were correctly transferred and mapped.
- Association Integrity: Verify crucial relationships using the HubSpot Associations API. For example, check a sample of Contact records to ensure they are correctly associated with their primary Company.
3.3 Error Handling and Partial Failures
Batch API calls can partially succeed — some records in a batch may be created while others fail. Your migration code must handle this:
- Parse batch responses: HubSpot batch endpoints return individual status codes per record. Check each result and log failures with the original Zendesk ID and the error message.
- Retry failed records: Collect failed records from each batch and retry them in a separate batch. Common failure causes include missing required properties, invalid property values, and association targets that don't exist yet.
- Handle 429 errors: When you receive a 429 Too Many Requests response, read the
Retry-Afterheader and wait before retrying the entire batch. - Idempotency via upsert: Where possible, use batch/upsert endpoints with a stable unique identifier (email for contacts, domain for companies) so that retrying a failed batch does not create duplicate records.
3.4 Handling Deltas and Cutover
For a zero-downtime migration, a final delta synchronization is often necessary.
- Initial Freeze: Coordinate a period where sales activity is temporarily paused in Zendesk Sell.
- Delta Sync: Use the updated_at timestamps recorded during the initial bulk export from Zendesk Sell (available on almost all Zendesk objects) to fetch only records updated since that time.
- Upsert to HubSpot: Use HubSpot's batch upsert endpoints (POST /crm/v3/objects/{objectType}/batch/upsert) for Contacts, Companies, and Deals using unique identifiers (like email or domain) to efficiently update existing records or create new ones in one call.
3.5 Leveraging Batch Operations and Throttling
To optimize transfer speed while respecting HubSpot's API limits (e.g., 100-190 requests per 10 seconds):
- Chunking: Break large transfers into small batches (max 100 inputs for standard batch operations).
- Throttling: Implement rate-limiting logic that monitors API usage headers and throttles requests immediately upon receiving a 429 error, waiting before retrying.
- Caching: When repeatedly querying HubSpot for read-only data (like Pipeline IDs or Owner IDs), cache the results locally to reduce API call volume.
- Webhooks: For ongoing data synchronization post-migration, leverage HubSpot's Webhooks feature to receive real-time notifications about data changes instead of continuous polling. Note that webhook-triggered workflow actions may still count toward API limits depending on the trigger type and your HubSpot plan tier — verify the behavior for your specific configuration.
3.6 Migration Checklist
Use this checklist to track progress across the full migration:
- API credentials configured for both Zendesk Sell and HubSpot
- Users/Owners mapped between systems
- Pipelines and Stages created with correct probability mappings
- Custom fields recreated as HubSpot properties (with correct type mapping)
- Companies migrated (is_organization: true contacts)
- Contacts migrated (is_organization: false contacts)
- Contact-to-Company associations established
- Tags migrated to custom multi-select properties
- Leads migrated with Contact associations
- Deals migrated with pipeline, stage, and owner mappings
- Deal associations established (Contact + Company)
- Loss/Unqualified reasons stored as custom properties or notes
- Products migrated
- Orders and Line Items migrated with Deal/Order associations
- Notes, Calls, and Text Messages migrated with associations
- Email history migrated (if required)
- Documents and Files uploaded
- Historical timestamps preserved via hs_createdate
- Record counts verified across both systems
- Association integrity spot-checked
- Delta sync executed for cutover
- Service Hub: Ticket pipelines and custom properties created (see Section 2.5.3)
- Service Hub: Automations reconstructed and tested as HubSpot Workflows (see Section 2.5.3)
- Service Hub: Ticket associations and routing rules validated (see Section 2.5.3)
- Zendesk Sell decommissioned



