---
title: "Deskpro to Kustomer Migration: A Technical Guide"
slug: deskpro-to-kustomer-migration-a-technical-guide
date: 2026-08-20
author: Rishabh
categories: [Kustomer, Migration Guide, Help Desk]
excerpt: "Technical guide to migrating from Deskpro to Kustomer. Covers API extraction, data model mapping, custom field translation, rate limits, and cutover."
tldr: Migrating Deskpro to Kustomer requires a custom API pipeline translating ticket-centric records into Kustomer's customer-centric model with typed attributes. Plan 2–6 weeks.
canonical: https://clonepartner.com/blog/deskpro-to-kustomer-migration-a-technical-guide/
---

# Deskpro to Kustomer Migration: A Technical Guide


# Deskpro to Kustomer Migration: A Technical Guide

> [!NOTE]
> **TL;DR — Deskpro to Kustomer Migration**
>
> A Deskpro to Kustomer migration is a **data-model inversion**. You are translating Deskpro's ticket-centric architecture — where Tickets, Messages, and Notes sit under Departments — into Kustomer's customer-centric CRM model where Conversations, Messages, and KObjects all hang off a unified Customer record. There is no native import path between these platforms; Kustomer documents a Zendesk import tool and general API migration guidance, but not a Deskpro importer. ([help.kustomer.com](https://help.kustomer.com/import-data-from-zendesk-HygpFcoXF)) Every migration requires a custom API pipeline: Deskpro REST API v2 for extraction, Kustomer REST API v1 for loading. Realistic timeline: **2–3 weeks** for under 50K tickets, **4–6 weeks** for larger datasets with complex custom field schemas. The biggest architectural challenges: translating Deskpro's flat custom fields into Kustomer's **typed attributes with strict naming conventions**, handling inline image rewrites, preserving original timestamps with the `importedAt` flag, and managing Kustomer's tiered rate limits during bulk loading.

## What Is a Deskpro to Kustomer Migration?

A Deskpro to Kustomer migration extracts Tickets, Messages, Notes, People, Organizations, Labels, Custom Fields, Agents, Agent Teams, Departments, and Knowledgebase Articles from Deskpro and reconstructs them to fit Kustomer's customer-centric model of Customers, Conversations, Messages, Notes, Companies, Tags, Custom Attributes, Users, Teams, and Knowledge Base Articles.

Before writing a single line of migration code, answer this: **do your agents need full historical conversation threads with inline attachments, or just contact records and metadata?** If agents will reference past interactions after cutover — and they almost always do — you need a full API-to-API ETL pipeline, not a CSV shuffle. Deskpro's CSV exports do not include full message bodies, inline images, or attachments. Kustomer's CSV importer only supports Customers, Users, Teams, and Companies — not Conversations or Messages.

**Deskpro** is a ticket-centric helpdesk platform. Every customer interaction becomes a Ticket with Messages, organized under Departments. People and Organizations hold contact data. Custom fields extend Tickets and People as flat key-value pairs. Deskpro offers both cloud and self-hosted (on-premise) deployments.

**Kustomer** is a customer-centric CRM and support platform built around a unified timeline. Every interaction — email, chat, SMS, social — hangs off a single Customer record. Custom data objects (**KObjects**) defined by Klass schemas let teams model business entities like orders, subscriptions, or shipments directly within the platform.

If you are evaluating the reverse direction, see our [Kustomer to Deskpro migration guide](https://clonepartner.com/blog/blog/kustomer-to-deskpro-migration-the-complete-technical-guide/).

## Why Teams Move from Deskpro to Kustomer

The most common triggers:

- **Unified customer timeline.** Deskpro organizes work by ticket. Kustomer organizes work by customer. Teams that handle high-touch, relationship-heavy support — e-commerce, fintech, subscription businesses — benefit from seeing every interaction, order, and data point in one chronological view rather than jumping between ticket IDs.
- **Custom data objects (KObjects).** Deskpro custom fields are flat key-value pairs on Tickets or People. Kustomer lets you model structured business entities — orders, shipments, subscriptions — as first-class objects tied to customer records, with up to 500 custom attributes per Klass.
- **Pricing model.** Deskpro publishes per-agent pricing on cloud plans. Kustomer offers both per-seat and per-conversation pricing. For teams with many occasional agents or BPO-heavy operations, the conversation model can change the math. The trade-off works both ways: Deskpro has a real advantage for teams that need self-hosted or sovereign deployment options. ([deskpro.com](https://www.deskpro.com/pricing/cloud))
- **Omnichannel consolidation.** Both platforms support email, chat, and voice. Kustomer adds native WhatsApp, SMS, Instagram, and Facebook messaging channels that feed directly into the customer timeline.

## Core Architecture Differences

Understanding these structural differences is the foundation of your migration plan.

| Concept | Deskpro | Kustomer |
|---|---|---|
| **Core unit** | Ticket | Customer |
| **Interactions** | Tickets → Messages | Conversations → Messages |
| **Contacts** | People | Customers |
| **Companies** | Organizations | Companies |
| **Grouping/routing** | Departments | Teams + Queues |
| **Tags** | Labels | Tags |
| **Staff** | Agents | Users |
| **Staff groups** | Agent Teams | Teams |
| **Custom data** | Custom Fields (flat key-value) | Custom Attributes on Klasses + KObjects |
| **Automation** | Triggers + Automations | Workflows + Business Rules |
| **Help center** | Knowledgebase | Knowledge Base |
| **API** | REST API v2 (`/api/v2/`) | REST API v1 (`/v1/`) |
| **Pagination** | Offset-based | Cursor-based |

The single most important distinction: **Deskpro is ticket-centric, Kustomer is customer-centric.** In Deskpro, a Person can have many Tickets, and each Ticket is a self-contained unit of work. In Kustomer, a Customer is the root object — Conversations, Messages, KObjects, and Notes all hang off that single Customer record.

This inversion means your migration script must group Deskpro Tickets by Person, create a Kustomer Customer first, then attach Conversations (mapped from Tickets) and Messages underneath. If you attempt a flat export/import, you will orphan ticket threads, lose inline attachments, and break historical reporting.

## Object-by-Object Data Mapping

### People → Customers

Deskpro People become Kustomer Customers. Map `email`, `name`, `phone` directly. Deskpro stores phone numbers as nested objects under People — flatten these into Kustomer's `phones` array. Deskpro's multi-email support maps cleanly to Kustomer's `emails` array.

> [!WARNING]
> **Custom field mapping matters here.** Deskpro custom fields on People are untyped key-value pairs. Kustomer requires you to pre-define Custom Attributes on the Customer Klass with explicit data-type suffixes: `Str` (string), `Num` (number), `Bool` (boolean), `At` (date-time), `Url` (URL). A Deskpro text field named "Subscription Tier" must become something like `subscriptionTierStr` in Kustomer. Plan and create all Custom Attributes **before** importing any data.
>
> **What silent rejection looks like:** When you POST a Customer with an attribute that is missing the required suffix or uses an unregistered attribute name, Kustomer returns HTTP 201 (success) with the customer record created — but the offending attribute is simply absent from the response body. There is no error key, no warning field, and no 4xx status code. The only way to detect dropped attributes is to compare your request payload against the response body field by field. This behavior makes pre-migration attribute schema validation mandatory, not optional.

Kustomer enforces uniqueness on email, phone, and external ID across customer profiles. **Conflict behavior on upsert:** if you POST a new Customer with an email address that already exists in Kustomer, the API returns HTTP 409 with a `duplicate` error code rather than merging or updating the existing record. It does not silently overwrite. Use `GET /v1/customers/email={email}` to check existence before creating, or use `PATCH /v1/customers/{id}` to update. If your Deskpro instance has duplicate People records sharing the same email address, build a pre-migration dedupe plan before the first load — you will hit 409 errors at scale, not just messy data. ([help.kustomer.com](https://help.kustomer.com/migrate-from-another-platform-rkdkZl3MG))

### Organizations → Companies

Deskpro Organizations map to Kustomer Companies. Core fields (`name`, `domains`) translate directly. Deskpro allows hierarchical organizations, whereas Kustomer companies are generally flat. You will need to flatten hierarchies or use Custom Attributes to denote parent-child relationships.

### Tickets → Conversations

Every Deskpro Ticket becomes a Kustomer Conversation. Key mappings:

- **Subject** → Conversation `name`
- **Status** → Deskpro has `awaiting_agent`, `awaiting_user`, `resolved`, `closed`, and `pending`. Kustomer has `open`, `snoozed`, and `done`. You need to collapse these. Our usual mapping: `awaiting_agent → open`, `resolved/closed → done`, `pending → snoozed`.
- **Priority** → Conversation `priority`
- **Department** → No direct equivalent. Use a combination of Kustomer Team assignment, Queue routing rules, or a custom attribute. If your Deskpro inbox behavior depends on Department, you need to rebuild that behavior in Kustomer routing — copying Department into a dead custom field preserves metadata but breaks operations. ([support.deskpro.com](https://support.deskpro.com/en-US/guides/admin-guide/what-are-departments))
- **Labels** → Tags on the Conversation
- **Agent assignment** → `assignedUsers` / `assignedTeams`
- **Created/updated timestamps** → Preserve using `createdAt` and `importedAt`

Store the legacy Deskpro ticket ID in Kustomer's `externalId` field. This makes delta syncs idempotent and gives agents a reference back to the source system. ([help.kustomer.com](https://help.kustomer.com/migrate-from-another-platform-rkdkZl3MG))

> [!WARNING]
> **Do not map Deskpro's Awaiting User blindly to Kustomer Done.** If you do, later email replies can reopen old migrated conversations and distort backlog, SLA, and assignment behavior. Use Snoozed or a workflow rule if you need "waiting on customer" semantics.
>
> **Post-cutover thread-matching risk:** Kustomer's email threading uses In-Reply-To and References headers to match inbound replies to existing conversations. When a customer replies to an email that originated in Deskpro (pre-migration), those headers reference Deskpro's Message-IDs, not Kustomer's. Kustomer will not find a matching conversation via header lookup and will create a **new conversation** instead of threading onto the migrated one. This is not a bug — it is the expected behavior when email headers do not match. To mitigate: during the parallel run period, leave recently-active conversations open in Deskpro and let them resolve there before cutover, rather than migrating them mid-thread. Alternatively, accept that the first reply from a customer after cutover opens a fresh conversation and train agents to manually merge or link to the historical migrated thread.
>
> In Kustomer, email replies to Done conversations reopen the existing conversation if threading headers match, while chat and SMS commonly create a new conversation after an inactivity window. ([help.kustomer.com](https://help.kustomer.com/en_us/change-status-HkvUsVS8W))

### Messages → Messages

Deskpro Ticket Messages map to Kustomer Conversation Messages. Extract full HTML message bodies via the Deskpro API — CSV/report exports only give you metadata, not full content.

For each message, set:
- `direction`: `in` (customer) or `out` (agent), derived from Deskpro's `person` or `agent` association
- `channel`: map the original channel (email, chat, etc.)
- `sentAt`: preserve the original timestamp
- `meta.author`: the original sender

Deskpro Notes (internal agent comments) become Kustomer Notes on the Conversation. These are separate API calls — do not mix them into customer-visible messages.

> [!WARNING]
> **The CC Edge Case.** Deskpro handles CCs natively at the ticket level. Kustomer does not have a traditional "CC" field on the Conversation object in the same way; CCs are handled as participants on individual Messages. When mapping Deskpro tickets with heavy CC usage, parse the CCs from the Deskpro message headers and append them to the Kustomer message payload to avoid dropping participants.

### Custom Fields → Custom Attributes + KObjects

This is where migration complexity concentrates.

**Simple custom fields** (text, number, boolean, date) on Deskpro Tickets become Custom Attributes on the Kustomer Conversation Klass. On Deskpro People, they become Custom Attributes on the Customer Klass.

**Complex or relational data** — anything that represents a business entity (order history, subscription records, product data) — should be modeled as **KObjects** in Kustomer. This requires:

1. Defining a new custom Klass (schema) in Kustomer
2. Creating the KObject instances via API
3. Linking them to the appropriate Customer record

Kustomer's naming convention for custom attributes requires a data-type suffix:

```
subscriptionTierStr    → string
orderTotalNum          → number
isVipBool              → boolean
renewalDateAt          → datetime
profileUrlUrl          → URL
```

### Agents → Users, Departments → Teams + Queues

Deskpro Agents map to Kustomer Users. Create Users in Kustomer first, then map their Deskpro IDs to Kustomer IDs for conversation assignment. Agent Teams become Kustomer Teams. Teams are not just grouping folders — they drive assignment, permissions, and routing. If you skip user and team setup, you lose authorship fidelity or spend the cutover week remapping assignments by hand. ([help.kustomer.com](https://help.kustomer.com/migrate-from-another-platform-rkdkZl3MG))

Deskpro Departments have no direct equivalent in Kustomer. Use **Teams** for agent grouping and ownership, **Queues** with routing rules for work distribution, and optionally store the original Department name as a custom attribute or tag on Conversations for historical reference.

### Knowledge Base

Deskpro Knowledgebase Articles can be migrated to Kustomer's Knowledge Base via API. One critical requirement: creating an Article in Kustomer is not enough to make it visible. You must also create a **Version** for each article to publish it. Many teams with fewer than 100 articles opt to move KB content manually rather than build a separate script.

### Triggers & Automations → Workflows & Business Rules

These cannot be migrated programmatically. Document every Deskpro Trigger and Automation (conditions + actions), rebuild the equivalent logic as Kustomer Workflows and Business Rules, and test thoroughly before cutover. This is typically the most time-consuming part of the migration for teams with complex automation stacks.

## Extraction: Getting Data Out of Deskpro

Deskpro's REST API v2 is your primary extraction path. As detailed in our [Deskpro to Help Scout migration guide](https://clonepartner.com/blog/blog/deskpro-to-help-scout-migration-a-technical-guide/), CSV exports via the Report Builder are limited — they produce incomplete field coverage and for large datasets require multiple segmented queries to avoid timeouts. For full historical thread export, use the API. ([support.deskpro.com](https://support.deskpro.com/en-US/guides/reports-guide/tables))

### API Authentication

Create an API key in Deskpro under **Apps & Integrations > API Keys**. The key consists of an integer ID, a colon, and a random string (e.g., `4:G24M3K6Y3R3H8DN9B6QGH3NW4`). Pass it via the `Authorization: key YOUR_KEY` header. Extraction keys should be tied to an admin or service account with full visibility — an API key only sees what the underlying agent account is allowed to see.

### Extraction Order

Follow this sequence to maintain referential integrity:

1. **Agents** and **Agent Teams** — you need their IDs for assignment mapping
2. **Organizations** — needed before People for company linkage
3. **People** — with custom fields and organization associations
4. **Tickets** — with status, priority, department, labels, custom fields, assignments
5. **Messages per Ticket** — full HTML bodies, timestamps, author info
6. **Notes per Ticket** — internal comments
7. **Attachments per Message** — download blobs
8. **Knowledgebase Articles** — categories, content, status

### Pagination and Rate Limits

Deskpro uses offset-based pagination with a default of 10 results per page, configurable up to 100 per page via the `per_page` parameter. When applying filters on cloud instances, the API may cap results at 1,000 records per filtered query — a behavior observed consistently across cloud deployments, though Deskpro does not publish this ceiling in their documentation. For large datasets, paginate with date-range filters (using `date_created` or `date_updated` bounds) to break the dataset into segments that each fall under the 1,000-record ceiling and avoid deep-offset query timeouts.

```python
import requests
import time

BASE_URL = "https://your-instance.deskpro.com/api/v2"
HEADERS = {"Authorization": "key 4:YOUR_API_KEY_HERE"}

def extract_all_tickets():
    tickets = []
    page = 1
    while True:
        resp = requests.get(
            f"{BASE_URL}/tickets",
            headers=HEADERS,
            params={"page": page, "per_page": 50, "order_by": "id", "order_dir": "asc"}
        )
        if resp.status_code == 429:
            time.sleep(60)
            continue
        data = resp.json()
        tickets.extend(data.get("data", []))
        pagination = data.get("meta", {}).get("pagination", {})
        if page >= pagination.get("total_pages", 1):
            break
        page += 1
    return tickets
```

> [!WARNING]
> **Self-hosted Deskpro edge case:** If you are running Deskpro on-premise, ensure your server's PHP `max_execution_time` and memory limits can handle large API responses. Timeout errors during extraction are a common failure mode for self-hosted instances with 100K+ tickets.
>
> On-prem deployments give you a second option: direct database extraction. Deskpro on-prem runs on MySQL/MariaDB. The core tables are `tickets` (ticket metadata), `ticket_messages` (message content and direction), `people` (contact records), `organizations` (company records), and `ticket_custom_field_values` (custom field data joined via `ticket_custom_fields` for field definitions). Direct DB reads bypass API rate limits and pagination entirely and can extract 200K+ tickets in under an hour, compared to days via API. The trade-off: you must join across multiple tables to reconstruct the full object graph, and schema versions differ between Deskpro releases — validate your table structure against the version you are running before writing extraction queries.

### Preserving Rich Text

Deskpro stores messages in HTML. Kustomer can ingest HTML but renders it through its own internal engine. During extraction, sanitize the Deskpro HTML — remove proprietary CSS classes and heavy inline styling, as these can cause Kustomer's UI to render historical messages poorly.

## Loading: Getting Data Into Kustomer

### API Authentication

Generate an API key in Kustomer under **Settings > Security > API Keys**. Set the role to `org.admin` for migration operations. Use the `Authorization: Bearer YOUR_API_KEY` header.

For US-hosted accounts, the base URL is `https://api.kustomerapp.com/v1/`. For EU-hosted accounts, use `https://api.prod2.kustomerapp.com/v1/`.

### Kustomer API Rate Limits and Throughput

Rate limits vary by pricing tier and apply across all API tokens for your organization:

| Tier | Machine User Limit | Platform Limit | Approx. Conversations/Hour (with importedAt) |
|---|---|---|---|
| Professional | 100 rpm | 300 rpm | ~900–1,200 |
| Business | 100 rpm | 500 rpm | ~1,500–2,000 |
| Enterprise | 100 rpm | 1,000 rpm | ~3,000–4,000 |
| Ultimate | 100 rpm | 2,000 rpm | ~6,000–7,000 |

The conversations-per-hour estimates above assume each conversation requires 2–3 API calls (create conversation + 1–2 messages), use the `importedAt` bypass flag, and account for ~15% headroom for retries and backoff. They represent practical throughput, not theoretical maximums. At Enterprise tier, a 50K-ticket dataset requires roughly 13–17 hours of pure API loading time, which is why multi-day loading windows are normal even for mid-size migrations.

Conversations and Messages created with an `importedAt` field in the request body are exempt from the standard 120 conversations/minute/customer rate limit. Without this bypass, migrating a single customer with 500+ conversations becomes a multi-hour operation.

Kustomer returns rate limit headers (`x-ratelimit-remaining`, `x-ratelimit-reset`) on most responses. Build exponential backoff into your migration script:

```python
import requests
import time

KUSTOMER_BASE = "https://api.kustomerapp.com/v1"
KUSTOMER_HEADERS = {
    "Authorization": "Bearer YOUR_KUSTOMER_API_KEY",
    "Content-Type": "application/json"
}

def create_customer(payload, max_retries=5):
    for attempt in range(max_retries):
        resp = requests.post(
            f"{KUSTOMER_BASE}/customers",
            headers=KUSTOMER_HEADERS,
            json=payload
        )
        if resp.status_code == 201:
            return resp.json()
        if resp.status_code == 409:
            # Customer already exists — fetch and return existing record
            email = payload.get("emails", [{}])[0].get("email", "")
            return fetch_customer_by_email(email)
        if resp.status_code == 429:
            wait = int(resp.headers.get("retry-after", 2 ** attempt))
            time.sleep(wait)
            continue
        resp.raise_for_status()
    raise Exception("Max retries exceeded")
```

### Bulk Endpoints

Kustomer provides bulk creation endpoints for Customers (`POST /v1/customers/batch`) and Conversations. **Optimal batch size is 100 records per request** — larger batches increase the risk of partial failures where some records in the batch succeed and others fail, and the error response does not always identify which specific records failed. At 100 records per batch, error isolation is more reliable and retrying failed batches is straightforward. Bulk endpoints count against the same platform rate limit as single-record endpoints but reduce HTTP round-trip overhead significantly, which matters when your bottleneck is connection latency rather than rate limits. For datasets over 50K records, bulk endpoints typically reduce total loading wall-clock time by 30–40% compared to single-record calls.

Bulk error handling differs from single-record endpoints: a bulk request may return HTTP 207 (Multi-Status) with a mixed array of success and error objects. Parse the response array, extract failed records, and retry them individually to isolate whether the failure is a data problem or a transient rate issue.

### Loading Order

Load data in this sequence — each step depends on IDs from the previous one:

1. **Users** (Agents) and **Teams**
2. **Companies** (from Deskpro Organizations)
3. **Customers** (from Deskpro People) — link to Companies
4. **Custom Klasses** (if modeling KObjects)
5. **KObjects** (if applicable) — link to Customers
6. **Conversations** (from Deskpro Tickets) — link to Customers, assign to Users/Teams
7. **Messages** per Conversation — with `importedAt` set
8. **Notes** per Conversation
9. **Attachments** — upload and link to Messages
10. **Knowledge Base Articles** — create article, then create Version to publish

### Idempotency with External IDs

Kustomer's migration guidance is explicit: **make the migration idempotent with external IDs**. Store the legacy Deskpro ticket ID on the Kustomer Conversation `externalId`, and use the same pattern for Customers, Messages, and Notes. This enables find-or-create checks at each step so reruns and incremental delta syncs do not create duplicates. ([help.kustomer.com](https://help.kustomer.com/migrate-from-another-platform-rkdkZl3MG))

```python
for ticket in deskpro_tickets:
    customer_id = upsert_customer(ticket.person, external_id=ticket.person_id)
    conversation_id = upsert_conversation(
        customer_id=customer_id,
        external_id=f"deskpro-ticket-{ticket.id}",
        status=map_status(ticket.status),
        importedAt=now_iso
    )

    for note in ticket.notes:
        upsert_note(
            conversation_id=conversation_id,
            external_id=f"deskpro-note-{note.id}",
            createdBy=agent_map.get(note.author_id),
            body=note.body
        )

    for msg in ticket.messages:
        upsert_message(
            conversation_id=conversation_id,
            external_id=f"deskpro-msg-{msg.id}",
            createdBy=author_map.get(msg.author_id),
            body=msg.body,
            sentAt=msg.created_at
        )
```

## The Attachment Pipeline

Attachments are the hardest part of any helpdesk migration. Deskpro stores attachments either in the database (older on-premise instances) or in AWS S3.

Kustomer does not let you pass a URL to an attachment directly. You must execute a multi-step upload process for every file:

1. **Download**: Fetch the binary file from Deskpro via the `/api/v2/blobs/{id}/download` endpoint.
2. **Request URL**: POST to Kustomer's `/v1/attachments/urls` endpoint with the file name, size, and MIME type. Kustomer returns a presigned AWS S3 upload URL and an `attachmentId`.
3. **Upload**: PUT the binary data directly to the provided S3 URL.
4. **Attach**: Include the `attachmentId` returned in step 2 in the `attachments` array when creating the Kustomer Message.

**Attachment size limits:** Kustomer enforces a **25 MB per-file limit** on attachments. Files exceeding this limit will be rejected at the presigned URL upload step (the S3 PUT returns a 400 error). There is no documented total-per-conversation attachment limit, but individual files above 25 MB must be handled separately — either truncated, linked externally, or flagged for manual review. For datasets with video attachments, screen recordings, or large exports, audit attachment sizes during extraction before you build the upload pipeline.

There is no bulk attachment upload — each file is an individual operation. For datasets with tens of thousands of attachments, this step alone can take days and is almost always the rate-limiting factor in the loading phase, independent of API rate limits.

> [!CAUTION]
> **Inline Images Will Break.** Inline images in Deskpro are embedded as `<img>` tags pointing to Deskpro blob URLs. If you migrate the HTML as-is, those images will break once the Deskpro instance is decommissioned. You must parse the Deskpro HTML, find every `src` URL pointing to Deskpro, download the image, upload it to Kustomer via the attachment flow, and rewrite the `src` attribute in the HTML body to point to the new Kustomer asset URL before creating the message.

> [!TIP]
> **Store the original Deskpro ticket URL in a Kustomer custom field.** Kustomer's migration guide explicitly recommends keeping a source-system URL. This makes audit, dispute resolution, and post-cutover agent confidence much better. ([help.kustomer.com](https://help.kustomer.com/migrate-from-another-platform-rkdkZl3MG))

## Preserving Timestamps with importedAt

The single most common failure in homegrown migrations is losing historical timestamps. If you POST a message to Kustomer without specifying timestamps, it stamps `createdAt` as the moment the API call was made.

To preserve history, pass `createdAt` on Conversations and `sentAt` on Messages to maintain the original timeline. The `importedAt` field must also be present. It does three critical things:

1. **Marks records as historical imports** so they can be excluded from operational reporting
2. **Prevents imported Conversations from firing active Workflows and Business Rules** — without this, a migration can send thousands of automated emails to customers
3. **Bypasses the standard 120 conversations/minute/customer rate limit**, which is essential for bulk historical loads

```json
{
  "conversation": "60f1a2b3c4d5e6f7a8b9c0d1",
  "customer": "50f1a2b3c4d5e6f7a8b9c0d2",
  "channel": "email",
  "direction": "in",
  "body": "<p>I am still seeing the rate limit issue.</p>",
  "createdAt": "2023-10-14T09:12:33Z",
  "importedAt": "2023-11-01T10:00:00Z"
}
```

> [!CAUTION]
> **Without `importedAt`, imported conversations trigger every active Workflow and Business Rule in Kustomer.** This includes outbound email automations, SLA timers, and assignment rules. In a typical mid-size migration of 20K+ conversations, omitting `importedAt` can generate thousands of automated outbound emails within the first few minutes of loading. Always include `importedAt` on every imported Conversation and Message. Verify it is present in your payload before running at scale — the field name is case-sensitive.

## Delta Sync and Cutover Strategy

A complete migration takes days or weeks of API processing time. Your support team cannot stop working in Deskpro while the data moves. You need a delta sync strategy. Kustomer's own migration guidance recommends continuing incremental migrations until the team fully switches. ([help.kustomer.com](https://help.kustomer.com/migrate-from-another-platform-rkdkZl3MG))

**Parallel run (lower risk, higher operational cost):**
1. **Initial sync**: Extract and load all historical data up to a specific freeze date. This moves 90%+ of your data volume without impacting daily operations.
2. **Parallel period**: Run both systems simultaneously for 3–5 days. Route new tickets to Kustomer while resolving in-flight tickets in Deskpro.
3. **Catch-up sync**: Run a secondary script that extracts everything created or modified since the freeze date.
4. **Final cutover**: Redirect email forwarding rules and channel routing from Deskpro to Kustomer. Run one last delta sync to capture tickets updated in the final window. Disable Deskpro intake channels.

**Hard cut (lower cost, higher risk):**
1. Migrate all historical data.
2. Freeze Deskpro (read-only).
3. Run a delta sync for tickets created during migration.
4. Switch DNS/email routing to Kustomer.
5. Go live.

The parallel approach adds cost (both platforms running) but reduces risk from unexpected data gaps or in-flight ticket confusion. Hard cuts are simpler operationally but fail when teams underestimate the in-flight ticket volume at the moment of switching — a 48-hour loading window with 200 active tickets means 200 threads that agents need to find and continue in a new system.

When running delta syncs, filter on Deskpro's `date_updated` field. Be aware that certain actions in Deskpro (like a silent tag addition) might not bump the `date_updated` timestamp on the parent ticket depending on your configuration. Where possible, also filter on `date_created` for any new tickets that were created entirely within the delta window.

## Validation Checklist

Do not declare the migration done until you have verified:

- [ ] **Record counts match**: Customers, Conversations, Messages, Notes, Attachments
- [ ] **Sample thread integrity**: Pull 20–30 random conversations and compare message bodies, timestamps, and author attribution against Deskpro originals
- [ ] **Custom field accuracy**: Verify Custom Attribute values on 50+ records across Customers and Conversations — compare response body field-by-field against source, since dropped attributes appear as absences, not errors
- [ ] **Attachment accessibility**: Confirm attachments are downloadable in Kustomer and check that files originally over 25 MB were handled per your exception plan
- [ ] **Assignment accuracy**: Verify Conversation assignments to correct Users and Teams
- [ ] **Company linkage**: Confirm Customers are properly linked to Companies
- [ ] **Tag preservation**: Verify all Labels migrated as Tags
- [ ] **Knowledge Base visibility**: Confirm all articles are published (Version created)
- [ ] **Workflow isolation**: Confirm no imported records triggered outbound communications
- [ ] **Thread-matching check**: Send a test reply to a migrated conversation's email thread and verify whether it attaches to the migrated conversation or creates a new one — document the behavior so agents know what to expect

> [!WARNING]
> **Do not rely on Kustomer CSV exports for validation.** Kustomer's reporting export covers only the last 30 days. Saved-search exports only include records updated in the past two years and cap at 50,000 rows. CSV exports include conversation-level attributes and message previews, not full message content. Use API-level validation, spot checks, and source-to-target row counts instead. ([help.kustomer.com](https://help.kustomer.com/en_us/categories/export-your-data-H1X6zdrVs))

Sample both easy records and ugly ones: merged users, long email threads, HTML-heavy tickets, multi-attachment tickets, and records with unusual statuses. The bugs that matter are almost never in the median record.

## Common Failure Modes

**1. Forgetting `importedAt` on Conversations.**
Triggers every active Workflow and Business Rule. Can generate thousands of automated outbound emails within minutes of the import starting. Field name is case-sensitive — `importedAt` not `imported_at`.

**2. Custom attribute naming convention violations — silent data loss.**
Kustomer requires data-type suffixes (`Str`, `Num`, `Bool`, `At`, `Url`). When the suffix is missing or the attribute name is not pre-registered, the API returns HTTP 201 with the record created — but the attribute is absent from the response body with no error, no warning key, and no 4xx status. Detection requires comparing request payload against response body field-by-field. Pre-register all attributes in the Klass schema before importing any data.

**3. Deskpro API filtered query ceiling.**
Filtered queries on cloud instances may cap at 1,000 results per query regardless of pagination depth. Use date-range filters on `date_created` to break large datasets into segments that each fall under this ceiling. Do not rely on deep-offset pagination alone for complete extraction.

**4. Orphaned Conversations.**
If a Deskpro Ticket's Person was deleted or merged, the Conversation has no valid Customer to attach to in Kustomer. Build error handling — either create a placeholder Customer or log for manual review.

**5. Knowledge Base articles without Versions.**
Articles created via the Kustomer API are invisible to end-users until you create a Version. The article exists in the system but returns no results in public KB searches. This catches teams every time.

**6. Routing drift from Department mapping.**
Deskpro Departments are mandatory ticket classification. If you copy Department into a dead custom field instead of mapping it into Kustomer routing (Teams, Queues, Business Rules), agents feel the regression on day one. ([support.deskpro.com](https://support.deskpro.com/en-US/guides/admin-guide/what-are-departments))

**7. Duplicate customer records from 409 conflicts.**
Kustomer returns HTTP 409 when a Customer with the same email already exists — it does not merge or update. Scripts that do not handle 409 will skip those customers and orphan their conversations. Build a fetch-on-conflict path: catch 409, retrieve the existing customer ID, and continue loading against that record.

**8. Post-cutover email threading breaks.**
Customer replies to pre-migration emails carry Deskpro Message-IDs in their headers. Kustomer's threading engine will not match these to migrated conversations and will open new conversations instead. Plan for this operationally: train agents to merge incoming threads, or use the parallel-run period to let active threads resolve in Deskpro before cutover.

**9. Bulk endpoint partial failures.**
Bulk create endpoints may return HTTP 207 with mixed success/error arrays. Scripts that only check the top-level status code will silently skip failed records within a successful-looking batch. Always parse the response array.

**10. Attachment size limit failures.**
Files over 25 MB fail at the S3 PUT step with a 400 error. Without pre-migration size filtering, these failures surface mid-pipeline and leave messages with missing attachments that are difficult to identify after the fact.

## What Cannot Be Migrated Programmatically

Be explicit with stakeholders about what requires manual rebuilding:

- **Triggers and Automations** → Kustomer Workflows and Business Rules
- **SLA Policies** → reconfigured in Kustomer's SLA settings
- **Macros / Snippets** → Kustomer Shortcuts
- **Saved Filters / Views** → Kustomer Saved Searches
- **Portal / Help Center design** → Kustomer's Knowledge Base templates
- **Agent permissions and roles** → Kustomer's permission system
- **CSAT survey configuration** → set up fresh in Kustomer

Budget at least **3–5 days** of manual configuration work for a mid-complexity Deskpro setup. For teams with 50+ Triggers, this can stretch to 2 weeks.

## Migration Timeline Estimates

The estimates below are based on single-engineer execution with a dedicated migration environment. They include extraction, transformation, loading, and validation time but exclude manual automation rebuilding.

| Dataset Size | Extraction | Transformation | Loading & Validation | Total |
|---|---|---|---|---|
| < 10K tickets | 1–2 days | 1–2 days | 2–3 days | **1–1.5 weeks** |
| 10K–50K tickets | 2–4 days | 2–3 days | 3–5 days | **2–3 weeks** |
| 50K–200K tickets | 4–7 days | 3–5 days | 5–10 days | **3–5 weeks** |
| 200K+ tickets | 1–2 weeks | 5–7 days | 1–2 weeks | **4–6 weeks** |

Loading time is the dominant variable and is primarily constrained by Kustomer's platform rate limit tier (see throughput estimates in the rate limits section above) and attachment volume. A 50K-ticket dataset with minimal attachments loads faster than a 20K-ticket dataset where every ticket has multiple large files. These estimates assume Enterprise-tier or higher rate limits; Professional-tier accounts should add 30–50% to loading time estimates.

Add 1–2 weeks if you have complex KObject schemas or need to model business entities that do not exist in Deskpro's flat custom field structure.

## When to DIY vs. When to Get Help

These are heuristics based on the technical scope described in this guide, not objective thresholds:

**DIY is viable if:**
- You have fewer than 10K tickets
- Your custom field schema is simple (< 15 custom fields, no KObjects needed)
- You have an engineer who can dedicate 2+ weeks to the project
- You are comfortable with API scripting in Python, Node, or similar
- Your attachment volume is low (< 5K files, all under 25 MB)

**Get help if:**
- You have 50K+ tickets or need to model KObject schemas from scratch
- You need zero-downtime cutover with a parallel run and delta sync
- Your Deskpro instance has 30+ Triggers that need Kustomer Workflow equivalents
- You are on a tight timeline (< 2 weeks)
- You have significant inline image volume requiring HTML rewriting at scale
- You need a [zero-downtime migration approach](https://clonepartner.com/blog/blog/zero-downtime-help-desk-data-migration/)

Start with your custom field audit and your attachment size distribution. Those two exercises will tell you whether this is a two-week project or a six-week one more reliably than ticket count alone.

> Need help with your Deskpro to Kustomer migration? ClonePartner handles the API pipelines, inline image rewriting, delta syncs, and cutover so your team can focus on supporting customers. Book a free 30-minute call to scope your project.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Is there a native Deskpro to Kustomer import tool?

No. Kustomer documents a native Zendesk import flow and a general API migration pattern, but not a one-click Deskpro importer. Treat Deskpro to Kustomer as a custom API migration using Deskpro REST API v2 for extraction and Kustomer REST API v1 for loading.

### Can I migrate from Deskpro to Kustomer using CSV?

Not for full history. Deskpro's CSV exports do not include full message bodies, inline images, or attachments. Kustomer's CSV importer only supports Customers, Users, Teams, and Companies — not Conversations or Messages. You need an API-to-API pipeline for complete thread migration.

### How long does a Deskpro to Kustomer migration take?

Expect 1–2 weeks for under 10K tickets, 2–3 weeks for 10K–50K tickets, and 4–6 weeks for 200K+ tickets. Timeline varies based on custom field complexity, KObject schema design, and the number of automations that need manual rebuilding.

### What is the importedAt flag in the Kustomer API?

The importedAt field is a timestamp you include when creating Conversations or Messages via the Kustomer API. It marks records as historical imports, bypasses the per-customer rate limit of 120 conversations/minute, prevents imported records from triggering active Workflows and Business Rules, and allows exclusion from operational reporting.

### How do Deskpro custom fields map to Kustomer?

Deskpro custom fields are flat key-value pairs. In Kustomer, you must pre-define Custom Attributes on the appropriate Klass (Customer, Conversation, etc.) with data-type suffixes: Str for strings, Num for numbers, Bool for booleans, At for dates, and Url for URLs. Attributes without the correct suffix will be silently rejected by the API.
