Skip to content

Deskpro to Jira Service Management Migration: Technical Guide

A technical guide to migrating from Deskpro to Jira Service Management—covering API constraints, data mapping, ADF conversion, and edge cases.

Nachi Nachi · · 25 min read
Deskpro to Jira Service Management Migration: Technical 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
Info

Quick Answer: Migrating from Deskpro to Jira Service Management (JSM) is a data-model translation, not a file copy. The biggest technical hurdles are converting Deskpro's HTML message bodies into the Atlassian Document Format (ADF) required by JSM's REST API v3, and resolving email-based Deskpro users to Jira accountIds. CSV export captures ticket metadata but strips conversation threads, attachments, and customer–organization relationships. For anything beyond basic ticket data, API-based migration is the only method that preserves full fidelity.

Warning

If the target Atlassian Cloud site was created after October 10, 2025, richer customer and organization profile features live in Customer Service Management, not only inside Jira Service Management. Plan your company/contact mapping around that date boundary. (support.atlassian.com)

Deskpro centers support work on tickets, people, organizations, and departments. Jira Service Management is built on Jira's core issue-tracking engine: a service desk maps 1:1 to a Jira project, and customer-facing request types sit on top of Jira issue types and fields. The two systems model conversations, custom fields, and organizational relationships differently enough that a naive CSV export will pass a record-count test and still fail operationally.

This guide covers the exact API constraints on both sides, object-level mapping, migration architecture, and the edge cases that trip up teams mid-migration. For a general JSM readiness checklist, see the Jira Service Management Migration Checklist. For a deeper look at JSM's architecture, see The Ultimate Jira Service Management Guide for 2026.

Overview: Deskpro vs Jira Service Management Architecture

Deskpro is a multi-channel helpdesk platform with a CRM-like data model. Deskpro has a full REST API allowing access to the functions of the helpdesk using HTTP requests, enabling developers to retrieve, create, or modify helpdesk data such as tickets and user records. Core entities include Tickets, People (end users), Organizations, Agents, Departments, Knowledge Base articles, Downloads, and News posts. Tickets contain threaded messages (agent replies, user replies, notes), each with their own attachments. Custom fields exist at the ticket, person, and organization level.

Jira Service Management is Atlassian's ITSM platform layered on top of Jira's issue tracker. The core entity is the Issue, which maps to a JSM Request when exposed through a service desk portal. Conversations are modeled as Comments on issues. Customer data maps to Customers and Organizations within JSM's customer management layer. JSM uses Request Types to define service categories, and workflows are managed through Jira's state-machine engine.

The key architectural differences that impact migration:

Concept Deskpro Jira Service Management
Core record Ticket (multi-message thread) Issue (comments-based)
Customer entity Person (with custom fields) Customer (limited custom fields)
Grouping Organization Organization
Agent grouping Departments, Teams Projects, Queues
Custom fields Per-ticket, per-person, per-org Per-issue type (project-scoped)
Knowledge base Built-in KB Confluence (separate product)
SLAs Per-department/org SLA rules SLA policies per project
Conversation model Messages (reply, note, agent_reply) Comments (public, internal)

Why Teams Migrate from Deskpro to JSM

  • Atlassian ecosystem consolidation. Teams already using Jira Software and Confluence want a single vendor for engineering and support.
  • ITSM maturity. JSM provides native ITIL workflows — incident, change, problem management — that Deskpro doesn't offer out of the box.
  • Asset management. JSM's built-in Assets (formerly Insight) provides CMDB capabilities for IT teams.
  • Automation and integration depth. JSM's automation engine and the Atlassian Marketplace provide broader integration options for teams scaling beyond pure helpdesk.
  • Cost restructuring. Teams with specific agent-count and feature requirements may find JSM's pricing tiers more favorable.

Migration Approaches: All Viable Methods Compared

There is no native "Deskpro → JSM" migration tool from either vendor. Every approach involves extracting data from Deskpro and loading it into JSM through one of these paths.

1. CSV Export/Import

How it works: Export ticket data from Deskpro using its built-in report engine (DPQL queries) to CSV. Import into JSM using Jira's CSV importer or the External System Import wizard.

When to use it: Only for the simplest migrations — under 1,000 tickets, no attachment requirements, no need for conversation history.

Each Deskpro report output can be exported to CSV/PDF, and even for large amounts of tickets, it should only take a few minutes to iterate through them all. However, if you have added a lot of custom ticket fields, this may not work — reports can only include up to about 55 fields. Deskpro's own guidance is to move to the API when the report builder stops being enough. (support.deskpro.com)

Pros:

  • No code required
  • Fast for small datasets
  • Good for a quick data audit before the real migration

Cons:

  • Loses full conversation threads (only ticket-level metadata)
  • No attachments
  • No person/organization relationships
  • 55-field limit on Deskpro reports
  • JSM CSV imports make comments public — if preserving internal/private notes matters, avoid this path (support.atlassian.com)
  • No automation possible for repeat runs

