Skip to content

Desk365 to Jira Service Management Migration Guide

Technical guide to migrating tickets, contacts, and knowledge base data from Desk365 to Jira Service Management. Covers API extraction, field mapping, ADF conversion, and common failure modes.

Roopendra Talekar Roopendra Talekar · · 20 min read
Desk365 to Jira Service Management Migration Guide
TALK TO AN ENGINEER

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

Desk365 to Jira Service Management Migration Guide

Migrating from Desk365 to Jira Service Management (JSM) means moving tickets, conversation history, attachments, contacts, knowledge base articles, and operational workflows between two systems with fundamentally different data models. Desk365 is a Microsoft 365-native helpdesk with a flat ticket structure. JSM sits on top of Jira's issue-tracking engine, where every ticket is an "issue" tied to a project, issue type, and workflow.

This guide covers the data objects you can extract, API mechanics on both sides, field mapping decisions, import method tradeoffs, knowledge base migration, and the failure modes you need to plan for. It assumes Jira Service Management Cloud.

Why Teams Move from Desk365 to JSM

Desk365 works well as a lightweight, Teams-integrated helpdesk. Teams outgrow it when they need deeper ITSM workflows — change management, problem management, asset CMDB, or tight coupling between service requests and engineering work in Jira Software.

JSM provides native integration with Confluence for knowledge bases, Jira Software for dev escalation, and a full ITIL-aligned workflow engine. The tradeoff: JSM is significantly more complex to configure, and its pricing model charges per agent.

The migration itself is not symmetrical. Desk365 makes it relatively easy to get data out. Getting data into JSM — with correct field types, statuses, user mappings, and comment visibility — is where most teams hit friction.

What You Can Extract from Desk365

Desk365 offers two extraction methods: CSV export and REST API v3.

The CSV export is accessible from the agent portal and supports filtered exports by date range, status, or assigned agent. It covers tickets, time entries, contacts, and companies. The API provides richer, threaded data including conversation history and knowledge base content. (help.desk365.io)

Warning

Desk365 requires separate exports from the All Tickets view and the Archived Tickets view for complete history. Forgetting archived tickets is a common cause of incomplete migrations. (help.desk365.io)

Desk365 API v3 Basics

  • Authentication: API key, found at Settings > Integrations > API
  • Base URL: https://yoursubdomain.desk365.io
  • Rate limits: Plan-specific — Standard: 100 calls/hour; Plus and Premium: 50 calls/minute (help.desk365.io)
  • Pagination: Controlled via ticket_count parameter
Object API Endpoint Notes
Tickets /v3/tickets Set include_description=1 — descriptions are not included by default
Contacts /v3/contacts Primary and secondary department associations
Companies /v3/companies Department structure included
Knowledge Base /v3/kb/categories, /v3/kb/folders, /v3/kb/articles Categories → Folders → Articles hierarchy
Surveys /v3/surveys, /v3/surveys/ratings CSAT, NPS, five-star ratings
Notes Included in ticket responses Private and public notes distinguished
Info

By default, the Desk365 "Get all tickets" endpoint does not include the ticket description in the response. You must explicitly set the include_description parameter. Skipping this leads to migrated tickets arriving in JSM with empty bodies. (help.desk365.io)

Desk365 tickets ship with these default fields: Priority, Status, Subject, Type, Group, Category, Subcategory, Source, and Contact. The Type field defaults to four values: Question, Incident, Problem, and Request. Custom fields support Dropdown, Text Input, Checkbox, Date, Number, Paragraph Input, and multi-level (cascading) field types.

CSAT and Survey Data

Desk365 exposes survey data via /v3/surveys and /v3/surveys/ratings. Extract this before migration — it is the only way to preserve historical satisfaction scores.

JSM does not have a native CSAT engine in the same model as Desk365. Your options for preserving this data:

  • Store survey scores as read-only custom fields on each migrated issue (e.g., desk365_csat_rating, desk365_csat_comment)
  • Export the full survey dataset as a CSV and store it in Confluence as a historical record linked from the migrated project
  • If ongoing CSAT measurement is required in JSM, configure a third-party integration such as Nicereply, Surveypal, or Atlassian's built-in customer satisfaction feature (available on Premium and Enterprise plans)

