ITOpsSignal

Zendesk to Jira Service Management Migration: Tickets, Knowledge Base, and What Breaks

By Shay Rozov

Every Zendesk-to-JSM migration I've been involved in started with the same sentence from someone in finance or leadership: "It's just moving tickets from one system to another, right?" And every one of them taught me the same lesson: moving the tickets is the easy part. What actually determines whether the project is a success is everything around the tickets - the internal notes that must stay internal, the statuses that don't exist on the other side, the automations nobody documented, and a knowledge base that has to change platforms, not just locations.

I've led these migrations for organizations that moved their IT service desk off Zendesk because they'd outgrown a customer-service tool, or because they were consolidating onto Atlassian. This article is what I wish someone had handed me before the first one: what moves cleanly, what needs a decision, and what quietly breaks unless you catch it.

Quick answer

Tickets and their conversation history can be moved from Zendesk to JSM either with Jira's built-in CSV importer or with a Marketplace migration tool. The CSV route is free and works well for small, simple histories, but it makes every internal note public, can't carry attachments without extra work, and silently maps statuses it doesn't recognize. A dedicated migration tool handles comments, private notes, and attachments properly and is worth the cost for any real volume. The knowledge base doesn't migrate into JSM itself - it moves into Confluence, which JSM then uses as its knowledge base. Triggers, automations, macros, views, and SLA policies don't migrate at all by any method; you rebuild them in JSM, and that rebuild is where most of your project time should go.

Start with an inventory, not an export

The first week of every migration I run is spent in the source system, not the target. You need to know what you're actually moving, and Zendesk accumulates configuration quietly over the years.

Here's what I inventory before anyone exports a single ticket:

Ticket fields. Every custom field, its type, and whether it's still used. Dropdown fields matter most, because their values have to exist on the JSM side before import.

Zendesk admin center showing ticket fields list, including custom dropdown fields such as Change reason, Change risk and Change type
Zendesk ticket fields. Every custom dropdown here needs a matching field - with matching values - in JSM before you import.

Ticket statuses. Custom statuses are the item I see missed most often. Zendesk lets you add statuses like "Awaiting CAB approval" under a status category. JSM statuses belong to a workflow, and your JSM workflow won't have them unless you add them.

Zendesk ticket statuses, including custom statuses 'Awaiting CAB approval' and 'Scheduled for implementation'
Custom ticket statuses in Zendesk. Keep this screen open when you design the JSM workflow - you'll need it again at import time.

Business rules. Triggers and automations are where years of institutional knowledge live. None of them migrate. List every active rule, what it does, and whether anyone still needs it.

Zendesk triggers list grouped by category, including a Change management category with four triggers
Zendesk triggers, grouped by category. Each of these has to be rebuilt as a JSM automation rule - or deliberately retired.
Zendesk automations list including time-based escalation and reminder rules
Time-based automations. These map to scheduled rules in JSM automation, and their conditions rarely translate one-to-one.

My rule of thumb: if a trigger hasn't fired in 90 days, it goes on a "retire unless someone objects" list. Zendesk shows usage per rule, which makes this conversation much easier than it sounds. It's common to start with well over a hundred triggers and end up rebuilding only a fraction of them. In my experience, very few of the retired ones are ever asked about again - and the retire list is exactly where you'll find out if one still matters.

Tickets: two paths, and they're not equivalent

Path 1: Jira's built-in CSV importer

JSM doesn't have a native "Import from Zendesk" connector. What it has is the External System Import in Jira administration, with CSV and JSON options. Note the banner at the top: Atlassian's newer import experience currently supports business and software spaces, so for a service space you're on the classic importer.

Jira admin External system import page, JIRA import wizard with CSV, Trello and JSON options
Jira admin → System → External system import. For a JSM space, CSV is the practical built-in option.