Complexity: Low | Scalability: Small datasets only | Risk: High data loss

How it works: Write a migration script that reads from Deskpro's REST API (/api/v2/tickets, /api/v2/people, /api/v2/organizations) and writes to JSM's REST API (/rest/api/3/issue, /rest/servicedeskapi/customer, /rest/servicedeskapi/organization). The script handles pagination, transformation, and relationship rebuilding.

When to use it: Any migration where you need to preserve conversation history, attachments, custom fields, or customer–organization links. This is the correct approach for 90%+ of real-world migrations.

You can use the Deskpro API to access all functions of the system. The simplest way to authenticate is by using an API key. On the JSM side, you authenticate with an API token (email + token for Cloud, PAT for Data Center).

Pros:

  • Full data fidelity — threads, attachments, custom fields
  • Scriptable and repeatable for test runs
  • Can preserve original timestamps (with workarounds)
  • Handles relationships natively

Cons:

  • Requires development effort (Python, Node.js, or similar)
  • Must handle rate limits on both sides
  • Attachment migration requires separate API calls per file
  • Author attribution requires mapping Deskpro agent IDs to JSM user accounts
  • Deskpro API returns only active tickets by default — you must explicitly request inactive statuses to include resolved and archived tickets (support.deskpro.com)

Complexity: High | Scalability: Enterprise-grade | Risk: Low (when properly implemented)

3. Third-Party Migration Tools

How it works: Services like Help Desk Migration offer wizard-based migration between helpdesk platforms. These tools connect to both Deskpro and JSM via their APIs and handle mapping through a web UI.

When to use it: Mid-size migrations (1K–50K tickets) where you want to avoid writing custom code but need better fidelity than CSV.

Deskpro's own docs point teams needing comprehensive platform migrations to Help Desk Migration. (support.deskpro.com) Treat vendor claims about capabilities as unverified until you validate them against your specific schema.

Pros:

  • No code required
  • Pre-built field mapping UI
  • Demo/test migration typically available

Cons:

  • Limited customization for edge cases
  • Per-record pricing can be expensive at scale
  • Opaque error handling — hard to debug failures
  • May not handle all custom field types or complex relationships
  • You still need target-side prep: agents, request types, statuses, fields

Complexity: Low–Medium | Scalability: Medium | Risk: Medium

4. Custom ETL Pipeline

How it works: Build a full extract-transform-load pipeline using an orchestration tool (Airflow, Dagster, or a simple queue-based system). Extract from Deskpro API into a staging database, transform data to match JSM's schema, then load via JSM API.

When to use it: Large-scale migrations (100K+ tickets), multi-instance consolidations, or migrations that need to run incrementally over days or weeks. This is the right pattern when Deskpro departments must fan out to several service projects, or when account and contract data lives outside native helpdesk objects.

Pros:

  • Full control over data transformations
  • Staging layer enables validation before loading
  • Supports incremental/delta migrations
  • Audit trail and retry logic built in

Cons:

  • Highest engineering investment
  • Requires infrastructure for staging database and orchestration
  • Overkill for straightforward migrations

Complexity: High | Scalability: Enterprise | Risk: Low (most controllable)

5. Middleware Platforms (Zapier, Make)

How it works: Use a workflow automation tool to connect Deskpro and JSM. Trigger on Deskpro events or schedule bulk reads, then create corresponding issues in JSM.

When to use it: Ongoing sync of new tickets during a phased cutover, not historical migration. These platforms are not designed for bulk data transfer.

Deskpro supports outbound webhooks, Zapier is available on both sides, and Make can fall back to raw HTTP modules. That makes iPaaS useful for deltas, not for rebuilding years of history.

Pros:

  • No code, visual builder
  • Good for continuous sync post-migration

Cons:

  • Not suitable for bulk historical migration
  • Task/operation limits make large migrations slow and expensive
  • Poor error handling for complex data structures
  • Cannot handle attachment migration reliably
  • No support for historical timestamp spoofing

Complexity: Low | Scalability: Small only | Risk: High for bulk migration

Migration Approach Comparison

Method Thread History Attachments Internal Notes Scale Engineering Effort
CSV Import No No No (public only) <1K tickets None
API-Based Script Yes Yes Yes Any High
Third-Party Tool Yes Yes Usually 1K–50K Low
Custom ETL Pipeline Yes Yes Yes 100K+ High
Middleware (Zapier/Make) Partial Partial Partial Small syncs only Medium

Recommendations by Scenario

  • Small team (<5K tickets), low complexity: Third-party migration tool or managed migration service.
  • Mid-market (5K–50K tickets), custom fields: API-based migration script or managed service.
  • Enterprise (50K+ tickets), multi-department: Custom ETL pipeline or managed migration service with staging layer.
  • Ongoing sync needed post-migration: API-based initial migration + middleware for ongoing new-ticket sync.
  • Low engineering bandwidth: Managed migration service — every time.

