How to Migrate from Pylon to Zendesk: The 2026 Technical Guide
Step-by-step guide to migrating Pylon Issues to Zendesk Tickets. Covers API rate limits, field mapping, Slack thread handling, and Zendesk AI accuracy.
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
How to Migrate from Pylon to Zendesk: The 2026 Technical Guide
TL;DR — Pylon to Zendesk Migration
Migrating from Pylon to Zendesk is a moderate-complexity data-model translation problem. Pylon's core object is the Issue — a Slack-native conversational thread that bundles messages, internal notes, and file attachments. Zendesk's core object is the Ticket — a structured lifecycle record with comments, custom fields, SLAs, and triggers. The realistic timeline is 5–10 business days depending on volume and custom field complexity. The single biggest risk is importing noisy Slack thread history that degrades Zendesk Intelligent Triage accuracy. You must use the Zendesk Ticket Import API (/api/v2/imports/tickets) to preserve historical timestamps — the standard ticket creation endpoint overwrites dates with the current server time. Teams with fewer than 5,000 issues and simple fields can self-serve with the API. Anything above that — or with attachment-heavy Slack threads, complex field mappings, and AI accuracy requirements — benefits from a managed migration service.
What Is a Pylon to Zendesk Migration?
A Pylon to Zendesk migration is the process of extracting Issues, Accounts, Contacts, Messages, Tags, Custom Fields, and Knowledge Base articles from Pylon's Slack-native helpdesk and loading them into Zendesk as Tickets, Organizations, Users, Comments, Tags, Custom Fields, and Help Center articles.
Why teams make this move:
- Channel expansion beyond Slack. Pylon is built for B2B teams running support inside Slack Connect channels. When a company needs email, web form, phone, and social media as first-class channels, Zendesk's omnichannel architecture is the natural next step.
- Enterprise compliance and reporting. Zendesk offers native SLA management, Explore analytics, HIPAA-eligible configurations, and a mature marketplace with 1,500+ integrations — capabilities Pylon is still building.
- AI and automation maturity. Zendesk's Intelligent Triage, AI Agents, and trigger/automation engine give operations teams more control over routing, deflection, and self-service at scale.
The architectural differences that make this migration non-trivial:
- Conversational vs. lifecycle model. Pylon Issues are conversation-first: messages flow in from Slack threads, and the Issue is a wrapper around that conversation. Zendesk Tickets are lifecycle-first: every ticket moves through defined statuses (New → Open → Pending → Solved → Closed) with triggers firing at each transition.
- Channel metadata. Pylon Issues carry
destination_metadatathat records the Slack channel ID, workspace, and whether the conversation originated via Slack Connect, email, or chat widget. Zendesk has no equivalent field — this context must be flattened into tags or custom fields. - Threading structure. A single Pylon Issue can contain multiple named threads (e.g., a customer-facing Slack thread and an internal side-conversation thread). Zendesk Comments are a flat chronological list with a
public/privateflag. Multi-thread conversations must be merged and annotated during transformation.
For teams evaluating the reverse path, see our guide on Zendesk to Pylon migration.
How Pylon's Data Model Maps to Zendesk
If you get the data model wrong, historical context is lost. Pylon and Zendesk handle relationships differently, and you must map Slack-native concepts to Zendesk's structured architecture.
Object mapping
| Pylon Object | Zendesk Object | Notes / Caveats |
|---|---|---|
| Issue | Ticket | States map: new→New, waiting_on_you→Open, waiting_on_customer→Pending, on_hold→On-hold, closed→Solved/Closed. Custom statuses require Zendesk custom ticket statuses. |
| Account | Organization | Pylon Account types (customer, internal, community, partner) map to Organization tags or a custom dropdown. domain maps to Zendesk domain_names for automatic user association. |
| Contact | User (end-user) | Pylon contacts are often tied to Slack IDs. You must extract the email address to map to Zendesk Users. Portal roles (no_access, member, admin) have no direct Zendesk equivalent — use tags or user fields. |
| User (agent) | User (agent) | Pylon user.role_id maps to Zendesk agent roles. Recreate agents before tickets to preserve comment authors and assignees. |
| Message (public) | Comment (public: true) |
Pylon message_html maps to Zendesk html_body. timestamp maps to created_at (only via Import API). |
| Message (private / internal note) | Comment (public: false) |
Pylon is_private: true → Zendesk private comment. Thread name context is lost — prepend it to the comment body. |
| Team | Group | Pylon Teams map to Zendesk Groups. Agent membership must be set up before ticket import. |
| Tag | Tag | Direct 1:1 mapping. |
| Custom Field | Custom Ticket/Org/User Field | Pylon custom field slug must match a pre-created Zendesk custom field key. Picklist values must be pre-loaded. |
| Issue followers | Followers / collaborators | Only migrate after the referenced Zendesk users exist. |
| Knowledge Base Article | Help Center Article | Requires Zendesk Guide. No bulk import API — use POST /api/v2/help_center/sections/{id}/articles. This is a separate workstream from ticket migration. |
| Attachment (file_urls) | Attachment (upload token) | Pylon returns file URLs. Each must be downloaded, then uploaded to Zendesk via POST /api/v2/uploads, and the resulting token attached to the comment. |
| Feature Request | No equivalent | Flatten to tickets with a tag like feature_request, or skip. |
| Slack channel metadata | No equivalent | Store as a custom text field or internal comment for audit trail. |
| Survey / CSAT response | Satisfaction Rating | Zendesk Satisfaction Ratings have a fixed schema (good/bad + comment). Pylon surveys with multi-question formats require lossy transformation. |
Field-level transformations
Field types are where DIY builds fail quietly. Zendesk enforces stricter type validation than Pylon.
| Pylon Field Shape | Zendesk Target | Transform Rule | Gotcha |
|---|---|---|---|
value text |
text / textarea | Direct map | Usually safe. |
value number |
integer / decimal | Normalize blanks and placeholders | Zendesk type enforcement is stricter. |
value single-select |
tagger / dropdown | Map to Zendesk option value, not just label |
Zendesk stores the tag value, not the display label. Duplicate option values can make fields uneditable. |
values multi-select |
multiselect | Pre-create target options, then load stored values | Duplicate option values can make fields uneditable. |
| boolean | checkbox | Normalize true/false before load | Do not trust display labels. |
| Pylon status enum | Zendesk status | new→New, waiting_on_you→Open, waiting_on_customer→Pending, on_hold→On-hold, closed→Solved |
Zendesk distinguishes between Solved (can be reopened) and Closed (archived). |
Biggest modeling mistake: treating Pylon Slack threads like plain ticket descriptions. If you only move the issue title and body_html, you lose the conversation agents actually worked from. You must extract per-issue messages via the Messages API and map them as individual Zendesk Comments.
Relationship dependencies — load order matters
- Organizations (from Accounts)
- Users — both agents and end-users (from Users and Contacts)
- Tickets with Comments (from Issues and Messages)
- Attachments (uploaded per-comment)
Breaking this order creates orphaned tickets and broken requester_id references. If you load tickets before users, tickets will default to the API admin, destroying historical ownership.
How to Export Data from Pylon
Pylon does not offer a one-click full data export. CSV exports from views are column-scoped and miss messages, threads, and attachments. Extraction requires the Pylon REST API at api.usepylon.com. Only Admin users can create API tokens, and auth is Bearer token-based. Pylon does not currently support OAuth for API access — all automation relies on static Bearer tokens, which do not expire automatically but can be revoked by admins in the Pylon settings panel.
Key endpoints and rate limits
| Endpoint | Rate Limit | Pagination | Notes |
|---|---|---|---|
GET /accounts |
60 req/min | Cursor-based, max 1,000/page | Returns channels, domains, custom fields, tags |
GET /contacts |
60 req/min | Cursor-based, max 1,000/page | Includes account association |
GET /issues |
10 req/min | Cursor-based, requires start_time/end_time (max 30-day window) |
The bottleneck. You must paginate in 30-day windows across your full history. |
POST /issues/search |
10 req/min | Cursor-based, up to 1,000/page | Filter by status, assignee, account, or custom field |
GET /issues/{id}/messages |
20 req/min | Cursor-based | Must be called per-issue — there is no bulk messages endpoint |
GET /issues/{id}/threads |
— | Returns all threads for an issue | Use to identify multi-thread conversations |
GET /users |
60 req/min | Cursor-based | Agent/internal user list |
[For engineering] Throughput math: At 10 req/min for Issues with a page size of 1,000, you can extract ~10,000 issues per minute. But Messages at 20 req/min, fetched per-issue, means a 10,000-issue dataset with an average of 8 messages per issue requires ~500 message-fetch calls. At 20/min, that's 25 minutes just for messages — before accounting for pagination within each issue. Budget 2–6 hours for a full extraction of 10,000 issues with messages and attachments.
import requests
import time
PYLON_TOKEN = "YOUR_PYLON_TOKEN"
BASE = "https://api.usepylon.com"
def fetch_issues(start, end, cursor=None):
params = {"start_time": start, "end_time": end, "limit": 1000}
if cursor:
params["cursor"] = cursor
resp = requests.get(f"{BASE}/issues",
headers={"Authorization": f"Bearer {PYLON_TOKEN}"},
params=params)
if resp.status_code == 429:
time.sleep(60) # Pylon issues rate limit: 10 req/min
return fetch_issues(start, end, cursor)
return resp.json()Treat Pylon CSV exports as a reconciliation check, not your source of truth. Messages and threads live on separate API endpoints that CSV exports do not cover. CSV-only migrations lose conversation context.
How the Zendesk Ticket Import API Works
The Zendesk Ticket Import API is the only way to preserve historical timestamps during migration. The standard Ticket Creation API (POST /api/v2/tickets) overwrites created_at with the current server time and fires triggers — including sending email notifications to every customer for every historical ticket. The Import API allows setting created_at, updated_at, and solved_at on both tickets and comments while suppressing notifications.
Two endpoints:
- Single:
POST /api/v2/imports/tickets— imports one ticket with its full comment history - Batch:
POST /api/v2/imports/tickets/create_many— imports up to 100 tickets per request, returns ajob_statusfor async processing
Key constraints:
- Triggers do not fire on imported tickets (unless the ticket is later updated).
- Zendesk metrics and SLAs are not calculated for imported tickets. Tag imported tickets with
pylon_importand exclude them from SLA reports. - Requesters must be active users. Suspended or deleted users cause import failures. Create placeholder users before import.
- Comments with empty bodies are rejected. Add placeholder text like
(No message body)for Slack reactions or file-only messages. - The
archive_immediatelyparameter sends closed tickets directly to the archive, bypassing the ticket lifecycle. Zendesk recommends this for imports exceeding 750,000 tickets. - No notifications are sent to requesters or CC'd users on imported tickets.
- A Zendesk ticket can contain up to 5,000 comments, and a single comment body is limited to 64 KB before truncation. Both limits matter for Slack-heavy Pylon history.
- Job concurrency: Zendesk allows up to 30 queued or running background jobs before returning
TooManyJobs.
Zendesk API rate limits by plan
| Zendesk Plan | Rate Limit (req/min) |
|---|---|
| Suite Team | 200 |
| Suite Growth | 400 |
| Suite Professional | 400 |
| Suite Enterprise | 700 |
| Suite Enterprise Plus | 2,500 |
| High Volume API Add-on | 2,500 (replaces base limit) |
Zendesk also enforces 100 ticket updates per minute per account as a separate cap, and returns 429 with a Retry-After header when exceeded.
Import throughput estimate
Using create_many with 100 tickets per batch on a Suite Enterprise plan (700 req/min), each batch counts as 1 API request. The theoretical ceiling is 70,000 tickets per minute — but Zendesk queues these as background jobs, and real-world throughput depends on job processing time.
The practical bottleneck is attachments. Every attachment requires a separate call to /api/v2/uploads to generate a token before the ticket can be created. If an average ticket has 2 attachments, your actual throughput drops dramatically. Expect 5,000–10,000 tickets per hour as a realistic sustained rate for an import with comments and attachments.
Plan your cutover window accordingly.
For a deeper look at Zendesk data export and import methods, see how to export tickets from Zendesk.
How Bulk Import Affects Zendesk AI Accuracy
This is the most commonly overlooked risk in any Pylon-to-Zendesk migration. Zendesk's Intelligent Triage automatically classifies incoming tickets by intent, sentiment, and language using AI models trained on your ticket history. Importing a large volume of low-quality Pylon data — unresolved Slack chatter, one-word messages, GIFs, internal noise — dilutes the training signal and can push triage accuracy below usable thresholds.
The AI-first migration strategy:
- Filter before import. Only migrate Pylon Issues with a
closedstate and at least 2 messages. Exclude issues that were auto-closed without resolution, have no customer reply, or consist only of Slack reactions. - Tag quality tiers. Mark imported tickets with tags like
resolved_importvs.unresolved_importso you can exclude low-quality records from Intelligent Triage training. - Import resolved tickets first. Give Zendesk's AI 2–4 weeks to learn from clean, resolved data before importing the long tail of open or ambiguous issues.
- Monitor Intelligent Triage confidence scores. After import, check the Triage Performance dashboard for accuracy drops. Zendesk requires at least 1,000 solved tickets with intent labels to produce reliable predictions, and confidence scores below 0.7 typically indicate the model needs more clean training data.
- Quarantine low-confidence predictions. For the first 30 days post-migration, route tickets with triage confidence below 0.5 to a human review queue rather than automated workflows. This prevents cascading misroutes while the model stabilizes.
For more on how Zendesk's AI features interact with migrated data, see Zendesk AI Agents Migration 2026: What Breaks, What Changes.
Step-by-Step Migration Process
Phase 1: Discovery and field mapping (Days 1–2)
Audit Pylon data. Map Slack channels to Zendesk tags. Define exact field translations. Pre-create all custom fields, ticket forms, groups, and custom statuses in Zendesk before loading any records — imported tickets that reference missing structures will fail or lose data.
Deliverable: A signed-off mapping document that covers every Pylon custom field, its Zendesk target field, the value transformation rule, and the handling decision for any unmappable data (discard, flatten to tag, or store in internal comment).
Phase 2: Extract from Pylon (Days 2–3)
Pull all Accounts, Contacts, Users, Teams, and Issues from the Pylon API. Fetch messages and threads per-issue. Download all attachments from Pylon file_urls before starting the Zendesk load phase — Slack attachment URLs expire (typically within 24–72 hours for shared channel files), and if your script attempts to download an attachment using an expired URL, it will fail silently or return a corrupted file.
Build a translation table mapping slack_user_id to email_address before pushing to Zendesk. For Slack Connect channels, some external user emails may not be available through Pylon — you may need to query Slack's users.info API to resolve them.
Phase 3: Transform (Days 3–4)
- Map Pylon
stateto Zendeskstatus - Convert
message_htmlto Zendeskhtml_body - Flatten multi-thread conversations into a single chronological comment stream, prepending thread names as headers (e.g.,
[via Slack],[via Email]) - Upload attachments to Zendesk via
POST /api/v2/uploadsand capture tokens - Resolve
requester_idandassignee_idby matching Pylon Contact/User emails to Zendesk User IDs - Convert Slack user mentions (
<@U12345>) to display names; handle emoji reactions as text or discard - Store the Pylon issue ID in
external_idfor traceability - Filter noisy data per the AI-first strategy
- Validate all enum/picklist values against pre-created Zendesk field options — log any unmapped values for manual review
Phase 4: Load into Zendesk (Days 4–6)
Load in strict order: Organizations → Users → Tickets.
import requests
import time
import hashlib
ZD_URL = "https://yoursubdomain.zendesk.com/api/v2/imports/tickets/create_many"
ZD_AUTH = ("[email protected]/token", "ZD_API_TOKEN")
def generate_idempotency_key(batch_data):
"""Deterministic key prevents duplicate imports on retry."""
return hashlib.sha256(str(batch_data).encode()).hexdigest()
def import_batch(tickets):
resp = requests.post(ZD_URL,
json={"tickets": tickets},
auth=ZD_AUTH,
headers={"Content-Type": "application/json",
"Idempotency-Key": generate_idempotency_key(tickets)})
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 60))
time.sleep(wait)
return import_batch(tickets)
if resp.status_code == 503:
# TooManyJobs — back off and wait for queue to drain
time.sleep(120)
return import_batch(tickets)
return resp.json()Example Zendesk Import Payload:
{
"ticket": {
"requester_id": 123456789,
"assignee_id": 987654321,
"subject": "Login failure on production",
"created_at": "2024-02-15T09:00:00Z",
"solved_at": "2024-02-16T10:30:00Z",
"status": "solved",
"tags": ["pylon_import", "resolved_import"],
"external_id": "pylon_issue_abc123",
"comments": [
{
"author_id": 123456789,
"value": "I cannot log into the dashboard.",
"created_at": "2024-02-15T09:00:00Z"
},
{
"author_id": 987654321,
"value": "We have reset your password. Please try again.",
"created_at": "2024-02-16T10:30:00Z",
"public": false,
"uploads": ["token_xyz123"]
}
]
}
}Phase 5: Sandbox testing and UAT (Days 6–8)
Import a 5% sample to a Zendesk Sandbox first. Zendesk Sandbox availability varies by plan: Suite Growth and above include a sandbox environment; Suite Team does not. If you're on a plan without sandbox access, request a trial sandbox from Zendesk support or use a separate test instance.
Support managers verify:
- Slack threads render correctly as comments
- Attachments open and are not corrupted 1KB error files
- Comment ordering and timestamps are accurate
- Public vs. private visibility is correct
- Organization → User → Ticket relationships are intact
- Custom field values display correctly (not raw tag values)
- Intelligent Triage predictions on sample tickets produce confidence scores above 0.5
Phase 6: Production import and delta sync (Days 9–10)
Run the production import. Then pause Pylon ingestion, extract any issues created since your last batch, and perform a final delta sync. Route live traffic to Zendesk.
Rollback plan: Zendesk's bulk delete API (DELETE /api/v2/tickets/destroy_many) can remove imported tickets by the pylon_import tag (up to 100 ticket IDs per request). For Organizations and Users created during migration, use DELETE /api/v2/organizations/destroy_many and DELETE /api/v2/users/destroy_many respectively — but note that deleting users who have been associated with non-imported tickets will fail. Tag all migration-created records consistently (e.g., pylon_import) to enable clean rollback scoping. Always import to a sandbox first.
Migration Timeline and Resourcing
| Phase | Duration | Dependencies |
|---|---|---|
| Discovery and field mapping | 1–2 days | Access to both Pylon admin and Zendesk sandbox |
| Pylon API extraction | 1–2 days | Pylon API token with admin access |
| Transform and data cleaning | 1–2 days | Mapping document sign-off |
| Zendesk sandbox import + UAT | 1–2 days | Zendesk sandbox environment |
| Production import | 1 day | Go-live window approval |
| Validation + buffer | 1 day | QA checklist |
| Total | 5–10 business days |
If you are also rebuilding ticket forms, routing logic, automations, or a Help Center, add 1–2 weeks.
Estimated costs
| Component | Estimated Cost |
|---|---|
| Zendesk Suite Professional (per agent/month) | $115/agent/month (annual billing, 2025 pricing) |
| Zendesk Suite Enterprise (per agent/month) | $169/agent/month (annual billing) |
| High Volume API Add-on | Contact Zendesk sales — typically $100–300/month |
| DIY engineering time (5,000 issues) | 40–80 developer hours |
| DIY engineering time (25,000+ issues with attachments) | 120–200 developer hours |
| Managed migration service (typical range) | $3,000–$15,000 depending on volume, complexity, and attachment density |
These are representative ranges based on publicly available Zendesk pricing and typical project scoping. Actual costs vary by contract terms and migration complexity.
Risk register
| Risk | Likelihood | Mitigation |
|---|---|---|
| Pylon Issues API rate limit (10 req/min) creates extraction bottleneck | High | Paginate in 30-day time windows; budget extraction time accordingly |
| Zendesk 429 errors during batch import | Medium | Implement exponential backoff with Retry-After header; run imports during off-peak hours (UTC 02:00–08:00) |
| Attachment download failures from expired Pylon/Slack URLs | Medium | Download all attachments during extraction phase; retry with fresh Pylon API calls; verify file sizes > 1KB |
| Broken requester references due to missing user records | High | Always load Organizations and Users before Tickets; run a pre-flight check matching all Pylon contacts to Zendesk users |
| Unmapped enum values cause silent field corruption | High | Pre-create all Zendesk custom field options; map by stored value, not display label; log and flag any unmapped values |
| Slack context flattened badly | High | Annotate comments with thread source markers ([via Slack], [via Email]); store Slack IDs in custom fields |
| Intelligent Triage accuracy degradation post-import | Medium | Follow the AI-first strategy: filter, tag quality tiers, import resolved first, monitor confidence scores |
| Duplicate Pylon contacts cause silent user merges in Zendesk | Medium | Deduplicate contacts by email before import; log all merge events |
What Customers Notice During Migration
Downtime: Zero, if planned correctly. Pylon remains live during extraction and transformation. The Zendesk import is additive — it does not lock your Zendesk instance. The recommended approach is to keep Pylon live for active conversations while importing historical data into Zendesk. On cutover day, extract any recent issues, run a final delta import, then redirect customer channels to Zendesk.
History continuity: Customers who previously communicated via Slack will see their conversation history as Zendesk ticket comments. Original timestamps are preserved via the Import API, so the timeline is accurate. Slack-specific formatting — emoji reactions, threaded replies, @-mentions with avatars — is reduced to plain text or HTML.
Notification safety: The Ticket Import API suppresses standard email notifications and triggers during import. If you accidentally use the standard creation API, Zendesk will email every customer a "Ticket Created" receipt for every historical issue. This is a common and expensive mistake.
SLA reset: Imported tickets do not carry Zendesk SLA metrics. Communicate to stakeholders that SLA reporting will only reflect tickets created natively in Zendesk post-migration.
Customer communication plan:
- 1 week before cutover: Email customers explaining the transition. Include the new support email address, Help Center URL, and portal login instructions (if applicable). Example subject line: "Your support experience is moving — here's what changes and what doesn't."
- Day of cutover: Post in active Slack Connect channels directing customers to the new Zendesk channel. Include a direct link to submit tickets.
- 1 week after cutover: Follow up with a brief email confirming the transition is complete and reminding customers of new contact methods.
The trust test is simple: agents should not have to ask customers to re-explain solved cases, and customers should not see private internal context exposed as public comments.
Post-Migration Operational Changes
Migrating data is half the project. The other half is adapting your team's workflows to Zendesk's operational model.
Agent retraining priorities:
- Ticket lifecycle vs. conversation model. Agents accustomed to Pylon's Slack-native workflow need to learn Zendesk's status progression (New → Open → Pending → Solved → Closed) and understand that setting a ticket to "Pending" starts an SLA clock.
- Internal notes vs. Slack side-conversations. In Pylon, agents collaborate via internal Slack threads. In Zendesk, the equivalent is a private comment (internal note) or a Side Conversation (available on Suite Professional and above). Train agents on when to use each.
- Macro and shortcut usage. Zendesk Macros replace Pylon's canned response workflows. Pre-build the 10–15 most common macros based on Pylon usage patterns before cutover. Review Pylon's most-used tags and response templates as a source.
- Views and queue management. Replace Pylon's Slack channel-based triage with Zendesk Views. Create views that mirror prior workflow: e.g., "My Open Tickets," "Waiting on Customer," "Unassigned — [Team Name]."
Internal documentation updates:
- Update escalation playbooks to reference Zendesk ticket URLs instead of Slack thread links.
- Revise on-call runbooks to include Zendesk notification routing and trigger behavior.
- Document the new support email address and Help Center URL in all internal wikis and customer-facing materials.
Budget 2–3 days of agent training and shadowed ticket handling before full cutover.
Edge Cases and Known Limitations
-
Pylon multi-thread Issues. A single Pylon Issue can have a Slack thread, an email thread, and an internal thread simultaneously. Zendesk Comments have no thread concept — all messages become a flat chronological list. Annotate each comment with
[via Slack]or[via Email]to preserve source context. -
Slack emoji reactions and mentions. Slack emoji reactions (
:thumbsup:) used for workflow triggers (e.g., 👀 for "looking into it") have no Zendesk equivalent. These must be translated into internal notes (e.g., "Agent X acknowledged this issue via 👀 reaction at 2024-02-15T09:05:00Z") or discarded. Slack user mentions (<@U12345>) should be converted to display names during transformation. -
Attachment expiration and size limits. Slack attachment URLs expire (typically 24–72 hours for shared channel files). If your script attempts to download a Pylon attachment using an expired URL, it will fail silently or return a corrupted file. Download all attachments during the extraction phase, not during the Zendesk load phase. Zendesk enforces a 50MB per-attachment limit (20MB on Team plans). Verify downloaded file sizes are > 1KB to catch corrupted downloads.
-
Duplicate contacts. Pylon allows multiple contacts with the same email across different accounts. Zendesk enforces unique email addresses. Deduplicate before import or the API will silently merge users, potentially associating tickets with the wrong organization.
-
Zendesk comment and ticket limits. A single ticket can hold up to 5,000 comments, and a single comment body is limited to 64 KB. Very large Slack histories can hit both limits. Split oversized comments and consider whether a single Pylon issue with extreme thread depth should map to multiple Zendesk tickets linked via the
problem_id/incidentrelationship. -
Custom Objects. Pylon supports Custom Objects — Zendesk also supports Custom Objects (Sunshine), but the schemas are entirely different. Zendesk Custom Objects require defining a schema via
POST /api/v2/custom_objects, creating fields, and then loading records viaPOST /api/v2/custom_objects/{key}/records. Relationship fields linking custom objects to tickets must be created separately. Budget 1–3 additional days for Custom Object migration depending on schema complexity. -
Macros, Ticket Forms, and Automation rules. These are configuration objects, not data. They must be manually recreated in Zendesk — there is no automated migration path. Audit Pylon's existing automation rules and map each to its Zendesk equivalent: Triggers (event-based), Automations (time-based), or Macros (agent-initiated).
-
Knowledge Base. KB migration is a separate workstream. Pylon KB content lives on different API endpoints and must be loaded through Zendesk Guide's article creation API (
POST /api/v2/help_center/sections/{id}/articles), not through Ticket Import. Articles must be assigned to a section within a category — pre-create the Guide category/section hierarchy before loading articles. Image and media references within articles require separate upload and URL rewriting.
Validation and Testing
Post-migration validation requires discrete checks. Do not rely solely on API response codes — a 200 OK does not guarantee data integrity.
- Record count reconciliation. The number of closed issues in Pylon must exactly match the number of imported tickets with the
pylon_importtag in Zendesk. UseGET /api/v2/search.json?query=tags:pylon_importto count. - Field-level spot check. Randomly sample 5% of tickets (minimum 50 tickets for statistical confidence). Verify that
created_attimestamps, statuses, assignees, and custom field values match the original Pylon data. - Comment ordering and visibility. Confirm that comments appear in chronological order and that internal notes are private, not public. Check at least 20 tickets with 5+ comments each.
- Attachment integrity. Click 50 random attachments in Zendesk to ensure they open and are not corrupted 1KB error files. Verify file sizes match the originals.
- Relationship integrity. Test Organization → User → Ticket chains. Verify that tickets are assigned to the correct organizations and requesters. Check 20 tickets across different organizations.
- AI readiness check. If using Intelligent Triage, verify that sample imported tickets receive intent predictions with confidence scores. Flag any systematic misclassifications.
For a comprehensive QA checklist, see Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration.
Build In-House, Use a Third-Party Tool, or Use a Managed Service
Option comparison
| Criterion | DIY (API scripts) | Third-party tool (e.g., Help Desk Migration) | Managed migration service |
|---|---|---|---|
| Best for | <5,000 issues, simple fields, available developer | 5,000–50,000 issues, standard field mappings | 10,000+ issues, complex fields, attachments, AI requirements |
| Typical cost | 40–200 dev hours | $1–3 per ticket (vendor pricing varies) | $3,000–$15,000 flat fee |
| Attachment handling | Manual pipeline (download → upload → token) | Varies — verify with vendor | Included |
| Custom Object support | Full control | Limited — verify with vendor | Included |
| AI-first filtering | Full control | Not typically offered | Included |
| Rollback support | You build it | Varies | Included |
| Timeline | 1–4 weeks | 1–2 weeks | 1–2 weeks |
Build in-house when:
- You have fewer than 5,000 issues with simple fields
- You have a Python/Node developer available for 1–2 weeks
- You don't use Pylon Custom Objects or multi-thread Issues
- You're comfortable managing Zendesk API rate limits, retry logic, and attachment tokenization
Consider a third-party migration tool when:
- You have 5,000–50,000 issues with standard field types
- You don't need AI-first data filtering
- Help Desk Migration (HDM) lists Pylon as a supported source — verify directly with the vendor that your specific data objects (messages, attachments, custom fields) are covered, as connector capabilities vary
Use a managed service when:
- You have 10,000+ issues or complex custom field schemas
- You need attachment migration (the download → upload → token pipeline is the most error-prone step)
- You're running Zendesk AI features and need an AI-first import strategy
- Engineering bandwidth is scarce and you can't afford a multi-week side project
- You need zero-downtime guarantees with a rollback plan
The hidden cost of DIY is not the initial script — it's the debugging. Broken requester references, expired attachment URLs, Slack formatting artifacts, silent enum mismatches, and rate-limit-induced partial imports are the failure modes that turn a 1-week project into a 4-week one. See common help desk data migration mistakes and how to avoid them for a deeper look at these patterns.
Pylon to Zendesk Migration FAQ
Can I preserve Pylon Slack thread history in Zendesk?
Yes. Pylon Messages extracted via GET /issues/{id}/messages include the full message_html body and timestamp. When imported using Zendesk's Ticket Import API, these become timestamped comments on the Zendesk ticket. Slack-specific formatting like emoji reactions and @-mentions are converted to plain text during transformation. The history is preserved as content — not as native Slack thread UI.
How long does a Pylon to Zendesk migration take? A typical data migration takes 5–10 business days: 1–2 days for extraction, 1–2 days for transformation, 1–2 days for sandbox testing, 1 day for production import, and 1 day for validation. If you are also rebuilding ticket forms, routing logic, or a Help Center, plan for 2–4 weeks total. The primary bottleneck is Pylon's Issues API rate limit of 10 requests per minute.
Does importing Pylon data affect Zendesk AI? It can. Zendesk Intelligent Triage trains on your ticket history to classify intent and sentiment. Bulk-importing unresolved or noisy Slack conversations degrades classification accuracy. The mitigation is to filter imports to resolved, high-quality issues, tag imported records with quality tiers, and give the AI 2–4 weeks to learn from clean data before importing the long tail. Monitor confidence scores in the Triage Performance dashboard — scores consistently below 0.7 indicate the model needs more clean training data.
What data cannot be migrated from Pylon to Zendesk? Slack channel metadata (channel IDs, workspace info), Macros, Ticket Forms, Automation rules, and AI Training Data cannot be migrated programmatically and must be manually recreated. Slack emoji reactions used for workflow states have no Zendesk equivalent. Multi-question survey responses require lossy transformation to fit Zendesk's binary satisfaction rating model.
Is there a native Pylon-to-Zendesk migration tool? No. Neither Pylon nor Zendesk offers a built-in migration tool for this direction. Help Desk Migration (HDM) lists Pylon as a supported source, but functionality should be verified directly with the vendor for your specific data objects. The most reliable method remains the API-based approach using Pylon's REST API for extraction and Zendesk's Ticket Import API for loading.
What happens to my Zendesk SLA metrics after migration?
Imported tickets do not generate Zendesk SLA metrics (first reply time, resolution time, etc.). SLA reporting will only reflect tickets created natively in Zendesk post-migration. Tag imported tickets with pylon_import and exclude them from SLA policy scoping to avoid skewing your metrics.
Can I roll back if the migration fails?
Yes, with caveats. Tickets tagged with pylon_import can be bulk-deleted via DELETE /api/v2/tickets/destroy_many (100 IDs per request). Organizations and Users created during migration can also be bulk-deleted, but only if they haven't been associated with non-imported tickets or other Zendesk objects. Always test the full import in a sandbox before production.
Frequently Asked Questions
- Can I preserve Pylon Slack thread history in Zendesk?
- Yes. Pylon Messages extracted via GET /issues/{id}/messages include full message_html and timestamps. When imported using Zendesk's Ticket Import API, these become timestamped comments. Slack-specific formatting like emoji reactions is converted to plain text. The history is preserved as content, not as native Slack thread UI.
- How long does a Pylon to Zendesk migration take?
- A typical data migration takes 5–10 business days: 1–2 days for extraction, 1–2 days for transformation, 1–2 days for sandbox testing, 1 day for production import, and 1 day for validation. If you also rebuild forms, routing logic, or a Help Center, plan for 2–4 weeks. The primary bottleneck is Pylon's Issues API rate limit of 10 requests per minute.
- Does importing Pylon data affect Zendesk AI?
- It can. Zendesk Intelligent Triage trains on ticket history to classify intent and sentiment. Bulk-importing unresolved or noisy Slack conversations degrades classification accuracy. Filter imports to resolved, high-quality issues and tag imported records so they can be excluded from AI training if needed.
- What data cannot be migrated from Pylon to Zendesk?
- Slack channel metadata, Macros, Ticket Forms, Automation rules, and AI Training Data cannot be migrated programmatically and must be manually recreated in Zendesk. Slack emoji reactions used for workflow states have no Zendesk equivalent. Multi-question survey responses require lossy transformation.
- Is there a native Pylon-to-Zendesk migration tool?
- No. Neither Pylon nor Zendesk offers a built-in migration tool for this direction. Help Desk Migration lists Pylon as a supported source, but the most reliable method is API-based extraction from Pylon and loading via Zendesk's Ticket Import API.