Getting data out of Zendesk is its own step, and it has more gaps than people expect. Zendesk's native CSV export gives you ticket metadata only - no comments, and no ticket description either. The JSON export does include comments, but if a single ticket is larger than 1 MB, its comments are left out and the affected tickets are listed in a separate error file. Neither export is switched on by default: the account owner has to ask Zendesk support to enable data exports, and the export tools require the Growth plan or above on Suite, or Professional or above on Support. In practice, for any migration I lead, we pull tickets and comments through the Zendesk API and build the import file ourselves - which gives us full control over formatting. Whichever route you take, an export file that was created is not an export file that's complete. Check it before you build anything on top of it.

Once the file is ready, field mapping is straightforward:

Jira CSV importer field mapping screen with Zendesk export columns mapped to Assignee, Comment Body, Date Created, Description, Labels, Priority, Reporter, Status, Summary and Work type
Mapping a Zendesk export to JSM fields. Note the hint under Comment: author and date are preserved only if each comment follows the importer's exact format.

Three things on this screen deserve attention:

  • Comments. Each comment goes in its own "Comment" column, and author and date only survive if the cell follows the importer's exact pattern. Atlassian's documented example looks like 31/Mar/24 6:49 AM;<Atlassian account ID>;Comment text - a specific date format, and the author identified by their Atlassian account ID, not their name. Treat that as the starting point, not a guarantee: check the format against Atlassian's current documentation and prove it on a small test import before you build the full file. Get it wrong and the comment gets today's date and your name as author - the most common reason a migrated history looks like it was all written by the admin on the same afternoon.
  • The Zendesk ticket ID. There's no native place for it. I always create a dedicated "Legacy Zendesk ID" custom field in JSM before import and map to it. Six months later, when someone searches for "that ticket from Zendesk, number 48213," you'll be glad it's there.
  • Reporter and Assignee. Plan the identity mapping before you import, don't discover it during. The importer matches these fields by email address or Atlassian account ID, and it can create Jira users from them - but whether it does depends on your setup: it can't create them if you use external user management, and it stops if your license doesn't have room for them. How portal-only JSM customers are handled is a separate question that you should test explicitly. If your Zendesk export has names instead of emails - as some exports do - nothing can be matched at all. Decide which agents and customers should exist, how they'll be matched, and verify it on the test import.

Then comes the screen that justifies this entire article:

Jira CSV importer value mapping screen showing Zendesk priorities 'normal' and 'urgent' not found, status 'Awaiting CAB approval' and 'Solved' defaulting to 'Canceled', and a warning 'All internal comments will become public'
The value mapping step. Look at the defaults for "Awaiting CAB approval" and "Solved" - and at the red banner.

Look closely at what this screen is telling you. For context, this is what an ordinary Zendesk IT ticket looks like - a public request from the user, followed by internal notes from the technician:

Zendesk ticket with internal notes marked 'Internal' from the technician, below the requester's public message
A Zendesk ticket with internal notes. In Zendesk, these are visible only to agents.

Now back to the importer:

  1. "All internal comments will become public." This is the single most dangerous thing in a Zendesk-to-JSM migration. Zendesk internal notes contain the things technicians write for each other: passwords that were reset, security findings, frank opinions about the user's request. The CSV importer imports every comment as public in a JSM space, which means customers can see them in the portal. In the file above I prefixed private notes with [INTERNAL NOTE] - that at least makes them identifiable, but it doesn't make them private. If your tickets have meaningful internal notes, the CSV importer alone is not an option.
  2. Status defaults are wrong. Zendesk's "Awaiting CAB approval" and "Solved" don't exist in this JSM workflow, and the importer's default suggestion for both is Canceled. Click "Begin Import" without checking, and every solved ticket in your history arrives as canceled. Your reporting on resolution rates is ruined on day one.
  3. Priorities don't match. Zendesk uses Low / Normal / High / Urgent. JSM's defaults are Lowest / Low / Medium / High / Highest. The importer offers to create "normal" and "urgent" as new priorities - don't. Map Normal to Medium and Urgent to Highest, and keep one priority scheme across the organization.