Why a Managed Migration Service Makes Sense

Building a Deskpro-to-JSM migration script isn't conceptually hard. The hard part is handling the 50+ edge cases that emerge at scale: duplicate customers, malformed HTML in ticket bodies, attachments exceeding JSM's size limits, rate limit throttling mid-migration, timezone mismatches on historical timestamps, and workflow transitions that refuse to land on the correct status.

When NOT to Build In-House

  • Your engineering team is already committed to product work
  • You've never worked with Deskpro's or JSM's APIs before
  • You have more than 20K tickets with attachments
  • You need the migration done in days, not weeks
  • You have complex custom field structures or multi-department configurations

Hidden Costs of DIY Migration

  • Engineering distraction: A senior engineer building and debugging a migration script for 2–4 weeks is a significant opportunity cost.
  • Data integrity risk: Broken relationships between customers and their ticket history are invisible until an agent tries to look up a customer's past interactions.
  • Retry overhead: A migration that fails at record 15,000 of 50,000 due to an unhandled edge case requires re-engineering and re-running — not just a restart.

DIY failures are rarely API failures. They are relationship failures: people loaded without organizations, agent notes exposed publicly, attachments orphaned, or request types mapped to the wrong workflow.

Why Teams Choose ClonePartner

ClonePartner has completed 1,500+ data migrations across helpdesk, CRM, and enterprise platforms. For Deskpro-to-JSM migrations, our team handles:

  • Full relationship preservation — customer-to-organization links, ticket ownership, and agent assignments
  • Custom field mapping — including Deskpro's per-department custom fields to JSM's project-scoped fields
  • Attachment migration — binary files transferred with original filenames and associated to the correct issues
  • Large dataset handling — rate limit management, batching, and incremental validation
  • Zero downtime — your support team keeps working in Deskpro during the migration

For a look at how we approach migrations internally, see How We Run Migrations at ClonePartner.

Pre-Migration Planning

A successful migration is won in the planning phase.

Data Audit Checklist

Before writing a single line of migration code, audit your Deskpro instance:

  • Ticket count by status (open, resolved, closed, archived), age, department, and channel
  • Attachment volume — total size and per-ticket count
  • People count — active users vs. inactive/spam
  • Organization count and membership mapping
  • Agent count and department assignments
  • Custom fields — list all ticket, person, and org custom fields with their types
  • Knowledge Base articles — count and structure (this is a separate migration to Confluence)
  • SLA configurations — rules, escalations, business hours
  • Automation rules/triggers — document for manual rebuild in JSM
  • CRM-style data — any accounts, contacts, or custom objects modeled through Deskpro custom fields or apps. These are not native Deskpro helpdesk objects; inventory them and decide whether they belong in JSM fields, customer/organization profiles, Assets, or a separate system.

What to Leave Behind

Not everything needs to migrate:

  • Spam/junk tickets
  • Test tickets from initial Deskpro setup
  • Users with zero ticket activity
  • Tickets older than your compliance retention window
  • Redundant custom fields that are no longer used
  • Automated password reset logs and system-generated tickets

Migration Strategy: Big Bang vs. Phased

Big bang: Migrate everything in a single window. Works for datasets under 50K tickets where the migration can complete in a weekend.

Phased: Migrate historical data first (closed tickets), validate, then migrate active tickets in a cutover window. Better for large datasets or teams that can't afford extended downtime.

Incremental: Migrate in batches with delta syncs. Required when the migration takes longer than a few days and agents are still creating tickets in Deskpro during the process.

A practical approach: migrate closed/archived tickets over a weekend, then sync delta changes (active tickets) just before final cutover.

Data Model & Object Mapping

This is where migrations succeed or fail. Deskpro and JSM model the same real-world concepts with different structures.

Core Object Mapping

Deskpro Object JSM Equivalent Notes
Ticket Issue (Service Request) One Deskpro ticket = one JSM issue. Must be assigned a specific Request Type so it appears correctly in the JSM customer portal.
Ticket Message (reply) Comment (public) Agent replies and user messages that were customer-visible.
Ticket Note Comment (internal) Restricted to "Service Desk Team" role in JSM.
Person (end user) Customer JSM customers are Atlassian accounts. Must be resolved to accountId, not email.
Organization Organization JSM organizations are project-scoped. A customer can belong to one organization per project.
Agent Agent (licensed user) Must exist in Atlassian before migration.
Department Service Desk Project One department typically maps to one JSM project. Can also map to Components within a single project.
Ticket Status Issue Status (Workflow) Requires workflow configuration. JSM issues must follow the assigned workflow — you cannot force an issue into "Closed" if the workflow requires it to pass through "In Progress" and "Resolved" first.
Ticket Priority Priority JSM has default priorities; can add custom ones.
Ticket Category Labels or Custom Field No direct equivalent; use labels or a select field.
Custom Ticket Field Custom Field Types must be matched (text→text, select→select).
Person Custom Field Customer property Very limited in JSM. Data may need to migrate to a linked Assets object or a custom field on issues.
Org Custom Field Org property Very limited in JSM. Consider using Assets.
SLA SLA Policy Must be rebuilt manually in JSM. No import path.
Knowledge Base Article Confluence Page Requires separate migration to Confluence.
Attachment Attachment Migrated per-issue via separate API call (multipart/form-data).
Tags/Labels Labels Direct mapping.
CCs/Participants Request Participants Requires accountIds, not emails.