Do not attempt to map Desk365 survey ratings into JSM's native satisfaction fields — the data models are not compatible and the write path for those fields is restricted.

How Jira Service Management Accepts Data

JSM has multiple import paths, and picking the wrong one causes problems that are hard to fix after the fact.

Relevant APIs

  1. Jira Cloud REST API (/rest/api/3/issue) — creates standard Jira issues with direct control over all fields including assignee, reporter, and custom fields
  2. JSM Service Desk API (/rest/servicedeskapi/request) — creates customer requests with service desk context, including serviceDeskId, requestTypeId, and request participants
  3. Jira Bulk Issue API (POST /rest/api/3/issue/bulk) — creates up to 50 issues per call; unlike single-issue creation, this endpoint supports overriding system-managed fields when used through the Jira CSV/JSON import pipeline
  4. Jira CSV/JSON importers — wizard-based bulk import, but with significant service-desk-specific limitations

For migrations requiring comment visibility control, the JSM Service Desk API is the right choice — it accepts a public flag on comments. The Jira Cloud REST API gives broader field control. CSV and JSON importers are simpler but make all imported comments public in JSM, which is a deal-breaker if Desk365 private notes must remain internal. (support.atlassian.com)

JSM Project Type Matters

JSM has two project types: Classic (company-managed) and Team-managed (formerly Next-gen). Most migration tooling — including Atlassian's own importers — works reliably only with Classic projects. If your target project is Team-managed, either convert it or create a new Classic project for the import.

Authentication and Rate Limits

JSM Cloud requires an API token generated at id.atlassian.com/manage-profile/security/api-tokens, paired with the admin user's email for Basic Auth.

Rate limiting is the single biggest operational constraint for migration scripts. Jira Cloud enforces three independent rate-limiting systems simultaneously:

  • Points-based quota (per-hour): Each API call consumes points based on complexity and data volume.
  • Burst rate limits (per-second): Restricts requests per second per endpoint, enforced per-tenant. Uses a token bucket algorithm.
  • Per-issue write limits: Restricts how frequently a single issue can be modified.

When you hit a limit, JSM returns a 429 Too Many Requests response with a Retry-After header. Your migration script must implement exponential backoff and read the X-RateLimit-Remaining and Retry-After headers on every response.

Warning

Atlassian does not publish exact rate limit numbers for Jira Cloud. The computation logic changes continuously. Design your scripts to be adaptive, not to target a fixed threshold.

Field Mapping: Desk365 to Jira Service Management

This is where migrations succeed or fail. Desk365 and JSM have different field semantics, and a naive 1:1 mapping loses data or creates garbage.

Core Field Mapping

Desk365 Field JSM Field Mapping Notes
Subject Summary Direct map
Description Description Desk365 returns HTML/plain text; JSM v3 API requires Atlassian Document Format (ADF)
Status Workflow Status Must pre-create statuses in JSM workflow; JSM will not create missing statuses during import
Priority Priority Map Desk365 values (Low, Medium, High, Urgent) to JSM priority scheme
Type (Question, Incident, Problem, Request) Issue Type + Request Type Each Desk365 type maps to both a JSM Issue Type and a Request Type
Group Component or Queue No direct equivalent — use Components, Queues, or a custom field
Category / Subcategory Labels or Custom Field JSM has no native category hierarchy; use labels or a cascading select custom field
Assigned Agent Assignee Agent must exist in JSM as a licensed user; match by email to accountId
Contact (requester) Reporter / Customer Must be a JSM customer; create via API if missing
Company Organization Works for customer grouping; breaks when the Desk365 company also drives routing or asset ownership
Department Custom field, Organization, or Service Project Desk365 departments control visibility; requires access-model redesign, not a field copy
Private Notes Internal Comments Use visibility property or public: false on the request comment API
Public Notes Public Comments Standard comment creation
Created Date Created Standard API sets creation date to the API call time; use bulk import to preserve historical dates
Custom Fields Custom Fields (customfield_XXXXX) Map by type; dropdown fields need option IDs, not display values
CSAT Score Custom Field (read-only) No native equivalent; store as desk365_csat_rating custom field
Warning