I stopped at this screen for this article; I didn't click Begin Import. That's also exactly what I'd tell anyone to do on a first dry run: get to this screen, screenshot it, and review every mapping with the service desk lead before a single ticket is created.

Path 2: a dedicated migration tool

For anything beyond a few hundred simple tickets, I use a dedicated migration service. One widely used option, Help Desk Migration, is listed on the Atlassian Marketplace and connects to both systems by API. It moves tickets with their full thread, private notes as internal comments, attachments and inline images, contacts, organizations, custom fields, tags, and Help Center articles. It's priced per record, and it offers a free demo migration of a small sample so you can check the result before paying.

What it doesn't move - and what no tool moves - is the same list: macros, triggers, automations, views, and SLA policies. Those are configuration, not data, and JSM models them differently.

My advice after doing this both ways: the tool pays for itself the first time it saves you from explaining to the CISO why technician notes about a security incident were visible in the customer portal.

Attachments

The CSV importer can import attachments from URLs, but the URLs have to be reachable by your Jira Cloud site at import time. Zendesk attachment URLs generally require authentication, so in practice you either download and re-host them somewhere Jira can reach, or you use a migration tool that transfers them API-to-API. Plan for this early; attachments are often the largest part of the data by volume.

The knowledge base: it moves platforms, not just locations

This is the part people underestimate most. In Zendesk, the knowledge base lives inside the same product - Help Center categories, sections, and articles.

Zendesk Help Center 'IT Help' category with sections Remote access & VPN, Accounts & passwords, and Laptops & hardware, including a locked agents-only article
A Zendesk Help Center category. Note the lock icon: that article is restricted to agents only.

In JSM, the knowledge base is Confluence. When you set up a knowledge base for a service space, the storage choice is Confluence - there's no "import from Zendesk Guide" option here.

JSM space settings Knowledge base page with 'Set up knowledge base' button and AI pre-drafted articles based on common requests
JSM → Space settings → Knowledge base. Notice the pre-drafted articles JSM generated from the requests it has already seen.
JSM knowledge base setup dialog 'Where do you store your knowledge?' with Confluence as the option
Setting up the knowledge base: the source is Confluence.

That has consequences you need to plan for:

  • Structure changes. Zendesk's category → section → article becomes a Confluence space → page tree. It's a reasonable mapping, but decide it upfront.
  • Permissions change model. Zendesk article visibility is controlled by user segments - that locked "agents only" runbook above. In Confluence, it's page and space permissions, and what customers can see through the portal depends on how the Confluence space is shared with JSM customers. Every restricted article needs a deliberate decision on the Confluence side, or your internal runbooks end up in the customer portal. That's the same failure as the internal notes, in a different place.
  • Links break. Articles that link to each other use Zendesk Help Center URLs. After migration, those links still point to Zendesk.
  • Images break. Images embedded in articles are hosted on Zendesk. Once your Zendesk subscription ends, they stop loading.
Zendesk Help Center article 'Troubleshooting VPN connection issues' with an internal link to another article and a broken embedded image showing only its alt text
A typical IT article: an internal link to another article (will point to Zendesk after migration) and an embedded image hosted on Zendesk. On my test site that image already fails to load and shows only its alt text - exactly what your migrated articles will look like once the Zendesk-hosted files are gone.

My approach: migrate articles with a tool that rewrites internal links and re-uploads images, then run a link checker across the Confluence space before go-live. And treat the migration as an opportunity. In every KB migration I've been part of, a large share of the articles turned out to be outdated, duplicated, or never viewed. Don't migrate those. JSM's AI can even pre-draft articles from your most common requests, as in the screenshot above - a useful way to find the gaps your old knowledge base never covered.

What you rebuild: rules, request types, SLAs

Triggers become automation rules