Field-Level Mapping

Deskpro Field JSM Field Transformation Notes
ticket.id Custom field deskpro_ticket_id JSM auto-generates issue keys (e.g., IT-101). Store Deskpro ID for traceability and delta re-runs.
ticket.subject issue.summary Max 255 characters in JSM.
ticket.status issue.status Map awaiting_user, awaiting_agent, resolved, closed to JSM workflow states. Requires executing workflow transitions in order.
ticket.priority issue.priority Map Deskpro priority values to JSM priority IDs.
person.primary_email issue.reporter Must be resolved to an Atlassian accountId via the user search API. Use email as the dedupe key.
agent.email issue.assignee Must be resolved to an accountId.
ticket.department_id issue.project Map department to JSM project. This should drive routing, not be stored as dead text.
ticket.date_created issue.created Read-only in JSM. Store original creation date in a custom field. Some migration services have workarounds to set historical dates.
ticket.labels issue.labels Normalize case and separators.
ticket.custom_fields.* issue.customfield_* Match field types carefully. Deskpro allows nested/hierarchical picklist choices; JSM supports single-select and cascading select (two levels only). Deep hierarchies must be flattened.
message.message comment.body HTML → ADF (v3 API) or wiki markup (v2 API). See conversion notes below.
message.is_note comment.visibility true → internal comment (role: "Service Desk Team").
message.person_id comment.author Requires impersonation or explicit note about original author.
person.name customer.displayName Direct copy.
organization.name organization.name Create the org before loading shared requests.
attachment.filename attachment.filename Direct copy. Download from Deskpro then re-upload via multipart/form-data.
Warning

HTML-to-ADF conversion is a common failure point. Jira's v3 API expects Atlassian Document Format (a JSON structure), not raw HTML. If you POST HTML as a comment body, it renders as raw markup. Options: use the v2 API (/rest/api/2/) which accepts wiki notation or plain text, convert HTML to ADF using Atlassian's @atlaskit/editor-json-transformer library, or strip HTML to plain text (lossy but simple). Nested tables, inline styles, and complex blockquotes in Deskpro HTML will cause standard converters to fail with HTTP 400 errors. You must build a robust parser or accept lossy conversion.

Handling Relationships and Dependencies

Migration order matters. Load data in this sequence:

  1. Organizations — create JSM organizations first
  2. Customers — create customers and associate with organizations
  3. Agents — ensure all agents exist as licensed JSM users
  4. Issues — create issues with correct reporter (customer) and assignee (agent)
  5. Comments — add in chronological order to preserve conversation flow
  6. Attachments — upload per-issue after issue creation

Migration Architecture

Data Flow

Deskpro REST API → Extract Script → Staging Store (JSON/DB) → Transform → JSM REST API

The extraction and load phases must be decoupled to handle API limits safely. Extract raw JSON from Deskpro and store it locally before transforming and loading. This lets you retry the load phase without re-extracting.

Deskpro API: Extraction Details

You can specify API limits on every API key you create. For example, it is not uncommon to set an upper limit to prevent mistakes such as a flood or infinite loop situation. You can define these limits in terms of an hourly limit or a daily limit. For cloud customers, there is also a default global limit to prevent abuse. You can raise this on request by contacting support@deskpro.com.

Key Deskpro API endpoints for extraction:

  • GET /api/v2/tickets — paginated ticket list with filters
  • GET /api/v2/tickets/{id}/messages — full message thread
  • GET /api/v2/tickets/{id}/attachments — file attachments
  • GET /api/v2/people — user/customer records
  • GET /api/v2/organizations — organization records
  • GET /api/v2/ticket_custom_fields — custom field definitions

Resource collections should include "count", "total_count", "page", "total_pages" — use these pagination meta fields to track extraction progress.

A superuser API key is a special type of API key that is not associated with a particular agent. Instead, it is able to make requests as any agent. When you create a superuser API key, you can optionally set a default agent. If you don't set a default agent, each request must identify the agent. Use a superuser key for migration to access all data regardless of agent permissions.

Critical: Deskpro's API returns only active tickets by default. You must explicitly request inactive statuses (resolved, archived, hidden) to include them. Missing this is a common false-success scenario where the migration appears complete but is missing a large portion of your ticket history.