The Request Type Trap: Importing Desk365 tickets as standard Jira issues without assigning a Customer Request Type value means those tickets will not appear in the customer portal, and end-users will not receive email notifications about updates.

The Description Format Problem

The Jira Cloud v3 REST API requires description and multi-line text fields in Atlassian Document Format (ADF), a JSON-based document structure. Desk365 exports descriptions as plain text or HTML.

You need a conversion layer. A minimal ADF wrapper for plain text:

{
  "type": "doc",
  "version": 1,
  "content": [
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "Your ticket description here"
        }
      ]
    }
  ]
}

For HTML content with formatting, you need to parse the HTML and build corresponding ADF nodes (headings, lists, code blocks, etc.). Two maintained options:

  • @atlaskit/adf-utils — Atlassian-maintained npm package for building and validating ADF documents. Install via npm install @atlaskit/adf-utils. Provides builders, validators, and traversal utilities.
  • adf-builder — community-maintained npm package (search npm registry for current version and maintenance status before using in production).

Do not skip ADF conversion — raw HTML dumped into an ADF field renders as literal markup text in the portal. Test your converter against real ticket data, not synthetic examples, before running at scale.

Department-to-JSM Mapping: Decision Table

Desk365 departments are nested inside companies and control ticket-form, knowledge base, and announcement visibility. JSM has no direct equivalent. The right mapping depends on what the department field actually does in your Desk365 environment:

Desk365 Department Usage Recommended JSM Approach Rationale
Purely organizational metadata (reporting only) Custom field (cf_department) Lowest effort; preserves data without restructuring
Controls ticket routing to different agent groups Component + Queue configuration Maps department-based routing to JSM queue filters
Controls visibility (customers see only their department's tickets) Separate Service Projects per department JSM project-level permissions are the correct isolation boundary
Drives both routing and visibility Separate Service Projects + Components within each Cleanest model; higher configuration overhead
Mixed usage across different companies Audit per company first; apply rules above per segment No single answer; avoid forcing a uniform mapping
Warning

Desk365 company and department managers can see tickets beyond the ones they created, scoped by company or department. JSM organizations provide grouped customer access but use a different permission model. If manager-level ticket visibility is a business requirement, map this to Organization Managers in JSM (available via the PUT /rest/servicedeskapi/organization/{organizationId}/user endpoint) or to project roles. Do not treat this as a field copy — it is an access model design decision. (help.desk365.io)

Desk365 Companies → JSM Organizations

Desk365 companies carry domains, contacts, assets, and department structure. JSM organizations are groups of customers shared across service projects. Create JSM organizations via:

curl -u admin@example.com:API_TOKEN \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Corp"}' \
  https://your-domain.atlassian.net/rest/servicedeskapi/organization

Then add customers to the organization:

curl -u admin@example.com:API_TOKEN \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"usernames": ["user@acme.com"]}' \
  https://your-domain.atlassian.net/rest/servicedeskapi/organization/{organizationId}/user

The company→organization mapping works for customer grouping but breaks when the Desk365 company record also drives routing, department logic, or asset ownership. Audit your company records before assuming a direct copy.

CSV vs API: Choosing Your Import Method

Atlassian recommends CSV as the default import format for most tools. That guidance is too broad for JSM migrations from Desk365, because imported CSV/JSON comments become public, request-type behavior matters, and Teams/portal workflows add constraints that basic import docs do not address. (support.atlassian.com)

Use CSV import when the migration is simple

  • You are loading ticket shells or history where all comments are safe to expose publicly
  • You can prebuild the target project, workflow, statuses, and request types before import
  • Attachment files are accessible via HTTP/HTTPS URLs that Jira Cloud can reach
  • You do not need to control public vs internal comment visibility

Use API or scripted migration when fidelity matters

  • You must control public vs internal comment visibility
  • You need request-specific semantics such as serviceDeskId, requestTypeId, or request participants
  • You need to migrate knowledge base, assets, customer details, or organization structures alongside tickets
  • You need phased backfill plus a short delta sync so the source system can keep running until cutover
  • You need to rebuild Teams intake, portal forms, or access rules
Tip

JSON import is not a middle ground. Atlassian recommends splitting large JSON imports to around 1,500 work items per file, and comments imported through JSON also become public in JSM. For Desk365-to-JSM work, JSON is a niche fallback, not the default path. (support.atlassian.com)

