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
TL;DR
Customer: Whittard of Chelsea (tea, coffee and hot chocolate retailer, London. Founded 1886, now selling in 26 countries.)
The Move: Freshdesk, Freshchat and Freshcaller into one Gorgias workspace. Three separate Freshworks products, three separate migrations, one destination.
The Scope: Roughly 3.9 million records. Over 1 million Freshdesk tickets with more than 700,000 replies and over 500,000 contacts; roughly 73,000 chat conversations carrying over 1.3 million messages; close to 117,000 phone calls. A decade of email history, and seven years of chat and phone.
The Roadblock: Freshdesk auto-archives old tickets, and archived tickets cannot be listed, searched or filtered through the API. A normal full-history pull returned fewer than 6,000 tickets. The real archive held over a million.
The Outcome: Email, live chat and telephony history that used to live in three disconnected products now sits in one Gorgias workspace, on one customer timeline, every record tagged by the system it came from.
By the Numbers
- Total records migrated: roughly 3.9 million
- Chat messages: over 1.3 million
- Freshdesk tickets: over 1 million
- Ticket replies: over 700,000
- Contacts: over 500,000
- Phone calls: close to 117,000
- Chat conversations: roughly 73,000
- Knowledge base articles: over 400
- Canned responses: close to 300
- Ticket types mapped: 10
- Data span: a decade of email history, seven years of chat and phone
The Challenge: Three Products, One Customer History
Whittard of Chelsea has been selling tea and coffee since 1886, when Walter Whittard opened a shop on Fleet Street and blended everything on site. The business now trades in 26 countries, and its customer service runs on the volume you would expect of a retailer that size across peak gifting seasons.
That service had grown across three separate Freshworks products. Email support ran in Freshdesk. Live chat ran in Freshchat. Phone ran in Freshcaller. Each held part of the same customer's history, and none of them held the whole of it. Moving to Gorgias meant collapsing all three into a single workspace without losing the decade of context that made the history worth keeping.
Three things made that harder than a standard Freshdesk to Gorgias migration:
- Most of the history was invisible to the API. Freshdesk automatically archives old tickets. Archived tickets do not appear in list calls, cannot be filtered for, and do not come back from ticket search. Ask Freshdesk for everything updated since the year 2000 and the call succeeds, returning fewer than 6,000 tickets. That is everything its list API is prepared to admit exists, against an account holding over a million.
- Three products meant three data models. A Freshdesk ticket, a Freshchat conversation and a Freshcaller call are not the same object. They have different identifiers, different status vocabularies, different notions of who the participants are, and in the case of calls, no text content at all.
- The destination had hard limits that failed loudly in some places and silently in others. Gorgias caps an individual message at 1 MB. It rejects some contact lookups that seem reasonable. And in at least one case it answers a malformed request with a bare HTTP 500 and no explanation.
Recovering a Million Archived Tickets
Establishing the true scope came first, and it came from probing rather than from documentation. Individual ticket IDs chosen well above the visible range returned 404 on a normal GET and returned the ticket when the request declared is_archived=true. So the records existed and were individually retrievable. They just could not be discovered.
The route around it was an account-level export. Whittard generated a CSV of ticket IDs from Freshdesk's own export tooling — a file of roughly 400 MB listing over a million unique ticket IDs, running from the first ticket the account ever raised through to its most recent. That file became the work list. Rather than asking Freshdesk what tickets existed, the pull walked the CSV and fetched each ticket by ID with the archive flag set.
At that volume the retrieval itself becomes the engineering problem. The pull ran at 15 concurrent fetches against a ceiling of 40 requests per 10 seconds, with per-ID error isolation so a single unreadable record cannot end a run carrying hours of work. Ticket IDs already stored were preloaded into a set and skipped, so the walk only ever fetched what it did not already hold.
Quota was the scarce resource throughout. With the archive walk and the comment pull drawing on the same 40 requests per 10 seconds, the comment pull never re-fetches a ticket body that is already stored, so the whole budget stays with the archive walk that needs it.
The ClonePartner Solution: Three Migrations, One Destination
Each product was run as its own migration job against the same Gorgias account, so a failure in one could never damage another, and each could be paced independently against its own source rate limits.
Email. Freshdesk tickets were recreated in Gorgias with their full reply history. Status was mapped from Freshdesk's numeric codes to Gorgias open and closed states, with resolved and closed both landing as closed. Support groups became tags — Customer Service, Corporate Sales, B2B/Wholesale, Automated — as did priority and each of the ten ticket types, so the routing knowledge encoded in the old helpdesk survived as something searchable in the new one. Original creation and resolution timestamps were written into dedicated custom fields, because a migrated ticket that reports today's date as its creation date is worse than useless for a retailer looking at seasonal patterns.
Chat. Freshchat conversations became Gorgias tickets, with each message classified on the way in: customer, agent and bot messages arrived as public replies, system messages as internal notes. Bot-handled conversations were kept. Freshworks' assistant had resolved a meaningful share of chats, and a customer timeline with those conversations missing would have unexplained gaps in it.
Phone. Freshcaller calls have no message body, so the migration had to synthesise the record: direction tagged inbound or outbound, outcome tagged answered, missed, voicemail or abandoned, and call metrics attached as a note. Abandoned calls and calls with no audio were migrated deliberately rather than skipped — a customer who called three times and gave up twice is telling you something, and that signal only exists if the abandoned calls are there. Phone numbers were normalised against the UK region so the same caller resolved to the same Gorgias contact across the whole history.
Every record carries a tag naming its source system, and each migration wrote to its own Gorgias view. A year from now, anyone can still tell which records came from where.
Under the Hood: What Gorgias Requires That Its Documentation Does Not Say
Most of the engineering in a three-product consolidation is not the mapping. It is the handful of destination behaviours that are written down nowhere — each one found by probing, designed around, and then proven by reading Gorgias back.
An attachment note needs the channel and the agent. Gorgias will take the message and refuse the file if either is missing, and the ticket around it still lands — so attachments are counted in their own right, against the destination. Over three thousand chat files came across on that basis.
An inline image can cost you the whole conversation. Freshchat sometimes returns an image as a base64 data URI rather than a link to storage. Embedded in the body, that pushes the message past Gorgias's 1 MB cap, and Gorgias refuses the entire ticket rather than the image — so the conversation would arrive as nothing at all, instead of as a conversation missing one picture. Inline images are decoded and uploaded as real attachments before the message is written.
An emoji cut in half comes back as a bare 500. Subject lines have to be truncated to 80 characters, and a truncation that counts UTF-16 code units can slice an emoji down the middle. Gorgias answers the unpaired half with an HTTP 500 and no hint as to the cause. Subjects are cut on character boundaries instead.
Contacts cannot be looked up by phone number. Gorgias does not support that filter — which matters when the records being migrated are calls. Calls are matched through a phone index built in advance, with every number normalised to the UK region first, so the same caller resolves to the same contact across a decade of history. A number the index cannot resolve is reported as unresolved, with the call it belongs to.
Gorgias meters per account, so uploads belong in the same queue as everything else. Two independent backoffs against one account limit spend the same budget twice and both slow down for it. With every call through a single queue, roughly five thousand downloads ran with rate-limit responses in the single figures.
The Results: Complete on All Three
Chat and phone were pushed in under two weeks. Email ran for roughly six weeks, the longer window reflecting the million-ticket archive.
All three finished complete. Every one of the Freshdesk tickets landed in Gorgias, along with effectively all of the replies. Every chat conversation and every chat message migrated. Every call migrated. The counts come from reading Gorgias back, and each migration's records are retrievable there under its own tag and view.
What Whittard holds now is a single workspace where an agent opening a customer can see the email thread from the early years, the chat from the middle of the decade and the call from last month in one place — which was not possible in any of the three systems they came from.
What Made This Migration Different
For teams planning a move off Freshworks, three details from this project are worth noting:
-
A clean pull is not a complete pull. Freshdesk's list API returned fewer than six thousand tickets without an error, a warning, or any indication that more than a million others existed. Auto-archiving is a storage feature with an API consequence that nothing in the response tells you about. Before trusting any full-history export, probe a few high ticket IDs directly and compare what comes back against what the list gave you, the way any Freshdesk migration checklist should start. If the numbers disagree, the export tooling in the account UI is the route to the real work list.
-
A push that reports success is only reporting that it sent something. The destination behaviours worth hunting are the quiet ones: an attachment refused while its ticket lands, a conversation refused outright for one oversized inline image, a download that returns a 403 and still looks retrieved. None of them raise anything on the way past. Verification has to read the destination back and count what is actually in it — which is how every figure on this page was produced.
-
Consolidating products is not three migrations glued together. Email, chat and phone have genuinely different shapes, and the decisions that matter are the ones about what a record means rather than where a field goes. Whether an abandoned call is worth migrating, whether bot-resolved chats belong in the history, whether a system message is a reply or a note — none of those are answered by a field mapping, and all of them change what the destination is worth to the people who have to use it.
Frequently Asked Questions
- Why does a Freshdesk full-history export return far fewer tickets than the account holds?
- Freshdesk automatically archives old tickets, and archived tickets do not appear in list calls, cannot be filtered for and do not come back from ticket search. On this account a standard full-history pull finished cleanly and returned fewer than 6,000 tickets while the real archive held over a million. Nothing in the response indicates that anything is missing.
- Can archived Freshdesk tickets be retrieved through the API?
- Individually, yes. An archived ticket returns 404 on a normal GET and returns the ticket when the request declares the archive flag, so the records are retrievable but not discoverable. The route around it is an account-level CSV export of ticket IDs from Freshdesk's own export tooling, which then becomes the work list the pull walks.
- Can Freshdesk, Freshchat and Freshcaller be merged into a single Gorgias workspace?
- Yes. Each product was run as its own migration job against the same Gorgias account, so a failure in one could never damage another and each could be paced against its own source rate limits. Every record carries a tag naming its source system and each migration wrote to its own Gorgias view, so a year later anyone can still tell which records came from where.
- How do Freshcaller phone calls migrate into a help desk that expects text?
- Freshcaller calls have no message body, so the record has to be synthesised. Direction was tagged inbound or outbound, outcome tagged answered, missed, voicemail or abandoned, and call metrics attached as a note. Abandoned calls and calls with no audio were migrated deliberately rather than skipped, because a customer who called three times and gave up twice is telling you something.
- Does Gorgias preserve original creation dates from a Freshdesk migration?
- Not in its own timestamp fields. Original creation and resolution timestamps were written into dedicated custom fields instead, because a migrated ticket that reports today's date as its creation date is worse than useless for a retailer looking at seasonal patterns.