Jira Service Management API: Loading Details

Enforcement of the new points-based API rate limits and tiered quota rate limits for Jira and Confluence Cloud apps began on March 2, 2026. API token-based traffic is not affected by this change and will continue to be governed by existing burst rate limits.

For migration scripts using API tokens (Basic Auth with email + token), you're subject to burst rate limits, not the new points-based system. If your app or integration exceeds the allowed number of API requests in a short period, you will receive a "429: Too Many Requests" error. These limits are enforced on a per-tenant per-API basis. Your loader must respect the Retry-After header. Failure to do so will result in a temporary IP ban.

Per-issue entity limits in Jira Cloud:

In Jira Cloud, each issue allows 5,000 comments, 10,000 worklogs, and 2,000 each for attachments, issue links, and remote links. If any Deskpro ticket has more than 5,000 messages, you'll need to split it across linked JSM issues or truncate older messages.

Attachment handling: Creating an issue requires posting a JSON payload. Adding an attachment requires posting MultiPartMime. Both at the same time is not possible. You must create the issue first, then attach files in a separate request. Jira doesn't restrict attachments to a specific list of file types by default — images, documents, code, archives, audio, and video all work, with a default 1 GB per-file limit on Cloud.

The JSM request API requires serviceDeskId, requestTypeId, and requestFieldValues when creating requests through the service desk API. Alternatively, you can use the core Jira issue API (/rest/api/3/issue) with project, issuetype, and standard fields.

Authentication

Deskpro: The simplest way to authenticate is by using an API key. You can create an API key via Deskpro's admin interface, under Apps & Integrations > API Keys.

JSM Cloud: Basic Auth with email address + API token generated from id.atlassian.com. Include as Authorization: Basic base64(email:token).

JSM Data Center: Jira DC supports Personal Access Tokens (PAT, available from DC 7.9+) as the recommended modern auth method, or Basic Auth (username:password).

Step-by-Step Migration Process

Step 1: Extract Data from Deskpro

Paginate through all entities. Store raw JSON responses in a staging directory or database.

import requests
import json
import time
import os
 
DESKPRO_URL = "https://yourcompany.deskpro.com/api/v2"
HEADERS = {
    "Authorization": "key YOUR_API_KEY",
    "Content-Type": "application/json"
}
 
def extract_all(endpoint, output_dir):
    page = 1
    all_records = []
    while True:
        resp = requests.get(
            f"{DESKPRO_URL}/{endpoint}?page={page}&count=50",
            headers=HEADERS
        )
        if resp.status_code == 429:
            time.sleep(60)  # Back off on rate limit
            continue
        data = resp.json()
        records = data.get("data", [])
        if not records:
            break
        all_records.extend(records)
        page += 1
        time.sleep(0.5)  # Respectful pacing
    
    os.makedirs(output_dir, exist_ok=True)
    with open(f"{output_dir}/{endpoint}.json", "w") as f:
        json.dump(all_records, f)
    return all_records
 
# Extract core entities
extract_all("organizations", "./staging")
extract_all("people", "./staging")
extract_all("tickets", "./staging")

For each ticket, also extract messages and attachments:

def extract_ticket_threads(tickets, output_dir):
    for ticket in tickets:
        tid = ticket["id"]
        msgs = requests.get(
            f"{DESKPRO_URL}/tickets/{tid}/messages",
            headers=HEADERS
        ).json().get("data", [])
        with open(f"{output_dir}/messages_{tid}.json", "w") as f:
            json.dump(msgs, f)
        time.sleep(0.3)

Step 2: Resolve Account IDs in JSM

JSM does not allow assigning tickets via email address. You must query the Atlassian API to find each user's accountId.

JIRA_URL = "https://yourdomain.atlassian.net"
JIRA_AUTH = ("admin@yourdomain.com", "YOUR_ATLASSIAN_API_TOKEN")
 
def get_jira_account_id(email):
    params = {"query": email}
    response = requests.get(
        f"{JIRA_URL}/rest/api/3/user/search",
        auth=JIRA_AUTH,
        params=params
    )
    users = response.json()
    if users:
        return users[0].get("accountId")
    return None

Build a full lookup table mapping Deskpro person emails to JSM accountIds before starting the load phase. This avoids repeated lookups during issue creation.

Step 3: Transform Data

Map Deskpro fields to JSM fields. Key transformations:

STATUS_MAP = {
    "awaiting_user": "Waiting for customer",
    "awaiting_agent": "Waiting for support",
    "resolved": "Resolved",
    "closed": "Closed",
    "open": "Open"
}
 
PRIORITY_MAP = {
    "urgent": {"id": "1"},   # Highest
    "high": {"id": "2"},     # High
    "normal": {"id": "3"},   # Medium
    "low": {"id": "4"}       # Low
}

