Zendesk to Pylon Migration: 5 Pitfalls & How to Avoid Them
Zendesk to Pylon migrations break on inline images, identity mapping, and API rate limits. Here are the 5 pitfalls we see repeatedly — and how to avoid them.
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,200+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
Moving from Zendesk to Pylon means moving from an email-first ticketing system to a Slack-first, account-centric B2B support platform. The strategic case is straightforward: your customers live in Slack and Teams, and Pylon meets them there natively. Pylon offers true omnichannel support across Slack, Teams, email, chat, ticket forms, and more. The migration itself is where teams get hurt.
After handling 1,200+ helpdesk migrations — including Zendesk-to-Pylon paths — we see the same five failure modes repeat. Orphaned tickets with no requester context. Inline images that render as broken icons. API rate limits that silently drop records. Identity mismatches between Zendesk's email model and Pylon's Slack/account model. And workflow debt that teams blindly try to recreate instead of rethinking.
This post covers each pitfall with the technical specifics, explains why DIY scripts and automated tools struggle with them, and lays out what a reliable migration actually requires. If you're planning this move, read this before you write your first line of extraction code.
Why this migration is structurally different
The reason teams move to Pylon is architectural, not cosmetic. Zendesk was built around email-based ticket queues. Pylon was purpose-built for B2B companies that support customers through shared Slack channels, Microsoft Teams, and email simultaneously.
Unlike legacy platforms that are adapted from email-first architectures, Pylon's approach centers on native integrations with business messaging: full support for Slack Connect, Microsoft Teams, and Discord as first-class channels. Founded in 2022, Pylon emerged from the insight that B2B customer relationships increasingly live in collaborative tools like Slack and Teams rather than traditional email-only help desks, and was built to treat these channels as first-class citizens.
The platform has gained serious traction. Pylon has raised $51M in venture funding, including a $17M Series A led by Andreessen Horowitz in August 2024 and a $31M Series B co-led by Andreessen Horowitz and Bain Capital Ventures in August 2025. The company reports 750+ customers, with more than 150 organizations migrating from legacy platforms like Zendesk, Intercom, and Salesforce.
Pylon stores data in an Issue/Contact/Account model. Zendesk stores equivalent history across tickets, users, organizations, identities, comments, attachments, and business rules. That model mismatch is where migrations fail. Pylon's own docs separate data migration from destination configuration — the import alone will not finish the job. (support.usepylon.com)
For the object-by-object architecture gap, read our Zendesk to Pylon Migration: The CTO's Technical Guide.
Pitfall #1: The broken inline image trap
Zendesk handles attachments in two ways: explicit file attachments (uploaded via the attachment button) and inline images embedded directly in the HTML body of a comment. Explicit attachments are straightforward to extract. Inline images are a known hazard.
The core problem: Zendesk inline images are hosted on Zendesk's infrastructure and — depending on account settings — require active authentication to load. When a migration script copies the html_body of a Zendesk comment into Pylon, every <img> tag still points to a *.zendesk.com URL. The images render fine on day one if you're logged into Zendesk in another tab. The moment you decommission Zendesk, every inline image in Pylon turns into a broken 404.
This is not an edge case. It is the default behavior for any account with secure downloads enabled.
When the "Enable secure downloads" option is enabled, agents are not able to add inline images to tickets any more — and those same authentication requirements apply to any external system trying to load those image URLs after migration.
If you allow private attachments, it will disable inline images for users who aren't signed in. Inline images are disabled only for Zendesk-hosted images and not images that are publicly accessible. Even within Zendesk itself, agents see inline images as broken links when the help center is host-mapped, because end users view the help center on a custom-branded domain while agents access their workspace on the standard Zendesk domain.
The private attachments in messaging feature is on by default for accounts created on or after December 5, 2024. If you created your Zendesk instance recently, your attachments already require authentication.
There is an extraction-level gotcha on top of all this: Zendesk's comments endpoint does not include inline images in the attachments array unless you explicitly request them with include_inline_images=true. If your extraction script doesn't set that parameter, you won't even see the inline images in the API response — let alone download them. (developer.zendesk.com)
Once Zendesk is decommissioned, all Zendesk-hosted image URLs die — authenticated or not. Any migration that copies HTML bodies without downloading and re-hosting the actual image binaries will produce permanently broken ticket history.
The programmatic fix
A resilient migration script must:
- Pull each ticket comment with
html_bodyandinclude_inline_images=true - Parse the HTML to identify every
<img>tag containing a*.zendesk.comURL - Authenticate against the Zendesk API to download each image binary
- Checksum the files so retries are idempotent
- Re-host the file in persistent storage (like AWS S3) or upload it directly to Pylon
- Rewrite the
srcattribute in the HTML payload to point to the new URL - Repeat for every comment on every ticket
For an instance with 50,000 tickets and even a modest rate of inline images, this step alone represents tens of thousands of individual file downloads.
Do not rely on regex to find inline images. Zendesk's rich text editor occasionally generates malformed HTML or nests images inside complex table structures. Use a dedicated HTML parsing library (like Cheerio for Node.js or BeautifulSoup for Python) to ensure you extract every URL.
Pylon's Zendesk migration guide states that ticket attachments are supported and migrated by default, and that migrated attachments appear inline at the end of each comment. (support.usepylon.com) That helps — but only if your extractor pulled the authenticated files correctly. Pylon cannot recover them for you later. Our guide to hidden costs of leaving Zendesk covers this and related attachment pitfalls in detail.
Pitfall #2: Orphaned tickets and identity mapping
Orphaned tickets are tickets that arrive in the target system with no linked requester, no assignee, or no associated account. The ticket body is there. The meaning is not.
This happens when ticket data is imported before users and organizations are fully migrated and mapped.
Why Zendesk → Pylon identity mapping is hard
Zendesk's identity model is email-centric. Every end user has an email address. Agents are identified by email. Organizations are optional groupings. Zendesk also supports multiple user identities — a user can have a primary email plus aliases, and Zendesk tracks delivery state across them. (developer.zendesk.com)
Pylon's model is account-centric and channel-native. Users are often identified by their Slack identity, their Teams identity, or both — in addition to email. A single Zendesk end user (identified as jane@acme.com) might correspond to a Pylon contact linked to the #acme-support Slack channel, a specific Slack user ID, and the Acme account object. If you import the Zendesk user without establishing that mapping, Pylon creates a new contact record. Now you have two jane@acme.com entries — one with Slack history, one with email history — and no unified view.
Duplicates typically come from these patterns:
- The same customer exists under a work email, alias email, and Slack identity
- Historical Zendesk users were merged badly or never merged at all
- Contractors or former champions still own old tickets
- Multiple Zendesk brands created parallel user records
- Account domains in the target are incomplete or wrong
That last one matters more than most teams realize. Pylon's docs warn that if account domains are set incorrectly, contacts can end up with access to issues outside the right account. (support.usepylon.com)
The sequencing rule
The migration must follow strict referential order:
- Organizations → Pylon Accounts — Map Zendesk organizations to Pylon accounts first. Establish the account hierarchy with domains and external IDs.
- Users → Pylon Contacts — Map each Zendesk user to their Pylon contact, linking to the correct account. Run an identity resolution script to bridge email addresses with Slack/Teams IDs. Deduplicate against any contacts that already exist in Pylon from Slack channel activity.
- Tickets → Pylon Issues — Only after users and accounts are in place, import tickets with their correct requester, assignee, and account associations.
- Comments — Append chronological comments to the established tickets.
- Attachments — Upload and link attachments to specific comments.
Breaking this order — or running steps in parallel without dependency resolution — produces orphaned tickets.
Before importing a single ticket, export your Pylon contact list (including Slack-linked contacts) and build a merge table mapping Zendesk user IDs → Pylon contact IDs. This is the single most important artifact in the entire migration.
Pylon's APIs support the account and contact preload strategy described above, including domains, multiple emails, and external IDs. (docs.usepylon.com) For the full pre-migration sequence, see our Zendesk migration checklist. For a deep dive on identity migration, see How to migrate users and organizations without breaking history.
Pitfall #3: Hitting Zendesk API rate limits
Every Zendesk data extraction runs through the API. There is no "export all" button that gives you tickets, comments, attachments, users, and organizations in a single structured archive. You extract them programmatically — and Zendesk's rate limits directly control how fast you can do it.
Zendesk API rate limits range from 200 req/min on Team to 2,500 on Enterprise Plus.
| Zendesk Suite Plan | Requests per Minute |
|---|---|
| Team | 200 |
| Growth | 400 |
| Professional | 400 |
| Enterprise | 700 |
| Enterprise Plus | 2,500 |
These are account-level limits, not per-agent limits. A 200-req/min Team plan limit covers your entire account — every integration, every webhook, every agent action through the UI that triggers an API call, all sharing the same bucket.
The incremental export bottleneck
The endpoint you need for bulk ticket extraction — the Incremental Exports API — has its own stricter limit: the Incremental Export endpoint is particularly important if you're syncing large datasets. You can only make 10 requests per minute to these endpoints, though this increases to 30 requests per minute if you have the High Volume API add-on.
At 10 requests per minute returning ~100 tickets per page, you can extract roughly 1,000 tickets per minute. A Zendesk instance with 200,000 tickets takes over three hours just for the ticket objects — before you fetch comments, attachments, or user records.
Standard offset pagination (?page=N) is capped at the first 100 pages or 10,000 resources. (developer.zendesk.com) For large exports, you must use Zendesk's cursor-based pagination:
GET /api/v2/incremental/tickets/cursor.json?start_time=1609459200Your extraction script must store the next_cursor value in a local database so it can resume exactly where it left off after a failure. Zendesk also excludes the most recent minute from incremental exports to avoid race conditions — your delta plan has to account for that gap.
What happens when scripts don't handle 429s
If the rate limit is exceeded, the API responds with a 429 Too Many Requests status code. The response also has a Retry-After header that tells you how many seconds to wait before retrying API requests. Ensure your code handles 429 errors and waits the Retry-After interval before retrying requests.
DIY scripts that lack proper retry logic don't fail loudly — they fail silently. The script receives a 429, doesn't retry, and moves to the next page. The result is a gap in your ticket export that nobody notices until an agent searches for a critical escalation three weeks after go-live.
resp = client.get(url)
if resp.status_code == 429:
sleep(int(resp.headers['Retry-After']))
retry_with_backoff(url)That snippet is the easy part. The real engineering work is centralized throttling across all concurrent API calls, checkpointing progress, and idempotent resume logic. For a deeper analysis of why quick-and-dirty extraction scripts break down, see Why DIY AI migration scripts fail.
Pitfall #4: Migrating legacy workflow debt
Zendesk instances that have been active for five or more years accumulate massive configuration debt: dozens of triggers with overlapping logic, SLA policies referencing deprecated groups, views nobody uses, and custom fields that served a purpose three product iterations ago.
The instinct is to replicate all of it in Pylon. Don't.
Pylon's architecture is fundamentally different from Zendesk's. Pylon's AI doesn't just suggest responses — it automatically triages tickets, routes intelligently, drafts contextual replies, and even updates ticket fields and statuses autonomously. Blindly porting Zendesk's trigger-based routing rules into Pylon's AI-driven system means you're overriding the platform's native intelligence with legacy logic.
Pylon's own migration documentation recommends a deliberate approach: You'll want to pay special attention to the major features like custom fields, SLAs, Triggers, Macros, CSAT, and Views. But "pay special attention" means audit and rebuild thoughtfully — not copy-paste.
Pylon's guidance on Zendesk automations points out that Zendesk automations run on an hourly cadence, are hard to predict precisely, and are harder to debug because the state change and the automation firing are separated in time. Pylon handles these use cases with event-triggered delays instead. A one-to-one copy of every Zendesk automation is usually the wrong design. (support.usepylon.com)
What to migrate vs. what to redesign
| Zendesk Element | Recommended Approach |
|---|---|
| Custom fields with active reporting value | Map to equivalent Pylon fields |
| SLA policies | Recreate in Pylon's SLA engine, but adjust for Slack-first response times |
| Triggers for routing | Let Pylon's AI triage handle routing; only port rules the AI can't infer |
| Macros (canned responses) | Import as templates for Pylon's AI assistant to reference, not as rigid playback |
| Views | Rebuild around Pylon's account-centric model, not agent-centric queues |
| Tags used for reporting | Migrate as data, but consolidate — Pylon's analytics work differently |
What to retire
Before the migration, export your macros and triggers to a spreadsheet and filter out anything that hasn't been used in the last 90 days. Common candidates for retirement:
- Old escalation tags tied to dead teams
- One-off fields created for past launches
- Macros owned by agents who left years ago
- Views built around statuses nobody uses consistently
- Automations that existed only to patch Zendesk behavior you're leaving behind
Treat the migration as a workflow reset. Export your Zendesk trigger and macro configurations for reference, but build Pylon's automation layer from your current operational reality — not your Zendesk muscle memory.
Pitfall #5: The cutover gap
Most teams plan a "big bang" migration: freeze Zendesk on Friday evening, run the migration over the weekend, go live on Pylon Monday morning. This is risky for any helpdesk migration, and especially risky for B2B teams where support doesn't stop because you sent a calendar invite.
During the freeze window, any new tickets that arrive in Zendesk create a gap. If the migration ran Friday at 11 PM and a customer emails at 2 AM Saturday, that ticket exists in Zendesk but not in Pylon. Your agents start Monday with missing context.
There is an operational constraint that makes the cutover design even more important: Pylon's migration documentation states that imported Zendesk tickets arrive in a closed state. Neither you nor the customer can keep replying to those imported historical threads in Pylon. (support.usepylon.com) This means you cannot run both systems in parallel indefinitely. Pylon's team migration guide warns that working out of both systems can create lossy issues, duplicated emails, and dropped tickets. (support.usepylon.com)
Zero downtime does not mean both systems stay live indefinitely. It means customers keep getting support while you control a short cutover window and run a final delta sync.
The delta-sync approach
A proper migration uses a delta-sync strategy:
- Initial bulk migration — Transfer the full historical dataset (users, orgs, tickets) while Zendesk remains live. Agents keep working normally.
- Incremental delta syncs — Run periodic catch-up syncs that pull only tickets created or updated since the last sync. This keeps Pylon's data within minutes of Zendesk's.
- Channel cutover — Point email forwarding to Pylon. Connect Slack channels. Swap chat widgets. Each channel cuts over independently.
- Final cutover sync — Run one last delta sync, then switch any remaining channels from Zendesk to Pylon.
- Reconciliation — Compare ticket counts, spot-check recent tickets, verify attachment integrity, and validate that no records were dropped.
This approach eliminates the freeze window entirely. Agents never stop working, and there's no gap where tickets fall into a void. You'll set a cutover date where your team starts responding from Pylon and you either shut off the old system immediately or let them finish first.
For the full methodology, see our guide to zero-downtime data migration.
DIY vs. automated tools vs. engineer-led service
Each migration approach handles these five pitfalls differently.
| Capability | DIY Scripts | Automated Tools (e.g. Help Desk Migration) | Engineer-Led Service (ClonePartner) |
|---|---|---|---|
| Inline image re-hosting | Must build custom; most skip it | Transfers attachments but may not handle inline HTML rewriting | Custom script downloads, re-hosts, and rewrites HTML |
| Identity deduplication | Manual merge table; error-prone | Requires all users migrated first; rigid sequencing | Builds merge table mapping Zendesk IDs → Pylon + Slack identities |
| API rate limit handling | Must implement backoff/retry; most don't | Handled internally but opaque | Managed throttling with intelligent retry and reconciliation |
| Workflow audit | No guidance | No guidance | Reviews Zendesk config, recommends what to port vs. redesign |
| Delta sync / zero downtime | Must build from scratch | Supports delta migration on some paths | Built-in delta sync with final cutover sync and reconciliation |
| QA and reconciliation | On you | Basic record counts | Side-by-side validation with audit trail |
DIY scripts give you maximum control but expose your team to weeks of API plumbing, attachment handling, and edge-case debugging. The rate limit math alone — 10 incremental export requests per minute on most plans — turns a "weekend project" into a multi-week engineering effort. If your core product is not data migration, assigning your engineers to build a one-off ETL pipeline is a drain on product velocity.
Automated tools handle field mapping through a UI but struggle with the identity mismatch between Zendesk's email model and Pylon's Slack-native model. They can automatically sync contacts and organizations that exist on both the source platform and Pylon, overwriting matching records with source data based on mapping settings. But if your Pylon instance already has Slack-linked contacts, the automated matching may not resolve correctly — creating duplicates rather than merging. Their own mapping documentation notes the most common risks show up during field mapping: missing tickets, duplicate users or organizations, and unwanted automation triggers.
Engineer-led services handle the full pipeline. At ClonePartner, we build custom extraction scripts that handle authenticated attachment downloads, construct the identity merge table before any ticket data moves, and run delta syncs until cutover. We've done this across 1,200+ migrations — not just Zendesk to Pylon, but Zendesk to Freshdesk, Zendesk to Intercom, Zendesk to Front, and dozens of other paths.
The right choice depends on your data volume, internal engineering capacity, and tolerance for data loss. If your Zendesk is tiny, clean, and disposable, a DIY approach may work. If it is revenue-linked support history with authenticated attachments, messy identities, and a mandate that support cannot pause — the hidden engineering bill of DIY or self-serve tools usually exceeds the cost of getting it done right the first time.
The go-live sequence that works
Here is the sequence we recommend — and execute — for Zendesk-to-Pylon migrations:
- Audit Zendesk — Export trigger, macro, SLA, and custom field configurations. Identify active vs. dead-weight config. Count tickets, users, orgs, and inline attachments.
- Build the identity merge table — Map every Zendesk user and org to their Pylon equivalent. Resolve Slack identity matches. Flag duplicates.
- Run the bulk migration — Extract and import historical data: orgs → accounts, users → contacts, tickets → issues. Re-host all inline images. Zendesk stays live throughout.
- QA the data — Verify that inline images load correctly, users are mapped to their Slack identities, and custom fields reflect accurate values.
- Run delta syncs — Daily (or more frequent) incremental syncs to keep Pylon current.
- Switch channels — Point email forwarding to Pylon. Connect Slack channels. Swap chat widgets. Each channel cuts over independently.
- Final delta sync and reconciliation — One last sync, then compare record counts, spot-check edge cases, and confirm attachment integrity.
- Decommission Zendesk — Only after Pylon is verified and stable. Keep read-only access for audit lookup until the team is satisfied.
The full weekend playbook is covered in our Zendesk to Pylon migration weekend checklist.
What this migration costs you if done wrong
The cost of a failed Zendesk-to-Pylon migration is not the tool fee or the engineering hours. It is the operational damage:
- Broken inline images across thousands of tickets mean agents can't reference past troubleshooting screenshots. Support quality drops immediately.
- Orphaned tickets mean your CSM opens a customer's history in Pylon and sees nothing — even though five years of context exists somewhere in a mislinked import.
- Dropped records from unhandled rate limits mean compliance gaps, missing audit trails, and customers who have to re-explain their issue.
- Duplicated contacts mean your AI triage routes to the wrong account, your health scores are split across two profiles, and your reporting is unreliable from day one.
The migration is a one-time event. The data quality you land with is permanent.
Frequently Asked Questions
- What happens to Zendesk inline images when migrating to Pylon?
- Zendesk inline images are hosted on Zendesk's servers and often require authentication. If your migration copies ticket HTML without downloading and re-hosting the actual image files, every image breaks permanently once Zendesk is decommissioned. A proper migration script must request inline images with include_inline_images=true, download each binary via authenticated API calls, upload to persistent storage, and rewrite the HTML src attributes.
- How long does a Zendesk to Pylon migration take?
- It depends on volume and plan. Zendesk's Incremental Export API is limited to 10 requests per minute on most plans. For a 200,000-ticket instance, just extracting ticket objects takes 3+ hours — before comments, attachments, and users. A full migration with delta-sync typically completes in days, not weeks, when handled by a dedicated migration team.
- What are Zendesk's API rate limits for data extraction?
- Zendesk Suite Team allows 200 requests/min, Growth and Professional allow 400, Enterprise allows 700, and Enterprise Plus allows 2,500. The Incremental Exports endpoint used for bulk ticket extraction has its own limit of 10 requests/min (30 with the High Volume API add-on). These are account-level limits shared across all integrations.
- Can I run Zendesk and Pylon in parallel during migration?
- Only for a short, controlled window. Pylon warns that working in both systems can create lossy issues, duplicated emails, and dropped tickets. Imported Zendesk tickets arrive in a closed state in Pylon, so you cannot continue replying to them. A single cutover plus a final delta sync is the safer approach.
- Should I recreate my Zendesk triggers and macros in Pylon?
- Not as a direct copy. Pylon's AI-driven triage and routing system works fundamentally differently from Zendesk's trigger-based automation. Audit your existing Zendesk configuration, identify which rules still reflect your current operations, and rebuild only those in Pylon's native automation framework. Let Pylon's AI handle routing and triage where possible.