Preserving Historical Timestamps

When creating issues via POST /rest/api/3/issue, JSM sets the Created date to the moment of the API call. To preserve historical creation dates, you have two options:

Option 1: Jira CSV/JSON bulk import pipeline — The Jira importer (accessed via Project Settings > Import) can override Created and Updated system fields when the import file includes them in the correct column format. This is the most reliable method for timestamp preservation at scale.

Option 2: Description prepend fallback — For API-driven migrations where bulk import is not used, prepend the original creation date to the issue description:

[Originally created: 2022-11-14 09:32 UTC by jane.smith@acme.com]

<original ticket body>

This preserves timestamp visibility without requiring bulk import infrastructure, at the cost of polluting the description field.

The Jira bulk create endpoint (POST /rest/api/3/issue/bulk) accepts up to 50 issues per request and shares the same field constraints as single-issue creation — it does not bypass timestamp restrictions. Timestamp override requires the importer pipeline specifically.

Step-by-Step Migration Runbook

Step 1: Audit Your Desk365 Data

Before writing any code, inventory everything:

  • Total ticket count (active and archived) and date range
  • All custom fields in use and their types
  • Knowledge base article count, category/folder structure
  • Contact and company count, department structure and department manager assignments
  • Attachment volume — this drives timeline more than ticket count
  • Inbound channels (Teams, email, web forms)
  • Custom statuses, groups, categories, subcategories
  • SLA configurations and automations
  • Survey/CSAT data volume and whether historical scores are needed for compliance

Export a sample CSV and pull a batch via the API to surface unexpected nulls and bad field values before touching production.

Step 2: Model the JSM Target First

Build the target service project before you move history. This is not optional.

  • Create a Classic project type
  • Define all issue types matching your Desk365 ticket types
  • Build the workflow with all needed statuses (JSM will not create missing statuses during import)
  • Create custom fields matching Desk365 custom fields (customfield_XXXXX identifiers)
  • Set up the priority scheme
  • Configure Request Types — map Desk365 types to JSM Request Types, and make sure the Customer Request Type field is populated for portal visibility

For identity management: if your agents authenticate via Microsoft 365, deploy Atlassian Guard (formerly Atlassian Access) and configure user provisioning from Microsoft Entra ID. Force a sync before migrating tickets.

For external customers, create JSM customer accounts via the API before migrating tickets — customer accounts must exist before issues that reference them can be created:

curl -u admin@example.com:API_TOKEN \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com",
    "displayName": "Jane Customer",
    "notify": false
  }' \
  https://your-domain.atlassian.net/rest/servicedeskapi/customer

Set "notify": false during bulk customer creation to suppress welcome emails to thousands of users simultaneously.

Step 3: Extract and Normalize Source Data

Use the Desk365 API v3 for extraction:

curl -X GET "https://yoursubdomain.desk365.io/v3/tickets?include_description=1&ticket_count=100&page=1" \
  -H "Authorization: Bearer YOUR_API_KEY"

Paginate through all tickets. For each ticket, also pull:

  • Replies and notes (public and private)
  • Attachments — download and store locally, since Desk365 URLs may be behind auth or short-lived
  • Contact details for reporter mapping

Desk365 API filters use arrays of string values, and -- represents an unassigned value. Use UTF-8 URL encoding for filter parameters. (help.desk365.io)

Delta sync method: For the cutover delta sync (Step 7), use the Desk365 API updated_since filter parameter to pull only tickets modified after your initial extraction timestamp. The parameter accepts an ISO 8601 date string. Record the exact UTC timestamp when your initial extraction completes — this becomes the updated_since value for the delta run. If you miss the sync window or the delta takes longer than expected, run a second delta using the timestamp from when the first delta started.

Desk365 also supports webhooks for real-time ticket event notifications. For migrations where the cutover window is under 4 hours, polling with updated_since is simpler. For longer parallel-run periods (days to weeks), configure Desk365 webhooks to write ticket update events to a queue, then replay that queue during cutover. Webhook events are not a substitute for a full extraction — use them only for the delta.

Tip

Throttle extraction to your plan's limits. Standard plans allow 100 calls/hour; Plus and Premium allow 50 calls/minute. Build retries and backoff into the extraction script from day one. (help.desk365.io)