Convert HTML message bodies to ADF for the v3 API, or use the v2 API with wiki notation. Deduplicate customer records by email before loading. Normalize labels for case and separators.

Step 4: Load Issues into JSM

Create issues via the JSM REST API. When creating the issue, format the description in Atlassian Document Format (ADF) for the v3 API.

def create_jsm_issue(ticket, customer_id, project_key, issue_type_id):
    payload = {
        "fields": {
            "project": {"key": project_key},
            "issuetype": {"id": issue_type_id},
            "summary": ticket["subject"],
            "priority": PRIORITY_MAP.get(ticket["priority"], {"id": "3"}),
            "reporter": {"id": customer_id},
            "labels": ticket.get("labels", []),
        }
    }
    resp = requests.post(
        f"{JIRA_URL}/rest/api/3/issue",
        json=payload,
        auth=JIRA_AUTH
    )
    if resp.status_code == 429:
        retry_after = int(resp.headers.get("Retry-After", 60))
        time.sleep(retry_after)
        return create_jsm_issue(ticket, customer_id, project_key, issue_type_id)
    return resp.json()

Step 5: Add Comments in Chronological Order

Add comments in chronological order to preserve the conversation flow. Use the internal visibility flag for agent notes.

def add_comment(issue_key, message, is_internal=False):
    endpoint = f"{JIRA_URL}/rest/api/2/issue/{issue_key}/comment"
    body = {
        "body": message["message_text"],
    }
    if is_internal:
        body["visibility"] = {
            "type": "role",
            "value": "Service Desk Team"
        }
    resp = requests.post(endpoint, json=body, auth=JIRA_AUTH)
    return resp
Tip

Author attribution: If you create all comments using a single admin API key, every comment appears as the admin. To preserve original authorship, you need separate API tokens per agent or use Atlassian's impersonation (available only on certain tiers). Jira Cloud falls back to the importing user if the comment author does not already exist on the site with the right permissions. (support.atlassian.com) This is the single most common fidelity loss in helpdesk migrations.

Step 6: Upload Attachments

def upload_attachment(issue_key, file_path, filename):
    headers = {"X-Atlassian-Token": "no-check"}
    with open(file_path, "rb") as f:
        resp = requests.post(
            f"{JIRA_URL}/rest/api/3/issue/{issue_key}/attachments",
            headers=headers,
            files={"file": (filename, f)},
            auth=JIRA_AUTH
        )
    return resp

Putting It Together

A clean migration script ties these steps together with proper logging and error handling:

def migrate_ticket(ticket):
    customer_id = ensure_customer(ticket['person'])
    org_id = ensure_org(ticket.get('organization'))
    issue = create_jsm_issue(ticket, customer_id, PROJECT_KEY, ISSUE_TYPE_ID)
    issue_key = issue.get("key")
    
    for msg in sorted(ticket['messages'], key=lambda m: m['date_created']):
        add_comment(issue_key, msg, is_internal=msg.get('is_note', False))
    
    for attachment in ticket.get('attachments', []):
        upload_attachment(issue_key, attachment['local_path'], attachment['filename'])
    
    save_id_map('ticket', ticket['id'], issue_key)
 
for ticket in fetch_deskpro_tickets(
    statuses=['live','awaiting_agent','awaiting_user','pending','resolved','archived','hidden']
):
    with retry_on_429_and_5xx():
        migrate_ticket(ticket)

Log every source ID, target ID, payload hash, response code, and retry count. Failed records should go to a dead-letter queue, not disappear into stdout.

Edge Cases & Challenges

These are the issues that derail migrations at scale.

Duplicate Customers

Deskpro may have multiple Person records for the same email (created across different channels). JSM requires unique email addresses per customer. Deduplicate before migration — merge ticket ownership to the canonical person record.

Inline Images in HTML Messages

Deskpro stores message content as HTML. Inline images reference Deskpro's internal attachment URLs which won't resolve after migration. You must download each inline image, upload it as a JSM attachment, and rewrite the reference in the ADF body. Skipping this step means images silently disappear from migrated tickets.

Multi-Department Ticket Transfers

Deskpro tickets can be moved between departments. In JSM, departments map to separate projects. A ticket that was transferred between departments may need to be placed in the final department's project, with a note indicating its history.

Inactive Users

If a user left the company and is deactivated in Atlassian Access, you cannot assign them as a reporter. Options: temporarily reactivate them for migration, or map their tickets to a generic "Legacy User" account.

Workflow Constraints

JSM issues must follow the assigned workflow. You cannot force an issue into a "Closed" state if the workflow requires it to pass through "In Progress" and "Resolved" first. Your migration script must execute the exact workflow transitions in order, or you must create a temporary "Migration Workflow" that allows global transitions to any status. Switch back to the production workflow after migration.

Tickets with 5,000+ Messages