Most of the Zendesk triggers you decide to keep become JSM automation rules - but not all of them. Some turn out to be workflow configuration in JSM (a transition, a validator, an approval step), some become SLA settings, some become email channel or request type configuration, and some simply aren't needed because JSM does the job natively. For the ones that do become rules, the concepts map well - trigger conditions become rule conditions, actions become actions - but the rule builder is different enough that it's a rewrite, not a copy.

JSM automation page listing routing rules such as 'Route: Accounts & access → Identity team' and 'Re-route: Unassigned > 1 day → Service Desk L1'
Rebuilt routing rules in JSM automation. Clear, consistent naming ("Route: X → Team") saves hours when you're debugging why a ticket went to the wrong queue.

Two practical notes. First, name rules consistently from day one; with 40 rules it becomes essential. Second, test every rule on a handful of real tickets before go-live. JSM automation has usage limits per plan, and a badly scoped rule that fires on every ticket update can consume a month's allowance in days.

Forms become request types

Zendesk ticket forms map most naturally to JSM request types, and this is a good place to redesign rather than replicate. JSM request types drive the portal, the queues, the SLAs, and the reporting - they're much more central than Zendesk forms ever were.

JSM Service requests settings showing request types such as Get IT help, Request a new account, Request new hardware, and Emailed request
JSM request types. Imported tickets don't get a request type automatically - which matters more than it looks.

Here's the trap: tickets imported through CSV don't have a request type unless you set one explicitly. Without it, they don't appear correctly in the customer portal, and customers can't see their own historical requests. You can set the request type as part of the import, but not by name: the importer needs the request type's ID. Open the request type in space settings and take the number after request-type/ in the address bar (or pull all of them through the JSM API), and put that number in your file. The alternative is a bulk edit afterward. Either way, log in as a real customer account before go-live and check that their old requests are there.

Views become queues, SLAs are rebuilt

Zendesk views map to JSM queues, with JQL instead of view conditions. SLA policies have to be recreated with JSM's SLA goals and calendars - and historical SLA data does not come across. If SLA history matters for compliance or reporting, export the Zendesk SLA reports before you switch off the old system.

What breaks (the checklist I actually use)

  • Internal notes become public with the CSV importer. Use a migration tool, or don't import notes via CSV.
  • Unknown statuses default to the wrong value - in my import, "Solved" and "Awaiting CAB approval" both defaulted to Canceled. Review every value mapping.
  • Priorities don't match (Normal and Urgent vs. Medium and Highest). Map them; don't create new ones.
  • Comment authors and dates collapse to the importing admin and today's date if the format or the author ID is wrong. Prove it on a test import.
  • Identity mapping - decide how agents and customers are matched (email or account ID) and whether the importer will create them in your setup, and test portal customers separately.
  • Exports can be incomplete - CSV has no comments or descriptions, and JSON drops comments on tickets over 1 MB.
  • Attachments need reachable URLs or an API-to-API tool.
  • Imported tickets have no request type unless you import the request type ID - not the name - and they don't show up properly in the portal.
  • No place for the Zendesk ID unless you create a custom field for it.
  • KB links and images still point to Zendesk.
  • Restricted KB articles can become visible to customers if Confluence permissions aren't set deliberately.
  • Triggers, automations, macros, views, and SLAs don't migrate at all.
  • Email channels - the support address, forwarding rules, and SPF/DKIM - have to be moved and tested, or users' replies to old Zendesk emails go nowhere.

Proving it worked: acceptance criteria