Step 4: Transform and Map Data

This is the bulk of the engineering work:

  • Convert descriptions from HTML/text to ADF
  • Map Desk365 statuses to JSM workflow statuses
  • Map Desk365 agents to JSM user accountId values (build a lookup table by email)
  • Map Desk365 contacts to JSM customers
  • Map custom fields by type (dropdowns need option IDs, not display values)
  • Distinguish private notes from public comments
  • Preserve original Desk365 ticket numbers in a custom field for cross-reference
desk365:
  company_name: Acme
  group: IT
  category: Access
  sub_category: VPN
  cf_department: Finance
  ticket_number: 48219
 
jsm:
  organization: Acme
  request_type: Access Request
  component: VPN
  labels:
    - desk365-import
  custom_fields:
    source_ticket_number: 48219
    source_department: Finance

That pattern keeps the original Desk365 identity visible inside JSM even when you have to remodel the operational meaning of the data.

Step 5: Load Data in Dependency Order

A reliable load sequence: customers/organizations → tickets → comments → attachments → request participants → knowledge base → assets.

For each ticket:

  1. Create the issue with core fields, including the Customer Request Type field
  2. Add comments with correct public/private visibility via the request comment API
  3. Upload attachments via the attachment endpoint with X-Atlassian-Token: no-check header
  4. Transition the issue to the correct status via the transition API
curl -D- -u admin@example.com:API_TOKEN \
  -X POST \
  -H "X-Atlassian-Token: no-check" \
  -F "file=@/path/to/local/file.pdf" \
  https://your-domain.atlassian.net/rest/api/3/issue/ITSM-142/attachments

For comments, use the JSM request comment endpoint to control visibility:

curl -u admin@example.com:API_TOKEN \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "body": "This is an internal note.",
    "public": false
  }' \
  https://your-domain.atlassian.net/rest/servicedeskapi/request/{issueIdOrKey}/comment
Info

When creating issues via the standard POST /rest/api/3/issue endpoint, JSM sets the Created date to the moment the API call is made. To preserve historical timestamps, use the Jira importer pipeline (Project Settings > Import), which allows overriding system fields like Created, Updated, and Reporter. If using the standard API, prepend the original creation date to the issue description as a fallback.

For status transitions: JSM enforces workflow transition rules. You cannot set a ticket directly to "Resolved" if the workflow does not allow a transition from the initial status. Either use the transition API to walk issues through valid paths, or temporarily simplify the workflow during import. The bulk import pipeline can bypass transition rules; the standard REST API cannot.

Step 6: Migrate Knowledge Base Articles

Desk365 knowledge base articles follow a Category → Folder → Article hierarchy. JSM uses Confluence, which has a Space → Page hierarchy. This is a separate migration phase — do not combine it with ticket migration.

Hierarchy mapping:

Desk365 Structure Confluence Structure
KB Category Confluence Space (or top-level page in an existing Space)
KB Folder Parent Page under the Space/Category page
KB Article Child Page under the Folder parent page

Step-by-step:

  1. Extract all categories, folders, and articles via the Desk365 API (/v3/kb/categories, /v3/kb/folders, /v3/kb/articles)
  2. Create the Confluence Space (or identify the target Space if using an existing one)
  3. Create parent pages for each Desk365 category, then child pages for each folder
  4. For each article, convert the HTML body content to Confluence storage format — an XHTML-based XML format that Confluence uses internally
  5. Create pages via POST /wiki/rest/api/content with the converted content

Confluence storage format is distinct from both ADF and standard HTML. A minimal page creation payload:

{
  "type": "page",
  "title": "Article Title",
  "space": { "key": "SUPPORT" },
  "ancestors": [{ "id": "123456" }],
  "body": {
    "storage": {
      "value": "<p>Article body in Confluence storage format XHTML.</p>",
      "representation": "storage"
    }
  }
}

For articles with complex HTML (tables, code blocks, images, embedded videos), you need an HTML-to-storage-format conversion step. The Confluence REST API provides a conversion endpoint: POST /wiki/rest/api/contentbody/convert/storage with the body {"value": "<your HTML>", "representation": "wiki"} — this converts wiki markup to storage format, but HTML requires preprocessing first.

