Zendesk to Pylon Migration: The CTO's Technical Guide
A technical guide to migrating from Zendesk to Pylon covering data mapping, API rate limits (10 req/min on Issues), migration approaches, and edge cases CTOs need to plan for.
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
Migrating from Zendesk to Pylon means moving from a traditional, email-first ticketing system to an AI-native, Slack-first B2B support platform. The data model is different, the API rate limits are punishingly asymmetric, and the object parity is incomplete. This guide covers the exact mapping, rate-limit math, migration approaches, and edge cases you need to plan for before writing a single line of migration code.
For a broader look at export options before you begin, see How to Export Tickets from Zendesk: The Complete Step-by-Step Guide.
Why B2B Teams Move from Zendesk to Pylon
The shift is architectural, not cosmetic. Zendesk is a mature, highly configurable helpdesk built around email-centric ticket queues. Pylon is purpose-built for B2B support, where the primary conversation happens in shared Slack channels and Microsoft Teams — not email portals. (support.usepylon.com)
Teams migrate for three reasons:
- Channel alignment. B2B customers live in Slack Connect and Teams. Pylon integrates natively with these channels so customers never leave their workspace. Zendesk treats chat as a secondary channel bolted onto an email-first architecture.
- Account-centric support model. Pylon ties every Issue to an Account, giving agents full customer context — health scores, CRM data, renewal risk — directly in the support view. Zendesk can achieve this, but it requires significant customization and app marketplace add-ons. Pylon also supports partner accounts and subaccounts for multi-entity customers. (docs.usepylon.com)
- Cost and complexity reduction. Zendesk pricing at scale (Enterprise at $169+/agent/month, Enterprise Plus at $209+/agent/month) plus add-ons for advanced API access, AI features, and custom objects can exceed what growing B2B teams want to pay for features they only partially use.
The trade-off: Pylon's data model is simpler than Zendesk's. You gain speed and B2B focus. You lose some of Zendesk's deep configurability, especially around custom objects and enterprise reporting. Pylon can sync account and contact attributes with a CRM, but campaign logic, opportunities, and most revenue data should stay in the CRM or marketing stack.
Zendesk to Pylon Data Mapping: What Goes Where
This is the core of your migration plan. Get the object mapping wrong and you'll orphan tickets, lose account associations, or silently drop fields.
Core Object Mapping
| Zendesk Object | Pylon Equivalent | Notes |
|---|---|---|
| Organizations | Accounts | Map organization.name → account.name, organization.domain_names → account.domains. Store organization.id in account.external_ids for reruns. Create first to establish relationships. |
| Users (end-users) | Contacts | Map user.email → contact.email, user.name → contact.name. Link to Accounts via account_id. Store user.id in contact.external_ids. |
| Users (agents) | Users | Ensure agent email addresses match exactly to preserve historical attribution. Roles must be recreated manually. |
| Tickets | Issues | Map ticket.subject → issue.title, ticket.description → issue.body_html. Link via account_id and requester_id. |
| Ticket Comments | Messages | Each comment becomes a Pylon Message on the parent Issue. Distinguish internal notes from public replies. |
| Groups | Teams | Recreate agent groups as Pylon Teams. Map ticket.group_id → issue.team_id. |
| Tags | Tags | Direct 1:1 mapping. Both platforms treat tags as string arrays. |
| Custom Ticket Fields | Custom Fields (on Issues) | Pylon supports String, Boolean, Date, and Select types. Multi-select and regex fields from Zendesk need transformation. |
| Custom Org Fields | Custom Fields (on Accounts) | Same type constraints apply. |
| Knowledge Base Articles | KB Articles / Collections | Map article.title, article.body → Pylon KB article. Zendesk Sections/Categories map to Pylon Collections. |
| Macros | Macros | Pylon supports macros, but the action set differs. Rebuild rather than migrate. |
| Triggers / Automations | Triggers | Not directly portable. Pylon's trigger system is structured differently. Rebuild from scratch. |
| Custom Objects | Custom Fields or CRM sync | Pylon does not have Zendesk-style custom objects with relationships. Flatten relational data into Custom Fields on Accounts/Issues or sync through a connected CRM. |
Status and Priority Mapping
Zendesk and Pylon use different status and priority vocabularies. Map these explicitly:
- Zendesk
pending→ Pylonwaiting_on_customer - Zendesk
hold→ Pylonon_hold - Zendesk
solvedandclosed→ Pylonclosed - Zendesk
newandopen→ rule-based split betweennewandwaiting_on_you - Zendesk priority
normal→ Pylonmedium
Pylon's Zendesk migration docs note that imported historical tickets land in a closed state. That's fine for reference history, but any open work needs a separate cutover plan. (support.usepylon.com) (developer.zendesk.com)
Handling Custom Objects and Legacy Fields
Do not attempt a 1:1 migration of every Zendesk field. Over time, Zendesk instances accumulate dead fields, abandoned tags, and deprecated macros. Conduct a strict audit before migration. Identify which custom fields actually drive reporting or workflow automation, then flatten necessary custom object data into Pylon Custom Fields — for example, extracting a Contract Renewal Date from a Zendesk custom object and attaching it as a Date field on the Pylon Account.
Pylon does not document issue-level external IDs in the public API, so many teams store the Zendesk ticket ID in a custom field like legacy_ticket_id for cross-referencing. Use external identifiers aggressively on Accounts and Contacts — Pylon supports external_ids on both. (docs.usepylon.com)
Zendesk Custom Objects with multi-level relationships (e.g., Asset → Contract → Ticket) cannot be natively represented in Pylon. You must either flatten these into Custom Fields, store them in your CRM and sync to Pylon, or accept data loss. Plan this mapping before any code is written.
API Rate Limits and Extraction Constraints
The rate-limit asymmetry between Zendesk and Pylon is the single biggest technical constraint in this migration. Extraction is fast. Loading is slow.
Zendesk Extraction Limits
Zendesk API rate limits depend on your plan:
| Zendesk Plan | Rate Limit |
|---|---|
| Team | 200 req/min |
| Growth / Professional | 400 req/min |
| Enterprise | 700 req/min |
| Enterprise Plus | 2,500 req/min |
The Incremental Export endpoints — the right choice for bulk ticket extraction — have their own limit: 10 requests per minute, or 30 with the High Volume API add-on. Use cursor-based pagination; offset pagination is deprecated for large datasets. (developer.zendesk.com)
Pylon Loading Limits
Pylon's API rate limits are significantly tighter:
| Pylon Endpoint | Rate Limit |
|---|---|
GET /issues (list) |
10 req/min |
POST /issues (create) |
10 req/min |
GET /issues/{id} |
60 req/min |
GET /accounts, POST /accounts |
60 req/min |
GET /contacts, POST /contacts |
60 req/min |
POST /attachments (upload) |
10 req/min |
GET /users |
60 req/min |
| Knowledge Base endpoints | 60 req/min |
The GET /issues endpoint enforces a maximum time range of 30 days per request. If you have 3 years of ticket history, you need at minimum 36 separate windowed requests just to enumerate your issues. This complicates validation significantly — you cannot query "all issues from 2023" in a single pass.
The math for bulk issue creation: At 10 creates per minute, migrating 10,000 tickets takes approximately 17 hours of continuous API calls. For 50,000 tickets, you're looking at 3.5 days. For 100,000 tickets, expect over 7 days of continuous runtime — excluding users, accounts, comments, and attachments. That's assuming zero errors and zero retries.
Pylon uses Bearer token authentication. Only Admin users can create API tokens. Generate a dedicated migration token and rotate it post-migration. (docs.usepylon.com)
Migration Approaches: CSV, API, Tools, and Managed Services
There are several paths to execute this migration. Each carries different cost, risk, and control trade-offs. If you're still deciding whether to build or outsource, read In-House vs. Outsourced Data Migration: A Realistic Cost & Risk Analysis.
1. Native CSV Export/Import
How it works: Export Zendesk data to CSV or JSON, clean the data, and manually load into Pylon or use as staging input for an API load.
When to use it: Very small migrations, audit prep, or shallow moves where you don't need full ticket history.
Pros: Fast start, zero code, useful for mapping workshops.
Cons: Zendesk CSV exports omit deleted tickets, ticket comments, and ticket descriptions. View-based CSV exports only work from unfiltered views, have a 10-minute cooldown, and large views can fail. CSV timestamps use the account time zone, not UTC. Pylon's official docs do not present CSV as the primary path for historical issue migration. (support.zendesk.com)
Complexity: Low
2. Custom API-Based ETL Pipeline (DIY)
How it works: Your engineering team writes scripts that extract from Zendesk's REST/Incremental Export APIs, transform the data to match Pylon's schema, and load via Pylon's REST API with rate-limit handling.
When to use it: You have a dedicated engineer who can spend 2–4 weeks on the project, your dataset is under 10K tickets, and you need full control over transformation logic.
Pros: Full control over mapping, retries, and edge cases. No vendor dependency.
Cons: You own every edge case. Rate-limit backoff, pagination, retry logic, relationship ordering (Accounts before Contacts before Issues) — all on you. A single missed dependency order orphans records silently. Review Helpdesk Migration Failed? The Engineer's Rescue Guide for common pitfalls.
Complexity: High
3. Help Desk Migration (HDM) — Self-Serve Tool
How it works: HDM is a no-code migration platform that Pylon officially partners with. You connect Zendesk as Source and Pylon as Target in their Migration Wizard, map fields via UI, run a free demo (20 tickets), then execute the full migration. (help-desk-migration.com)
When to use it: Standard data structures, limited custom objects, and you want a fixed, trackable timeline without writing code.
Pros: UI-based mapping, free demo runs, delta migration support, officially supported by Pylon.
Cons: Less control over transformation logic. Complex custom object flattening may require their specialists. Pricing scales with record volume. The load still sits on top of Pylon's endpoint limits.
Complexity: Low to Medium
4. Middleware (Zapier / Make)
How it works: Trigger on new Zendesk events or schedules, transform fields, and create or update Pylon records.
When to use it: Forward sync after cutover. Never for historical migrations.
Pros: Low code, quick to set up.
Cons: Poor fit for bulk history. Cannot handle complex data mapping or relationship rebuilds. Easy to create duplicate records when retries appear. Zapier's current Pylon integration is action-oriented (Create Account, Create Call Recording, etc.) and the issue endpoints are tightly rate-limited. (zapier.com)
Complexity: Low
5. Managed Migration Service
How it works: A migration engineering team handles the full pipeline — audit, extraction, transformation, loading, validation — as a managed project.
When to use it: Dataset exceeds 25K tickets, complex custom objects or multi-level relationships, engineering team can't afford 2–4 weeks of migration work, or you've already tried and failed with a DIY approach.
Pros: Rate-limit handling built in, relationship integrity guaranteed, zero engineering distraction.
Cons: External dependency, cost.
Complexity: Low (for your team)
Approach Comparison
| Criteria | CSV | DIY ETL | HDM Tool | Middleware | Managed Service |
|---|---|---|---|---|---|
| Engineering effort | None | High (2–4 weeks) | Low (hours) | Low | None |
| History fidelity | Low | Medium–High | Medium–High | Low | High |
| Custom object handling | None | Full control | Limited | None | Full control |
| Rate-limit management | N/A | You build it | Handled | Brittle | Handled |
| Ongoing sync | No | Limited | Delta support | Yes | Yes |
| Best for | <1K tickets | <10K, simple schema | <25K, standard fields | Forward sync only | >25K, complex data |
Pre-Migration Planning and Audit
Before touching any API, audit your Zendesk instance. Migrating garbage into a new system just creates faster garbage. Use our Zendesk Migration Checklist as a starting framework.
Data audit checklist:
- Count Organizations, Users (end-users + agents), Tickets (by status), Ticket Comments, Tags, Custom Fields, Knowledge Base articles
- Identify and purge: suspended/deleted tickets, spam, test organizations, inactive agents, unused custom fields
- Document all active Zendesk Custom Objects and their relationships
- Export and review a sample of 100 tickets to validate field completeness
- Map every Zendesk trigger, automation, and macro — decide which to rebuild in Pylon vs. retire
- Identify CRM-owned data that should not become native Pylon objects (leads, opportunities, sales tasks)
- Document channel cutover points for email, Slack, chat widget, portal, and API intake
Pylon's CRM docs warn that blank CRM values can clear existing Pylon field values. Define field ownership — what lives natively in Pylon vs. what syncs from the CRM — before enabling any sync. (support.usepylon.com)
Freeze configuration. Stop changing Zendesk fields, groups, forms, and macros while mapping is underway. Pylon says destination setup — creating custom fields, teams, and workflow objects — is your responsibility, so build the target schema first. (support.usepylon.com)
Choose your migration strategy:
- Big bang: Migrate everything in a single weekend cutover. Works for teams under 20 agents with <50K tickets. Requires a hard freeze on Zendesk.
- Phased by channel: Migrate Slack/Teams sources first (net-new in Pylon), then email, then chat. This is Pylon's recommended approach — it minimizes dual-system time for each channel. (support.usepylon.com)
- Delta migration: Run the bulk migration, continue using Zendesk for new tickets, then run a delta sync to catch records created during the transition window.
Step-by-Step Zendesk to Pylon Migration Process
Phase 1: Extract from Zendesk
Use the Incremental Exports API for tickets and the standard list endpoints for organizations and users. Do not rely on view-based CSV if you need comments or descriptions.
import requests
import time
ZD_SUBDOMAIN = "yourcompany"
ZD_TOKEN = "your_api_token"
ZD_EMAIL = "admin@yourcompany.com"
def extract_tickets_incremental(start_time_unix):
url = f"https://{ZD_SUBDOMAIN}.zendesk.com/api/v2/incremental/tickets/cursor.json?start_time={start_time_unix}"
all_tickets = []
while url:
resp = requests.get(url, auth=(f"{ZD_EMAIL}/token", ZD_TOKEN))
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 60))
time.sleep(wait)
continue
data = resp.json()
all_tickets.extend(data.get("tickets", []))
url = data.get("after_url") if not data.get("end_of_stream") else None
return all_ticketsIf you need audit-grade history, pull ticket audits or ticket events alongside ticket records. For detailed extraction methods, see How to Export Tickets from Zendesk: The Complete Step-by-Step Guide.
Phase 2: Transform
Map extracted Zendesk records to Pylon's schema. Build ID lookup tables: Zendesk Org ID → Pylon Account ID, Zendesk User ID → Pylon Contact ID.
def transform_ticket_to_issue(ticket, account_lookup, contact_lookup):
return {
"title": ticket["subject"] or f"Legacy ticket {ticket['id']}",
"body_html": ticket["description"],
"account_id": account_lookup.get(ticket.get("organization_id")),
"requester_email": ticket.get("requester", {}).get("email"),
"tags": ticket.get("tags", []),
"priority": map_priority(ticket.get("priority")),
"created_at": ticket["created_at"],
"custom_fields": transform_custom_fields(ticket.get("custom_fields", [])),
"destination_metadata": {"destination": "internal"}
}Key transformation steps:
- Convert Zendesk HTML comments to Pylon's
body_htmlformat. Zendesk descriptions can contain complex HTML, embedded images, and formatting — test rendering fidelity with a representative sample. - Remap Zendesk
organization_idto the newly created Pylonaccount_id. - Map status and priority values using the status mapping table above.
- Download Zendesk attachments to temporary secure storage (S3, GCS). Zendesk attachment URLs require authentication and may expire shortly after extraction.
Always set destination_metadata.destination to "internal" when creating historical issues via API. If you omit this or set it to "email" or "slack", Pylon will send a live notification to your customer for every migrated ticket.
Phase 3: Load into Pylon
Load in dependency order: Accounts → Contacts → Issues → Messages.
import requests
import time
PYLON_TOKEN = "your_pylon_bearer_token"
PYLON_BASE = "https://api.usepylon.com"
def create_pylon_issue(issue_payload, max_retries=5):
for attempt in range(max_retries):
resp = requests.post(
f"{PYLON_BASE}/issues",
headers={
"Authorization": f"Bearer {PYLON_TOKEN}",
"Content-Type": "application/json"
},
json=issue_payload
)
if resp.status_code == 429:
wait = min(2 ** attempt * 6, 120) # Exponential backoff, max 2 min
time.sleep(wait)
continue
if resp.status_code in (200, 201):
return resp.json()
resp.raise_for_status()
raise Exception(f"Failed after {max_retries} retries")At 10 requests per minute, your baseline delay is ~6 seconds per request. Build idempotency into every write by keying reruns off stored source IDs. Persist Pylon request_id values from error responses for debugging. Use a dead-letter queue for 429s and 5xx errors that exhaust retries.
Phase 4: Validate
Validation is complicated by Pylon's 30-day query constraint. You must iterate through history in 30-day windows and aggregate counts.
Sampling strategy:
- Compare total counts per object type (Accounts, Contacts, Issues) against Zendesk source counts — target within 0.1% tolerance.
- Random-sample 5% of Issues and verify all mapped fields against Zendesk source data.
- Specifically validate the 10 most recent and 10 oldest tickets (boundary conditions).
- Confirm every Issue with an Account has the correct Account association.
- Verify Custom Field values on at least 20 records.
- Check tag arrays match between source and target.
UAT process: Have 2–3 agents work in Pylon for 48 hours before fully decommissioning Zendesk. They should look up specific customer histories, verify ticket threads are complete, and confirm Custom Field data is usable.
Rollback plan: Keep Zendesk active (read-only) for 30 days post-migration. Do not delete any Zendesk data until UAT is signed off by the support team lead.
Edge Cases That Break Migrations
Duplicate contacts. Zendesk allows multiple users with the same email in certain configurations. Pylon deduplicates contacts by email — the second create will fail or overwrite.
Suspended and deleted tickets. Zendesk's Incremental Export includes suspended tickets by default. Filter these out during transformation unless you explicitly want them.
Attachments. Zendesk attachment URLs require authenticated session handling. If you simply push the text URLs to Pylon, they'll dead-link when you close your Zendesk account. Your pipeline must download each file, upload to temporary storage, generate accessible URLs, and pass those to Pylon. Pylon's POST /issues accepts attachment_urls, but those URLs must be publicly accessible or pre-uploaded. Pylon attachment upload is rate-limited to 10 req/min. (developer.zendesk.com)
Custom field type mismatches. Zendesk supports regex-validated fields, multi-line text, and decimal fields. Pylon Custom Fields support String, Boolean, Date, and Select types only. Decimals need conversion to strings. Multi-select fields need restructuring.
Multi-level relationships. Zendesk Custom Objects can model Asset → Contract → Ticket chains. Pylon's schema is flatter. Parent/child tickets usually need to be merged or linked via custom fields. These relational structures either flatten into Custom Fields or live in your CRM. (developer.zendesk.com)
Author attribution. If you don't map agent IDs correctly, every historical ticket will appear as if it was created and solved by the API Admin who ran the script.
CRM sync conflicts. Pylon does not automatically import all CRM contacts — contacts are created when there is activity. Blank CRM values can overwrite Pylon field values. Define field ownership before enabling sync. (support.usepylon.com)
Domain hygiene. Pylon account matching relies heavily on domain data. Dirty or missing domains in your Zendesk Organizations will break account matching in Pylon.
Post-Migration: What to Rebuild
Several Zendesk constructs don't migrate — they must be rebuilt natively in Pylon:
- Triggers and automations: Pylon's trigger system uses different conditions and actions. Rebuild from your documented Zendesk trigger list. (docs.usepylon.com)
- SLA policies: Recreate in Pylon's SLA configuration.
- Views: Pylon Issue Views are structured differently from Zendesk views. Recreate your agent queue views.
- Macros: Pylon supports macros, but action sets differ. Rebuild.
- Integrations: Re-connect CRM (Salesforce, HubSpot), engineering tools (Linear, Jira), and other third-party apps through Pylon's Apps Directory.
- Chat widget: Swap the Zendesk Web Widget code for Pylon's Chat Widget. This requires a front-end deployment.
- Email routing: Configure email cutover carefully. Pylon's email migration guide warns that overlap can create duplicate tickets. (support.usepylon.com)
Train agents on Pylon's issue model, especially the difference between general issues and formal tickets in Slack-heavy workflows.
Best Practices
- Backup first. Always.
- Run at least one test migration. Use HDM's free 20-ticket demo or load a small batch via API.
- Validate incrementally, not only at the end.
- Store legacy IDs everywhere. Zendesk Org ID on Pylon Account
external_ids. Zendesk User ID on Pylon Contactexternal_ids. Zendesk Ticket ID in an Issue custom field. - Keep CRM-owned data in the CRM. Sync only agent-facing fields into Pylon.
- Make every write idempotent. Key reruns off stored source IDs so partial loads can resume without duplicates.
- If a first pass already failed, stop layering manual fixes and use a structured recovery approach — see Helpdesk Migration Failed? The Engineer's Rescue Guide.
Why Choose ClonePartner for Your Pylon Migration
Pylon's 10 req/min limit on the Issues endpoint means a 50K-ticket migration takes days of continuous API work. That's days of rate-limit handling, retry logic, error recovery, and relationship integrity checks that your engineering team could spend on product work.
ClonePartner has completed 1,200+ data migrations, including complex helpdesk moves where the target API is the bottleneck — not the source. Here's what that means for a Zendesk-to-Pylon project:
- Rate-limit orchestration built in. We handle exponential backoff, parallel token management, and windowed batching so you don't build and maintain that infrastructure.
- Custom object flattening. We map Zendesk Custom Objects into Pylon Custom Fields or CRM-synced fields, preserving data integrity where native parity doesn't exist.
- Zero downtime. Zendesk stays live throughout. We run the bulk migration, then a delta sync to catch any tickets created during the transition window.
- Engineer-to-engineer accountability. No black-box tools. You get a dedicated migration engineer who understands both APIs and communicates directly with your team.
Your engineering team should be building your product, not writing throwaway ETL scripts for helpdesk APIs.
Frequently Asked Questions
- What are Pylon's API rate limits for migration?
- Pylon's GET /issues and POST /issues endpoints are limited to 10 requests per minute, with a maximum 30-day time range per request on queries. Other endpoints like Accounts, Contacts, and Users allow 60 requests per minute. These limits make bulk historical migrations slow without careful batching and backoff logic.
- How do Zendesk objects map to Pylon?
- The standard mapping is Organizations → Accounts, Users (end-users) → Contacts, Tickets → Issues, Ticket Comments → Messages, Groups → Teams, and KB Categories → Collections. Tags transfer directly. Custom Objects have no native Pylon equivalent and must be flattened into Custom Fields or synced via CRM.
- How long does a Zendesk to Pylon migration take?
- At Pylon's rate of 10 issue creates per minute, migrating 10,000 tickets takes roughly 17 hours of continuous API calls. A 50,000-ticket migration can take 3–4 days. That's assuming zero errors and zero retries, and excludes accounts, contacts, comments, and attachments.
- Can I migrate Zendesk Custom Objects to Pylon?
- Not directly. Pylon does not support Zendesk-style custom objects with inter-object relationships. You must flatten relational custom object data into Pylon Custom Fields (String, Boolean, Date, Select types) on Accounts or Issues, or store the data in a connected CRM and sync it to Pylon.
- Can I use CSV or Zapier to migrate Zendesk tickets to Pylon?
- CSV only works for very small, shallow moves — Zendesk CSV exports omit ticket comments and descriptions. Zapier and similar middleware are suitable for forward sync after cutover but fail for bulk historical migrations due to rate limits, brittle retries, and inability to handle complex data mapping.