A dry run and a look around as an agent and as a customer are necessary, but they don't answer the question your steering committee will ask: how do we know the migration succeeded? Before the historical migration, I agree on acceptance criteria with the service desk lead, and I reconcile source and target against them after every run:

  • Ticket counts by status and by year. Zendesk and JSM side by side. Any gap has to be explained, not rounded off.
  • Comment counts, split into public and internal. Totals per ticket sample and overall. If internal notes arrived as public, or vanished, this is where you see it.
  • Attachments. Count and total size in the source vs. the target, and open a sample of them.
  • Orphans. Tickets with no reporter, no request type, or no Legacy Zendesk ID. The target number is zero.
  • Authors and dates. On a sample, check that comment authors and timestamps match the original, not the importing admin and import day.
  • A hand-picked sample of the hardest tickets. Long threads, many attachments, merged tickets, sensitive internal notes, customers with several organizations. Compare each one with the original, screen by screen.
  • Knowledge base. Article count per section, broken-link report clean, and every restricted article confirmed invisible from a customer account.

Here's what those checks look like in practice. On the Zendesk side, the source numbers come straight from the views - I take them before each run, so the comparison is against a fixed baseline, not a moving one:

Zendesk Views panel with ticket counts per view, including All unsolved tickets, Pending tickets and CAB - Awaiting approval, next to the All unsolved tickets list
Source baseline in Zendesk: counts per view and per status, captured before the run starts.

On the JSM side, the space summary gives you the same breakdown by status in one chart:

JSM space summary Status overview donut chart showing work items by status: Waiting for support, Open, No value, Work in progress and Completed
Target side in JSM. Every slice has to reconcile with a Zendesk status - and a "No value" slice is the first thing I'd ask about.

And the orphan check is a single JQL query. The result you want is an empty list:

Jira search with JQL 'project = ITHD AND ("Request Type" is EMPTY OR reporter is EMPTY)' returning no work items
The orphan check: no work item without a request type or a reporter. Add the Legacy Zendesk ID field to the same query once it exists.

If a criterion fails, the run fails, and we fix and repeat it. It sounds bureaucratic, but it turns "it looks fine" into something you can sign off on.

How I run the cutover

This is the timeline from one of my migrations - a mid-sized service desk with a clean-ish Zendesk instance. Treat the durations as an example, not a standard: a larger volume, more integrations, or a bigger knowledge base will stretch it, and a small, simple instance can go faster. The order is what I'd keep:

  1. Inventory and retire (weeks 1-2). Fields, statuses, rules, forms, articles. Decide what not to migrate.
  2. Build JSM first (weeks 2-5). Workflows with the statuses you need, request types, fields including the Legacy Zendesk ID, queues, SLAs, automation, Confluence KB structure.
  3. Dry run with real data (week 5). A sample of a few hundred tickets, including your ugliest ones - long threads, many attachments, internal notes. Review them in JSM as an agent and as a customer in the portal, and run the acceptance checks above.
  4. Historical migration (week 6). Everything closed, migrated while Zendesk is still live, then reconciled against the acceptance criteria.
  5. Delta and cutover (a weekend). Move the email address, migrate tickets created or updated since the historical run, freeze Zendesk to read-only.
  6. Keep Zendesk read-only for 60-90 days. You will need to look something up. Budget for the overlap.

Where this is heading

Migration tooling in this space has improved a lot. API-to-API migrations that preserve private notes, attachments, and article structure are now routine rather than heroic, and AI in JSM is starting to help with the knowledge base side, drafting articles from real request history instead of relying on whatever you carried over.

But the hard part of a Zendesk-to-JSM migration was never the data. It's the decisions: which rules still matter, which statuses reflect how your team actually works, which articles are worth keeping, and who can see what. Make those decisions deliberately, test with real tickets and a real customer account, and the migration becomes a chance to leave years of accumulated configuration debt behind instead of carrying it with you.


References: Import data from a CSV file - Atlassian Support · Import comments with author and date - Atlassian Support · Set the request type when importing from CSV - Atlassian Support · Exporting ticket, user, or organization data - Zendesk help · Zendesk Incremental Exports API · Zendesk to Jira Service Management migration - Atlassian Marketplace

Shay Rozov

Written by

Shay Rozov

Shay Rozov has 30 years of experience in IT and technology leadership, with hands-on expertise evaluating and deploying enterprise IT and AI tools.

Related in Guides