For images and file attachments within articles: download them from Desk365, upload to Confluence as page attachments via POST /wiki/rest/api/content/{pageId}/child/attachment, then rewrite the image src URLs in the article body to point to the Confluence attachment URLs before creating the page.

After creation, link the Confluence Space to your JSM service project under Project Settings > Knowledge Base. This makes articles searchable from the JSM customer portal.

Step 7: Validate

Do not stop at record counts. Run these checks:

  • Total ticket count matches (source vs. target, split by active and archived)
  • Random sample of 50+ tickets checked for field accuracy
  • All comments present, in correct order, with correct public/private visibility
  • Attachments downloadable
  • Status distribution matches source
  • Custom field values spot-checked
  • Knowledge base articles render correctly in Confluence and are surfaced in the JSM portal
  • CSAT custom fields populated on surveyed tickets
project = SUPPORT AND labels = desk365-import
project = SUPPORT AND statusCategory != Done

Also compare volumes by status, month, and company/organization to catch bad status mapping and incomplete batches.

Recreating Microsoft Teams Workflows

Desk365's primary selling point is deep Teams integration. To replicate this in JSM, configure Atlassian Assist (formerly Halp, now natively integrated into JSM):

  1. Install the Jira Service Management app in your Microsoft Teams tenant
  2. Configure Request Channels — map specific Teams channels to specific JSM Request Types
  3. Train users on the new behavior: Assist requires an emoji reaction (🎫) or direct bot interaction to create tickets

Key constraints:

  • A Jira site can connect to only one Microsoft Teams tenant at a time (support.atlassian.com)
  • Request types with unsupported required fields will block chat intake — design your request types with this in mind
  • Each service project gets one agent channel in Teams
Info

Behavioral difference: Desk365 often creates tickets from every message in a designated channel. Atlassian Assist is more deliberate, requiring an emoji reaction or bot interaction to prevent channel chatter from creating false-positive tickets.

Common Failure Modes

Missing descriptions: Forgetting include_description=1 on the Desk365 API. Tickets arrive in JSM with empty bodies.

Missing archived tickets: Exporting only the standard ticket view and forgetting that archived tickets require a separate export. (help.desk365.io)

Private notes exposed: Importing Desk365 history through Jira CSV or JSON importers makes all comments public. Use the JSM request comment API with "public": false when private note visibility matters. (support.atlassian.com)

ADF conversion errors: Raw HTML in JSM description fields renders as unreadable markup. Test your converter against real ticket data using @atlaskit/adf-utils validation before running at scale.

User mismatches: Desk365 agents identified by name/email will not match JSM's accountId system. Build a lookup table first. If an agent does not have a JSM license, their tickets land as unassigned.

Status transition failures: JSM enforces workflow transitions and will not create missing statuses during CSV or JSON import. Statuses must already exist in the workflow before any import begins.

Request Type omission: Tickets imported without a Customer Request Type value will not appear in the customer portal, and end-users will not receive email notifications.

Rate limit cascades: Hitting Jira's burst limits mid-migration and not handling 429 responses correctly. The script retries immediately, gets throttled harder, and the migration grinds to a halt. Always read Retry-After before retrying.

Attachment URL failures: Desk365 attachment URLs may be behind auth or short-lived. Stage attachments locally before import — do not assume Jira Cloud can reach Desk365 attachment URLs directly.

Delta sync window missed: If the delta sync takes longer than expected and new tickets are created during the delta run, you need a second pass. Record the UTC timestamp when each extraction starts, not when it ends, to ensure complete coverage.

KB article image links broken: Desk365 KB article images are hosted on Desk365 infrastructure. After migrating article HTML to Confluence without re-hosting the images, they will break when Desk365 is decommissioned. Download, re-upload to Confluence, and rewrite URLs before cutover.

SLA data loss: Historical SLA data cannot be cleanly migrated into JSM's native SLA engine, which calculates SLAs dynamically based on JQL calendars and issue events. Extract Desk365 SLA metrics and map them to read-only custom fields in JSM for compliance reference.

Customer creation order violation: Creating issues before customer accounts exist causes reporter/requester fields to fail silently or default to the API user. Always create customers first.