New per-issue limits in Jira Cloud limit the maximum number of comments to 5,000 per issue. Once the limit has been reached, you won't be able to add more comments to the issue. If any Deskpro ticket exceeds this, split it across linked JSM issues or truncate older messages.

SLA and Automation Migration

Deskpro SLA rules and automation triggers have no import path into JSM. You must manually recreate SLA policies with calendar/business hours, automation rules, escalation rules, and trigger-based assignments. Document all active automations in Deskpro before migration. See Your Help Desk Data Migration's Secret Saboteur: Automations, Macros, and Workflows for a detailed approach.

Knowledge Base

Deskpro's built-in KB doesn't map into JSM. JSM uses Confluence for knowledge base functionality. This is a separate migration with its own challenges — article formatting, category structure, embedded media. Plan it as a distinct workstream.

Satisfaction Ratings

Deskpro CSAT data has no standard import path into JSM. If you need historical satisfaction data, export it separately and store it in a reporting tool or custom field.

Chat Transcripts

Deskpro live chat threads may not map cleanly to JSM's comment model. Expect some flattening of chat-specific metadata.

Limitations & Constraints

JSM-Side Limitations

  • No custom objects: JSM doesn't support arbitrary custom objects. Use Assets (CMDB) for structured data that doesn't fit into issue custom fields. JSM is not a generic object database.
  • Customer custom fields: Very limited. JSM customers are Atlassian accounts with minimal extensible properties.
  • Organization scope: JSM organizations are scoped per service desk project. A customer can belong to one organization per project.
  • Request Type rigidity: JSM request types define the fields shown to customers. Mapping Deskpro's department-based field visibility to JSM's request type model requires careful planning.
  • Customer Service Management boundary: On Atlassian Cloud sites created after October 10, 2025, richer customer and organization profile features live in Customer Service Management rather than core JSM. Factor this into your target-side configuration.

Deskpro-Side Extraction Constraints

  • Rate limits: Cloud instances have a default global rate limit. When you hit a rate limit, requests will begin to fail with an HTTP status code 429 Too Many Requests error. Contact Deskpro support to raise limits before a migration.
  • No bulk export API: Every record must be fetched through paginated list endpoints. There's no bulk archive download.
  • Attachment download: Attachments must be downloaded one at a time from the API. For large attachment volumes, this is the slowest phase of extraction.
  • Inactive ticket visibility: The API returns only active tickets by default. Explicitly include resolved, archived, and hidden statuses or you will miss a significant portion of your data.

Validation & Testing

Never run a migration directly into production without testing.

Sandbox Run

Test migration on a JSM Sandbox environment first. Migrate 5% of your data to catch mapping issues before the full run.

Record Count Comparison

After migration, compare counts:

Entity Deskpro Count JSM Count Match?
Organizations ___ ___ ✅/❌
Customers ___ ___ ✅/❌
Tickets/Issues ___ ___ ✅/❌
Comments (total) ___ ___ ✅/❌
Attachments ___ ___ ✅/❌

Field-Level Spot Checks

Randomly sample 2–5% of migrated tickets. For each, verify:

  • Subject line matches
  • All messages/comments present and in correct chronological order
  • Attachments accessible and not corrupted
  • Custom field values transferred correctly
  • Reporter and assignee correctly mapped
  • Status and priority match expected mapping
  • Internal notes are not visible to customers

Spot-check complex tickets with 20+ replies, inline images, and mixed public/internal notes. Ensure visibility permissions match exactly.

UAT Process

  1. Have 2–3 agents review their own historical tickets — they'll spot errors faster than any script
  2. Verify SLA policies trigger correctly on migrated issues
  3. Test customer portal — can customers see their migrated tickets?
  4. Verify search works for historical records

Rollback Planning

If the migration fails or produces unacceptable results:

  • JSM Cloud: Bulk-delete migrated issues using JQL + bulk operations or the API
  • JSM Data Center: Restore from a pre-migration database backup
  • Always keep Deskpro running until JSM is fully validated — don't cancel your Deskpro subscription until agents have worked in JSM for at least 2 weeks

For a comprehensive post-migration testing framework, see Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration.

Post-Migration Tasks

Once the data is moved, the operational cutover begins.

Rebuild Automations and Workflows

JSM's automation engine is powerful but structurally different from Deskpro's triggers. Recreate:

  • Auto-assignment rules
  • SLA escalation paths
  • Customer notification templates
  • Status transition triggers
  • Macro/canned response equivalents (Deskpro saved replies → JSM automation templates)

Update Mail Handlers

Reroute your support email addresses (e.g., support@company.com) from Deskpro to JSM.

Agent Training

JSM's agent interface is significantly different from Deskpro's. Plan for:

  • Queue management training (JSM queues vs. Deskpro views)
  • Workflow transition training (Jira-style status workflows rather than "Reply and Close")
  • Knowledge base integration (Confluence linking)
  • Customer portal differences
  • The difference between public replies and internal notes in JSM's comment model

