---
title: "Deskpro to Jira Service Management Migration: Technical Guide"
slug: deskpro-to-jira-service-management-migration-technical-guide
date: 2026-07-22
author: Nachi
categories: [Migration Guide, Jira Service Management]
excerpt: "A technical guide to migrating from Deskpro to Jira Service Management—covering API constraints, data mapping, ADF conversion, and edge cases."
tldr: "Deskpro to JSM migration requires API-based extraction to preserve ticket threads, attachments, and internal notes. CSV flattens everything. Plan for HTML-to-ADF conversion and per-issue limits."
canonical: https://clonepartner.com/blog/deskpro-to-jira-service-management-migration-technical-guide/
---

# Deskpro to Jira Service Management Migration: Technical Guide


> [!NOTE]
> **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 `accountId`s. 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](https://support.atlassian.com/jira-service-management-cloud/docs/what-is-customer-service-management/))

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](https://clonepartner.com/blog/blog/jira-service-management-migration-checklist/). For a deeper look at JSM's architecture, see [The Ultimate Jira Service Management Guide for 2026](https://clonepartner.com/blog/blog/ultimate-guide-jira-service-management-2026/).

## Overview: Deskpro vs Jira Service Management Architecture

**Deskpro** is a multi-channel helpdesk platform with a CRM-like data model. <cite index="3-1,3-2">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.</cite> 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.

<cite index="24-2,24-3">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.</cite> However, <cite index="24-5">if you have added a lot of custom ticket fields, this may not work — reports can only include up to about 55 fields.</cite> Deskpro's own guidance is to move to the API when the report builder stops being enough. ([support.deskpro.com](https://support.deskpro.com/en/kb/articles/how-do-i-export-my-tickets))

**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](https://support.atlassian.com/jira-cloud-administration/docs/import-data-from-a-csv-file/))
- No automation possible for repeat runs

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

### 2. API-Based Migration (Recommended)

**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.

<cite index="5-6,5-7">You can use the Deskpro API to access all functions of the system. The simplest way to authenticate is by using an API key.</cite> 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](https://support.deskpro.com/en-US/kb/articles/pdf/basic-api-usage))

**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](https://support.deskpro.com/en-US/kb/articles/can-i-import-data-from-other-systems-or-helpdesks-1)) 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](https://clonepartner.com/blog/blog/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 `accountId`s, 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

<cite index="48-1,48-2,48-3">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.</cite> <cite index="48-4,48-5">For cloud customers, there is also a default global limit to prevent abuse. You can raise this on request by contacting support@deskpro.com.</cite>

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

<cite index="48-17">Resource collections should include "count", "total_count", "page", "total_pages"</cite> — use these pagination meta fields to track extraction progress.

<cite index="7-3,7-4,7-5,7-6">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.</cite> 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

<cite index="13-6,13-7">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.</cite> <cite index="13-9">API token-based traffic is not affected by this change and will continue to be governed by existing burst rate limits.</cite>

For migration scripts using API tokens (Basic Auth with email + token), you're subject to burst rate limits, not the new points-based system. <cite index="11-6,11-7">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.</cite> 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:**

<cite index="62-1">In Jira Cloud, each issue allows 5,000 comments, 10,000 worklogs, and 2,000 each for attachments, issue links, and remote links.</cite> 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:** <cite index="44-8,44-9,44-10">Creating an issue requires posting a JSON payload. Adding an attachment requires posting MultiPartMime. Both at the same time is not possible.</cite> You must create the issue first, then attach files in a separate request. <cite index="46-5">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.</cite>

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:** <cite index="5-7,5-8">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.</cite>

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

**JSM Data Center:** <cite index="39-5">Jira DC supports Personal Access Tokens (PAT, available from DC 7.9+) as the recommended modern auth method, or Basic Auth (username:password).</cite>

## 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.

```python
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:

```python
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`.

```python
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 `accountId`s before starting the load phase. This avoids repeated lookups during issue creation.

### Step 3: Transform Data

Map Deskpro fields to JSM fields. Key transformations:

```python
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.

```python
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.

```python
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](https://support.atlassian.com/jira/kb/how-to-preserve-comment-author-details-when-importing-issues-via-csv/)) This is the single most common fidelity loss in helpdesk migrations.

### Step 6: Upload Attachments

```python
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:

```python
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

<cite index="58-1,58-2,58-3">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.</cite> 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](https://clonepartner.com/blog/blog/how-to-migrate-automations-macros-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. <cite index="48-6">When you hit a rate limit, requests will begin to fail with an HTTP status code 429 Too Many Requests error.</cite> 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](https://clonepartner.com/blog/blog/help-desk-data-migration-qa-checklist/).

## 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](https://clonepartner.com/blog/blog/zoho-desk-to-jira-service-management-migration-the-ctos-guide/) for a parallel reference or [Deskpro to Zoho Desk Migration: A Technical Guide](https://clonepartner.com/blog/blog/deskpro-to-zoho-desk-migration-a-technical-guide/) if you're still evaluating destination platforms.

> Need to migrate from Deskpro to Jira Service Management without losing ticket threads, attachments, or customer data? ClonePartner's engineering team handles the hard parts — ADF conversion, rate limits, field mapping, relationship preservation — so your team stays focused on support. Book a free 30-minute migration assessment.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## 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.