Welcome email flood: Creating JSM customer accounts via API with "notify": true sends a welcome email to every imported customer. Set "notify": false for bulk customer creation.

Cutover Strategy

A helpdesk migration is not a one-shot event. IT teams cannot stop supporting users during the transition.

  1. Initial sync: Migrate historical data (closed tickets, old attachments, inactive users) over several days.
  2. Validation: Verify data integrity, ADF formatting, comment visibility, and attachment accessibility.
  3. Cutover window: Choose a low-traffic period.
  4. Delta sync: Pull only tickets created or updated since the initial sync using the updated_since filter on the Desk365 API. Record the start timestamp of the initial sync as your delta boundary.
  5. Channel cutover: Update email forwarding rules to point to JSM's email handler. Remove the Desk365 bot from Teams and deploy Atlassian Assist to all relevant channels.
  6. Make Desk365 read-only: Only after validation passes on the delta sync.

A pragmatic approach to historical data volume:

  • Migrate open and pending tickets in full — all comments, attachments, custom fields
  • Migrate closed tickets from the last 12–24 months with reduced detail — subject, description, status, dates
  • Archive older closed tickets as CSV/PDF exports stored in a shared drive or Confluence space

This cuts migration volume significantly while keeping actionable history accessible.

When to Use a Tool vs Custom Scripts

Third-party tools like Help Desk Migration (Relokia) offer wizard-based field mapping and basic transformations. Good for straightforward migrations with standard fields and no private note sensitivity.

Custom scripts are necessary when:

  • You must control public/private comment visibility
  • You have deeply nested custom fields or multi-level Desk365 fields
  • You need precise control over timestamps and internal note handling
  • Your ticket volume exceeds 50,000+ and you need optimized batching
  • You need to maintain original ticket IDs as references
  • You are migrating KB articles with embedded images that need URL rewriting
  • You have department-based access rules that require project-level restructuring in JSM

The tradeoff is engineering time. A tool gets you 80% there in a day. Custom scripts get you to 100% but take one to two weeks of development and testing.

The Hard Parts Are Not the Records

The mistake teams make is assuming this migration is just a record export. The actual hard parts are comment visibility, access-model redesign, Teams channel behavior, ADF conversion, Confluence storage format conversion for KB articles, delta sync timing, and cutover control.

The extraction side from Desk365 is straightforward. The JSM side — particularly the interaction between request types, customer portal visibility, comment privacy, and organizational structure — is where careful engineering matters. Plan for the access model before writing a single migration script. Every other problem is solvable mid-flight; a misdesigned permission model requires rebuilding the target project.

Frequently Asked Questions

Will Desk365 private notes stay private after migrating to Jira Service Management?
Not if you use Jira's CSV or JSON importers — imported comments become public in JSM. To preserve internal/public visibility, use the JSM request comment API, which accepts a public true/false flag on each comment. ([support.atlassian.com](https://support.atlassian.com/jira-cloud-administration/docs/import-data-from-a-csv-file/))
How do I handle Desk365 ticket descriptions in JSM?
JSM's v3 REST API requires descriptions in Atlassian Document Format (ADF), a JSON structure. Desk365 exports descriptions as HTML or plain text. You need a conversion layer — dumping raw HTML into an ADF field renders as literal markup text in the portal.
Do I need to export archived tickets from Desk365 separately?
Yes. Desk365 requires separate exports from the All Tickets view and the Archived Tickets view to get complete history. Missing the archived export is a common cause of incomplete migrations. ([help.desk365.io](https://help.desk365.io/en/articles/export-tickets/))
Can I keep Microsoft Teams ticketing workflows after moving to JSM?
Yes, but you need to rebuild them. JSM provides Teams integration through Atlassian Assist. Key constraints: a Jira site can connect to only one Teams tenant, request types with unsupported required fields block chat intake, and ticket creation requires an emoji reaction or bot interaction rather than auto-capturing every message.
How long does a Desk365 to Jira Service Management migration take?
It depends on ticket volume, attachment size, and custom field complexity. A straightforward migration under 10,000 tickets can complete in 1–3 days. Complex migrations with deep custom fields, knowledge base content, private note handling, and full conversation history typically take 1–2 weeks of engineering work.

More from our Blog