You have evaluated the Zendesk competitors. You have compared pricing, features, and G2 ratings. You have picked a platform. Now comes the part none of the comparison articles cover: moving six years of tickets, custom fields, conversation threads, and Sunshine Custom Objects into it without losing data.
Every Zendesk competitor — Freshdesk, Intercom, Salesforce Service Cloud, HubSpot, Pylon, Help Scout — has an importer. What none of them advertise is that their importer operates entirely on data you provide. They do not pull from the Zendesk API. They do not resolve your custom field names. They do not know that Sunshine Custom Objects exist. They receive whatever file or payload you send them, and they import it.
If that payload is incomplete, anonymous, or truncated, that is what lands in the new system.
This is not a criticism of any specific platform. It is a structural problem with how help desk migrations work. The extraction step is your responsibility, and it has five specific failure points that kill most migrations before the import begins.
How every Zendesk importer works
Before getting into the failure modes, it helps to understand what a Zendesk competitor importer actually does.
Most importers accept one of three inputs: a CSV file produced by Zendesk's built-in export, a structured API payload you construct yourself, or a payload built by a third-party migration service (Help Desk Migration, Trujay, or similar).
In every case, the importer's job begins where your export ends. It maps fields from the input format to the destination platform's data model. It creates tickets, conversations, contacts, and organizations in the new system. It does not validate that your field names are correct, that your conversation threads are complete, or that your attachment URLs are still valid.
The quality of your import is entirely determined by the quality of your extraction.
The five failure points
1. Anonymous custom field IDs
Zendesk stores every custom field with a numeric ID. Your ticket export contains columns like custom_field_4471, custom_field_8821, custom_field_11043. The human-readable names — Refund Reason, Customer Tier, Product ID, Escalation Path — live in a completely separate API endpoint: GET /api/v2/ticket_fields.
Most exports skip the join. The Zendesk CSV export does not resolve field names. Most API scripts export ticket payloads without hitting the ticket_fields endpoint first. The result is a payload where every custom column is a number.
When this payload lands in the destination importer, those numbers become the field names in the new system. A 20-agent support team that has used Zendesk for five years ends up with a Freshdesk or Intercom instance full of fields labeled 4471, 8821, 11043. This is one of the most common complaints in a Zendesk vs Freshdesk migration — and in Freshdesk vs Zendesk evaluations where the team does a test import before committing. It applies equally to every other destination. Every custom field has to be manually relabeled after the migration, by someone who remembers what the numbers meant.
The correct approach: pull GET /api/v2/ticket_fields before extraction begins, build a mapping table of ID to name, and apply it to every ticket payload before the import. This is a non-trivial ETL step, especially for accounts with hundreds of custom fields accumulated over years, some of which have been deleted, renamed, or repurposed.
2. Conversation threads truncated to ticket headers
The Zendesk Incremental Export API — the standard endpoint for bulk ticket export — returns ticket objects. A ticket object contains status, subject, requester ID, assignee ID, timestamps, tags, and custom field values.
It does not contain the conversation.
Conversation history — the full thread of public replies, internal notes, and side-conversations — lives in the Ticket Comments API. Retrieving it requires a separate API call per ticket. For an account with 200,000 tickets, that is 200,000 additional API calls, each returning a paginated list of comment objects that must be stored and joined back to the parent ticket.
Most DIY export scripts stop at the ticket object level. The migration importer receives a CSV or JSON payload with ticket headers but no messages. The destination platform creates empty tickets — status, subject, date — with no conversation history.
Internal notes are the most commonly lost data type. They are stored as private comments in Zendesk's data model. In destination platforms, the equivalent field varies: Freshdesk uses private notes, Salesforce uses internal CaseComments, HubSpot has no direct equivalent in the standard data model. Without a deliberate extraction and mapping step, internal notes disappear entirely.
3. Sunshine Custom Objects silently skipped
Sunshine Custom Objects are the part of Zendesk's data model that most teams do not discover until they run their first extraction.
Zendesk's Sunshine layer allows storing arbitrary structured data outside the ticket schema: hardware asset inventories, license entitlements, B2B account hierarchies, product metadata, SLA configurations. This data exists only in the Custom Objects API (GET /api/v2/custom_objects). It is not included in the Incremental Export. It is not mentioned in most migration guides. No Zendesk competitor has a native importer for it.
The deprecation deadline: Zendesk is permanently retiring the Sunshine Custom Objects API on July 1, 2026. After that date, the endpoints return 410 Gone. The data is not archived, not moved, not accessible by any other means. It is deleted.
If your Zendesk instance has Sunshine Custom Objects — and any account with more than 200 agents or product-led support workflows almost certainly does — you have a fixed window to extract them. The extraction requires paginating through every object type definition, every object record, and every relationship record (the join table between object types and tickets). It is a separate extraction pipeline from the ticket export.
See the full Sunshine extraction guide for the technical walkthrough.
4. Attachment URLs expire mid-migration
Every attachment in Zendesk is stored in Amazon S3. The URL you receive from the API is a pre-signed link with an expiration timestamp — typically between one and a few hours from the time of generation.
A slow extraction script, a migration that runs overnight, or a process that pauses and resumes will generate fresh attachment URLs early in the run. By the time the importer processes the payload, those URLs have expired. The importer receives a 403 Forbidden response when attempting to fetch the binary file. Depending on the importer, it either silently skips the attachment or logs a warning that nobody reads until the migration is complete.
The correct approach is to resolve attachments during the extraction run — download the binary files, re-host them, and provide direct URLs to the importer rather than the Zendesk pre-signed links. This requires building file handling into the extraction pipeline, not treating attachments as a pass-through URL column.
For large accounts with years of ticket history, attachment resolution is typically the most time-consuming part of a migration. Treating it as an afterthought is why many migrations finish with thousands of broken attachment references.
5. Rate limit stalls produce partial datasets
The Zendesk API enforces rate limits at both the per-minute and per-hour level. The Incremental Export endpoint has specific throttling behavior: it returns a cursor that must be used to continue pagination, and if a request fails mid-run, the cursor position may need to be tracked manually to avoid starting over.
A naive extraction script that hits a 429 Too Many Requests response and stops — rather than implementing exponential backoff and retry logic — will produce a partial dataset. Tickets after the stall point are simply missing. The importer has no way to know that the input is incomplete. The migration completes successfully from the importer's perspective, with a subset of the actual ticket history.
At scale, this failure is invisible. A 500,000-ticket account that stalls at 340,000 tickets produces an import that looks complete. Nobody notices that 160,000 tickets are missing until someone searches for a specific ticket that should exist and does not.
The Incremental Export's cursor-based pagination requires building state into the extraction process: persisting the cursor position, detecting stalls, implementing retry logic with jitter, and validating the final record count against the Zendesk account total. See the API rate limits and pagination guide for the implementation details.
Summary
The five failure points at a glance.
| Failure point | What triggers it | Data at risk | Detectable before import? |
|---|---|---|---|
| Anonymous field IDs | Skipping the ticket_fields join | All custom field names | Yes — columns are numbered |
| Truncated conversations | Exporting ticket headers only | All replies, internal notes | No — tickets look complete |
| Sunshine objects skipped | Not querying the Custom Objects API | Hardware records, entitlements, custom object data | No — not present in standard export |
| Expired attachment URLs | Slow or paused extraction run | Binary files, screenshots, logs | No — 403s often logged, not flagged |
| Rate limit partial dataset | No retry logic on 429s | All tickets after stall point | No — import count looks correct |
What a clean extraction actually produces
A complete Zendesk extraction is not a single API call or a CSV download. It is five coordinated pipelines running against five different parts of the Zendesk API, with the output joined into a single structured dataset before the importer ever sees it.
The output of a correct extraction:
- Tickets with human-readable column names (not
custom_field_4471) - Full conversation threads with internal notes, agent replies, and timestamps
- Sunshine Custom Objects with their relationship graph intact
- Attachments resolved to direct URLs, not expiring pre-signed links
- A complete record count validated against the Zendesk account total
That is what every destination importer needs to do its job. The importer itself does not care how you produced it — it just needs clean, complete data.
Evicta runs all five pipelines in a single job. Human-readable column names via AI-assisted field mapping. Full conversation threads retrieved per ticket. Sunshine Custom Objects extracted before the July 1, 2026 deadline. Attachments resolved during the run. Cursor-based pagination with exponential backoff and state persistence throughout.
The output is a Postgres schema or AI-ready JSONL file ready to hand to whichever competitor you have chosen. Flat-fee pricing: $499 for the core extraction (tickets, users, orgs), $1,499 for the full extraction including Sunshine Custom Objects and JSONL output. No ongoing subscription.
For a comparison of the platforms themselves — features, pricing, and which one fits your team — see the Zendesk alternatives guide. For the broader pattern of why migrations fail beyond these five technical points, see the helpdesk migration trap.
Pick your platform. Then extract your data cleanly before you cancel the Zendesk subscription.