Monitor for Data Inconsistencies

For the first 2 weeks post-migration:

  • Monitor agent-reported issues with missing data
  • Check for orphaned customers (no associated tickets)
  • Verify attachment access logs for 404 errors
  • Compare reporting numbers between legacy Deskpro reports and new JSM dashboards
  • Watch for automation failures, sync gaps, and unexpected notification bursts

Decommission Deskpro

Keep Deskpro in a read-only state for 30–60 days post-migration to ensure no edge-case data was missed before shutting down the servers or canceling the subscription.

Best Practices

  • Disable JSM notifications during migration. Turn off outgoing email notifications in your Jira Service Management project settings before running the migration script. Otherwise, your customers will receive thousands of "Ticket Created" emails for tickets resolved years ago.
  • Back up Deskpro data before starting. Export a full database backup (on-premise) or request a data export from Deskpro Cloud support.
  • Run at least two test migrations before the live cutover. The first exposes mapping issues. The second validates fixes. Don't rely on a 20-record smoke test.
  • Validate incrementally. Don't wait until all 50K tickets are loaded to check if the mapping is correct. Validate after the first 100.
  • Track ID mappings. Maintain a lookup table of Deskpro IDs → JSM issue keys. You'll need this for relationship rebuilding, debugging, and delta re-runs.
  • Log everything. Your migration script must log the Deskpro ID, the resulting JSM Issue Key, and the HTTP status code of every API call. This is your audit trail for troubleshooting.
  • Automate everything repeatable. The transform and load steps should be fully scripted. Manual CSV manipulation introduces errors at scale.
  • Communicate the timeline to your support team. Agents need to know when to stop working in Deskpro and start in JSM.
  • Create a temporary migration workflow if needed. If JSM's default workflow prevents direct transitions to specific states, create a simplified workflow that allows global transitions, run the migration, then switch back to the production workflow.
  • Make your loader idempotent. If a load run fails partway through, your script should be able to pick up where it left off without creating duplicates.

Making the Right Call

Deskpro-to-JSM migration is a solvable problem, but it's not a simple one. The API-based path is the only way to maintain data fidelity. CSV exports are fine for a quick look at your data but will leave you with hollow ticket records and broken relationships.

The biggest risk isn't technical — it's underestimating the scope. What looks like a straightforward export-import becomes a multi-week project once you account for custom field mapping, HTML-to-ADF conversion, attachment re-linking, workflow transitions, and author attribution. Teams with limited engineering bandwidth or tight timelines should seriously consider a managed migration service.

For more migration guidance, see the Zoho Desk to Jira Service Management Migration: The CTO's Guide for a parallel reference or Deskpro to Zoho Desk Migration: A Technical Guide if you're still evaluating destination platforms.

Frequently Asked Questions

Can I migrate Deskpro to Jira Service Management using CSV export?
Only partially. Deskpro's CSV export captures ticket metadata but does not include full conversation threads, attachments, or customer–organization relationships. Deskpro reports also have a ~55-field limit. JSM CSV imports make all comments public, so you lose internal/private note distinctions. For anything beyond basic ticket data, you need an API-based migration.
What are the API rate limits for Deskpro and JSM during migration?
Deskpro Cloud has a default global rate limit configurable per API key; contact support@deskpro.com to raise it before a migration. JSM Cloud enforces burst rate limits per-tenant per-API — exceeding them returns a 429 error with a Retry-After header. API token-based traffic uses burst limits, not the newer points-based system introduced in March 2026.
How long does a Deskpro to JSM migration take?
Timelines depend on volume. Under 10K tickets with minimal attachments: 3–5 business days. 10K–100K tickets with attachments: 1–3 weeks. Over 100K tickets: 3–6 weeks. These estimates include test runs, validation, and cutover.
Does Jira Service Management have per-issue limits that affect migration?
Yes. Jira Cloud enforces per-issue limits: 5,000 comments, 2,000 attachments, 2,000 issue links, and 2,000 remote links per issue. Deskpro tickets with very long conversation threads exceeding 5,000 messages may need to be split across multiple linked JSM issues.
How do I prevent JSM from sending emails to customers during the migration?
Disable outgoing email notifications in your Jira Service Management project settings before running the migration script. Otherwise, customers will receive notification emails for every historical ticket being recreated. Re-enable notifications only after migration and validation are complete.

More from our Blog

Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration
Help Desk

Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration

Ensure your help desk migration is a success with this comprehensive 20-point post-migration QA checklist. This expert guide details the 20 essential tests needed to validate your data integrity, system functionality, user-friendliness, and performance . Learn exactly how to check everything from ticket data, attachments, and knowledge base articles to critical workflows, automations, and integrations before you go live. This process is your final line of defense against lost tickets, broken workflows, and unhappy customers.

Raaj Raaj · · 8 min read