Kustomer to Deskpro Migration: The Complete Technical Guide
A technical guide to migrating from Kustomer to Deskpro, covering API extraction, data model mapping, KObject handling, rate limits, and validation.
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
TL;DR — Kustomer to Deskpro Migration
A Kustomer to Deskpro migration is a data-model translation project. Kustomer's customer-centric timeline architecture — where Conversations, Messages, Notes, and KObjects all hang off a single Customer record — must be decomposed into Deskpro's ticket-centric model of People, Organizations, Tickets, and Messages. The realistic timeline is 2–3 weeks for under 50K conversations (roughly 1 week extraction, 3–4 days transformation, 1 week loading and validation), and 3–5 weeks for larger datasets with complex KObject schemas. The single biggest risk is losing full conversation transcript history: Kustomer's CSV exports only include message previews, not full message bodies, so you must extract via the REST API. KObjects have no direct equivalent in Deskpro — they must be flattened into custom fields on Tickets, People, or Organizations. Kustomer's Business Rules, Workflows, and Saved Searches cannot be exported and must be rebuilt as Deskpro Triggers and Automations manually.
What Is a Kustomer to Deskpro Migration?
A Kustomer to Deskpro migration is the process of extracting Customers, Companies, Conversations, Messages, Notes, KObjects, Tags, Users, and Teams from Kustomer and loading them into Deskpro as People, Organizations, Tickets, Messages, Notes, Labels, Agents, and Agent Teams.
A successful migration starts with one question: are you migrating records, or working context? If agents need searchable thread history, notes, attachments, and account relationships after cutover, treat this as an API/ETL job — not a flat CSV import.
Kustomer is a customer-centric CRM and support platform built around a single-timeline architecture. Every interaction — email, chat, SMS, social — hangs off a unified Customer record. Custom data objects (KObjects) let teams model business-specific entities like orders, subscriptions, or shipments directly within the platform.
Deskpro is a ticket-centric helpdesk platform available as cloud, private cloud, or on-premise. It organizes work around Tickets assigned to Departments, with People (end users) and Organizations providing the CRM layer. Deskpro supports custom fields on Tickets, People, and Organizations but does not have a custom-object system equivalent to KObjects.
Why teams migrate from Kustomer to Deskpro
- Deployment flexibility. Deskpro offers on-premise and private cloud options. Teams in regulated industries (finance, healthcare, government) with data-sovereignty requirements need infrastructure control that Kustomer's SaaS-only model can't provide.
- Cost structure. Kustomer's per-seat pricing at the Enterprise/Ultimate tier can exceed Deskpro's per-agent cost significantly. Deskpro publishes public cloud pricing starting at $39, $59, and $99 per agent/month for Team, Professional, and Enterprise tiers. (deskpro.com)
- Simplified data model. Not every team needs KObjects, Klasses, and a full CRM data layer. Teams that primarily need ticket management and a knowledge base find Deskpro's simpler schema easier to maintain.
- Ownership uncertainty. Kustomer was acquired by Meta (acquisition closed February 2022) and subsequently divested. Teams evaluating long-term platform stability sometimes prefer Deskpro's independent trajectory.
Core data model differences
| Concept | Kustomer | Deskpro |
|---|---|---|
| Primary entity | Customer (timeline) | Ticket |
| Support interaction | Conversation → Messages | Ticket → Messages/Replies |
| End user | Customer | Person (User in UI, /people in API) |
| Company | Company | Organization |
| Custom data objects | KObjects / Klasses | No equivalent (use custom fields) |
| Agent | User | Agent |
| Categorization | Tags, Queues | Labels, Departments, Queues |
| Automation | Business Rules, Workflows | Triggers, Automations, Escalations |
| Knowledge Base | KB Articles | KB Articles (with Categories) |
Migration Approaches: Which Method Fits Your Scenario
There are five viable paths. Each has sharp trade-offs.
1. Native CSV Export/Import
How it works: Export Customers, Conversations, and Messages from Kustomer's Reporting section as CSV files. Import into Deskpro via Admin > Data > Importer.
When to use it: Contact/Organization-only migrations with no conversation history requirements.
Pros:
- No code required
- Fast for small user lists
Cons:
- Kustomer's CSV exports only include message previews, not full message bodies — you lose transcript history
- Kustomer's reporting export is limited to a 30-day window per export and a maximum of 50,000 rows
- Standard searches return data updated within the past 2 years only
- Deskpro's CSV ticket importer only supports ticket properties and the first message — it excludes full message threads and attachments
- No relationship preservation (Customer→Conversation→Messages chain breaks)
- KObjects cannot be exported via CSV at all
Complexity: Low Scalability: Small datasets only (< 5,000 contacts, no conversations)
CSV is not a migration method for conversation history. Both platforms' CSV tooling drops message bodies. Use it only for bootstrapping a contact list. (help.kustomer.com)
2. API-Based Migration (Recommended for Most Teams)
How it works: Extract all data from Kustomer's REST API (/v1/customers, /v1/conversations/{id}/messages, /v1/companies, KObject endpoints), transform it into Deskpro's schema, and load it via Deskpro's REST API (/api/v2/tickets, /api/v2/people, /api/v2/organizations).
When to use it: Any migration that needs full conversation history, attachments, or KObject data.
Pros:
- Full message bodies preserved
- Attachment migration possible
- Relationship mapping (Customer↔Company, Conversation↔Messages)
- KObject data extractable and mappable to custom fields
- Incremental/resumable extraction
Cons:
- Requires engineering effort (Python, Node.js, or similar)
- Rate limit management needed on both sides
- Attachment re-hosting adds time and storage considerations
- You own retries, deduplication, and mapping logic
Complexity: Medium–High Scalability: Handles enterprise datasets (100K+ conversations) with proper batching
Throughput estimate: At Kustomer's 1,000 RPM rate limit ceiling, expect roughly 400–600 tickets/hour with attachments (each ticket requires multiple API calls for messages and blob uploads), or ~1,500–2,000 tickets/hour for text-only tickets without attachments.
3. Third-Party Migration Tools
How it works: Services like Help Desk Migration offer a wizard-based migration from Kustomer to Deskpro, handling field mapping and data transfer through their platform.
When to use it: Teams without engineering bandwidth who need a standard migration without heavy customization.
Pros:
- No code required
- Pre-built connectors for common helpdesk platforms
- Demo migration before full run
Cons:
- Limited control over edge cases (KObject handling, custom transformations)
- Per-record pricing can get expensive at scale
- May not support all custom field types or complex relationship chains
- You're trusting a third party with your data in transit — verify their SOC 2 or equivalent compliance if handling PII
Complexity: Low–Medium Scalability: Medium (depends on tool pricing and connector capabilities)
4. Custom ETL Pipeline
How it works: Build a dedicated extract-transform-load pipeline using tools like Python + Airflow, AWS Glue, or Azure Data Factory. Extract from Kustomer API, stage data in a warehouse or flat files, transform with SQL or code, load into Deskpro API.
When to use it: Large-scale migrations (500K+ records) with complex KObject schemas, multi-brand setups, or compliance requirements that mandate data staging and audit trails.
Pros:
- Full control over every transformation
- Audit trail and lineage tracking
- Reusable for ongoing data operations
- Can handle complex KObject→custom field transformations
Cons:
- Highest engineering investment
- Requires infrastructure (scheduler, staging DB, monitoring)
- Overkill for simple migrations
Complexity: High Scalability: Enterprise-grade
AWS Glue's Kustomer connector has been reported to have a hard limit of 10,000 records per filtered request — data beyond that limit may be silently dropped unless you implement date-range partitioning. Verify this against current AWS documentation before relying on it for full extraction.
5. Middleware / Integration Platforms (Zapier, Make)
How it works: Use Zapier or Make to connect Kustomer triggers to Deskpro actions, creating tickets or contacts on the fly.
When to use it: Ongoing sync of new records only — not historical migration.
Pros:
- No code needed
- Good for keeping two systems in sync during a transition period
Cons:
- Cannot migrate historical data (trigger-based, not batch)
- Per-task pricing makes bulk operations prohibitively expensive
- No control over ordering or relationship integrity
- Attachment handling is limited or absent
Complexity: Low Scalability: Not viable for migration; only for ongoing sync of new records
Comparison Table
| Method | Complexity | Full History | Attachments | KObjects | Scale | Cost Model |
|---|---|---|---|---|---|---|
| CSV Export/Import | Low | ❌ | ❌ | ❌ | Small | Free |
| API-Based | Medium–High | ✅ | ✅ | ✅ (flattened) | Enterprise | Engineering time |
| Third-Party Tools | Low–Medium | ✅ (partial) | ✅ | ❌ (limited) | Medium | Per-record |
| Custom ETL | High | ✅ | ✅ | ✅ | Enterprise | Infrastructure + engineering |
| Middleware (Zapier/Make) | Low | ❌ | ❌ | ❌ | Small | Per-task |
Scenario-Based Recommendations
- Small team (< 10K conversations, no KObjects, limited budget): Third-party migration tool or lean API script
- Mid-market (10K–100K conversations, some KObjects): API-based migration with a dedicated engineer or managed service
- Enterprise (100K+ conversations, complex KObject schemas, compliance needs): Custom ETL pipeline or managed migration service
- Ongoing sync during transition period: Middleware for new records + API batch for historical backfill
When a Managed Migration Service Makes Sense
When NOT to build in-house
- Your engineering team is already committed to product work and can't absorb 3–5 weeks of migration effort
- You have complex KObject schemas that need careful flattening into Deskpro's custom field structure
- You're migrating attachments at scale (re-hosting to Deskpro's blob storage adds significant complexity)
- You need zero-downtime cutover with delta sync of records created during the migration window
- You've never worked with either platform's API before
Hidden costs of DIY migration
- Rate limit management across two APIs with different throttling models (Kustomer's org-wide 1,000 RPM ceiling vs. Deskpro's per-key configurable limits)
- Attachment re-hosting — downloading from Kustomer, uploading to Deskpro's
/api/v2/blobs/tmpendpoint, then linkingblob_authcodes to tickets - Relationship reconstruction — Deskpro requires People and Organizations to exist before Tickets can reference them, so load order matters
- Edge case handling — duplicate emails, orphaned conversations, multi-channel threads, and encoding issues eat weeks of debugging time
- Security and compliance — if migrating PII (customer emails, phone numbers, support transcripts), you need TLS encryption in transit, secure staging storage, and a data deletion plan for intermediate files. GDPR-regulated organizations must also consider lawful basis for processing during migration.
Why ClonePartner
With 1,500+ migrations completed — including complex helpdesk platform moves involving custom objects, multi-threaded conversation histories, and attachment-heavy datasets — ClonePartner has built repeatable patterns for exactly this kind of data-model translation.
We handle the Kustomer API extraction, KObject-to-custom-field mapping, attachment re-hosting, and Deskpro API loading as a managed service. We run historical syncs in the background and perform a final delta sync before cutover — your agents never stop working. Typical turnaround is 5–10 business days depending on dataset size and complexity.
For more on our approach, see How We Run Migrations at ClonePartner.
Pre-Migration Planning
Data audit checklist
Before writing any migration code, inventory what you actually have in Kustomer:
- Customers — total count, custom attributes, linked Companies
- Companies — count, custom fields, parent-child hierarchies
- Conversations — total count by channel (email, chat, SMS, social), date range, status distribution
- Messages — average messages per conversation, attachment count
- Notes — internal notes attached to customers or conversations
- KObjects — list all Klasses, count per Klass, custom attribute schemas
- Tags — full tag list, frequency distribution
- Users (Agents) — count, team assignments, roles
- Teams — structure, queue assignments
- Knowledge Base — article count, categories, attachments, translations
- Business Rules & Workflows — document logic (these cannot be exported via API)
- Saved Searches — document filter criteria for recreation in Deskpro
Identify data to exclude
Not everything deserves migration. Cut aggressively:
- Spam/junk conversations (filter by tag or status)
- Test/sandbox customer records
- Conversations older than your retention policy requires — consider archiving older data to a data warehouse and only migrating the last 2 years to Deskpro
- Unused KObject Klasses with zero or minimal records
- Duplicate customer records (Kustomer allows duplicates; Deskpro matches on email)
Migration strategy
| Strategy | Best For | Risk Level |
|---|---|---|
| Big bang | Small datasets, weekend cutover window | Medium — all-or-nothing |
| Phased | Large datasets, teams that can run both platforms temporarily | Low — validate each phase |
| Incremental | Teams that need zero downtime and delta sync | Low — but highest engineering effort |
For most Kustomer-to-Deskpro migrations, a phased approach works best: migrate Organizations and People first, validate, then migrate Tickets with Messages, then KB articles.
No read-only access after cancellation. Kustomer does not provide a read-only access period after your subscription ends. Export all data via the API before your subscription terminates — you won't be able to retrieve it afterward. (help.kustomer.com)
Data Model and Object Mapping
This is where the migration succeeds or fails. Kustomer and Deskpro have fundamentally different data architectures.
Object-level mapping
| Kustomer Object | Deskpro Object | Notes |
|---|---|---|
| Customer | Person (User) | Primary email is the match key. Deskpro deduplicates on email. |
| Company | Organization | Map company name + domain. |
| Conversation | Ticket | One Conversation = one Ticket. Channel metadata maps to Ticket channel. |
| Message | Ticket Message/Reply | Preserve direction (inbound/outbound), timestamp, and channel. |
| Note (internal) | Ticket Note (internal) | Agent-only visibility preserved. |
| KObject | Custom Fields (on Ticket, Person, or Org) | No 1:1 equivalent. Flatten key attributes into custom fields. |
| Tag | Label | Direct mapping. |
| User (Agent) | Agent | Map roles and team assignments. |
| Team | Agent Team | Recreate team structure. |
| Queue | Department / Custom Queue | Routing logic must be rebuilt. |
| KB Article | KB Article | Map categories, re-host inline images. |
Field-level mapping
| Kustomer Field | Type | Deskpro Field | Transformation |
|---|---|---|---|
customer.name |
String | person.name |
Direct |
customer.emails [0].email |
String | person.primary_email |
Take first verified email |
customer.phones [0].phone |
String | person.phone |
Format with country code |
customer.custom.* |
Various | person.custom_fields.* |
Create matching custom fields in Deskpro first |
company.name |
String | organization.name |
Direct |
conversation.name |
String | ticket.subject |
Fallback to "No Subject" if null |
conversation.status |
Enum | ticket.status |
Map: open→awaiting_agent, snoozed→awaiting_agent, done→resolved |
conversation.priority |
Enum | ticket.priority |
Map Kustomer priority values to Deskpro's 1–10 scale |
conversation.assignedUsers |
Array | ticket.agent |
Map by agent email |
conversation.tags |
Array | ticket.labels |
Direct |
conversation.createdAt |
ISO 8601 | ticket.date_created |
Preserve original timestamp |
message.preview |
String | — | Do not use. Extract full body via API. |
message.body (via API) |
HTML | ticket_message.message |
Set format: html. Rewrite inline image URLs. |
message.direction |
Enum | Message type | in → user reply, out → agent reply |
message.attachments |
Array | ticket_message.attachments |
Re-host via blob upload |
Pre-create all Deskpro custom fields before loading data. Deskpro's API addresses custom fields by field IDs, and choice fields also require value IDs. Preserve every source system ID in custom fields or a sidecar mapping table — it makes delta loads and reconciliation much easier.
Handling KObjects
This is the hardest part of any Kustomer migration. KObjects are fully custom — each org defines its own Klasses (schemas) with up to 500 custom attributes per Klass.
Deskpro has no custom object system. Your options:
- Flatten into custom fields — Take the most important KObject attributes and create custom fields on Tickets, People, or Organizations in Deskpro. Works for 1–2 KObject types with < 10 key attributes each.
- Serialize into a text field or pinned note — Store the full KObject as JSON in a long-text custom field, or compile the data into an HTML table and inject it as a pinned note on the Deskpro Person profile. Preserves data but loses queryability.
- External database — Store KObject data in an external system (your CRM, a database) and link to Deskpro records via ID. Best for complex KObject schemas that can't be flattened.
- Drop it — If the KObject data isn't needed in Deskpro's operational context, archive it externally and don't migrate it.
Before mapping KObjects, audit which ones agents actually use day-to-day. In our experience, teams often have 5+ Klasses defined but only 1–2 that are actively referenced during support interactions.
Migration Architecture
Data flow: Extract → Transform → Load
Kustomer REST API (/v1/*)
↓ [Extract]
Staging Layer (JSON files / SQLite / PostgreSQL)
↓ [Transform]
Deskpro-ready payloads (JSON)
↓ [Load]
Deskpro REST API (/api/v2/*)
Kustomer API extraction details
- Base URL:
https://{orgname}.api.kustomerapp.com/v1/ - Authentication: Bearer token in
Authorizationheader - Pagination: Cursor-based using
page [size](max 100 per page) andpage [after] - Rate limits: 1,000 RPM org-wide for standard API keys (all keys share this ceiling). Machine users: 100 RPM independent limit.
- Pagination ceiling: 100 pages max per search query. For datasets exceeding 10,000 records per query, paginate using
updated_atsort and windowed date-range queries. - Archive Search: Use Archive Search for data older than 2 years, as standard searches only return records updated within that window. (help.kustomer.com)
- Key endpoints:
GET /v1/customers— extract all customersGET /v1/customers/{id}— single customer with full attributesGET /v1/conversations— extract conversationsGET /v1/conversations/{id}/messages— full message bodies (not available in CSV exports)GET /v1/companies— extract companiesGET /v1/klasses/{klassName}/kobjects— extract KObjects by Klass
Deskpro API loading details
- Base URL:
https://{helpdesk}.deskpro.com/api/v2/ - Authentication: API key via
Authorization: key {id}:{code}header. Use a superuser API key to create records as different agents. - Rate limits: Configurable per API key (hourly and daily limits). Set generously for migration, then tighten afterward.
- Error responses: Deskpro returns standard HTTP status codes —
400for malformed requests (with a JSON body containing field-level error details),401for authentication failures,404for invalid endpoints or IDs,429for rate limit exceeded (withRetry-Afterheader), and500for server errors. Log full response bodies for failed requests. - Pagination (for validation): Deskpro uses
pageandcountparameters (e.g.,GET /api/v2/tickets?page=2&count=50). The response includespagination.totalfor record counts. - Key endpoints:
POST /api/v2/people— create usersPOST /api/v2/organizations— create organizationsPOST /api/v2/tickets— create tickets with initial messagePOST /api/v2/tickets/{id}/messages— add subsequent messages/repliesPOST /api/v2/blobs/tmp— upload attachments (returnsblob_authcode)POST /api/v2/articles— create KB articlesDELETE /api/v2/tickets/{id}— delete individual tickets (for rollback)POST /api/v2/tickets/actions/mass_delete— bulk delete tickets matching criteria (for rollback)
On-premise Deskpro: If loading into a self-hosted Deskpro instance, you may need VPN access or IP whitelisting for your migration servers. On-premise instances may also run different Deskpro versions with API behavior differences — verify API compatibility against your installed version before starting. Plan this with your infrastructure team.
Attachment handling
Deskpro uses a two-step attachment process:
- Upload the file to
POST /api/v2/blobs/tmpasform-data - Receive a
blob_authcode in the response - Include the
blob_authcode when creating the ticket or message
Every attachment requires two API calls on the Deskpro side, plus a download from Kustomer. For attachment-heavy migrations, this is the bottleneck — expect throughput to drop to ~400–600 tickets/hour when tickets average 2+ attachments each.
Kustomer stores attachment URLs as signed URLs that may expire. Download all attachments during extraction and store locally before beginning the Deskpro upload phase. Don't start uploading to Deskpro while you're still extracting from Kustomer — a signed URL that worked at extraction time can 404 by the time you circle back to it.
Channel-specific considerations
Different Kustomer channels have structural differences that affect migration:
- Email conversations: Most straightforward — map subject, HTML body, headers, and attachments directly to Deskpro email tickets.
- Chat transcripts: Kustomer stores chat as a series of short messages with rapid timestamps. In Deskpro, these become sequential ticket messages. Consider concatenating rapid-fire chat messages (< 60 seconds apart from the same sender) into single messages to reduce noise.
- SMS messages: Typically plain-text, short messages. Map to Deskpro ticket messages with
format: text. Phone number becomes the Person identifier if no email exists. - Social media (Facebook, Twitter/X, Instagram): Kustomer stores social handles and platform-specific metadata. Deskpro may not preserve all platform metadata — map the message content and store social handle as a custom field on the Person record.
Step-by-Step Migration Process
Step 1: Extract data from Kustomer
import requests
import time
import json
KUSTOMER_BASE = "https://yourorg.api.kustomerapp.com/v1"
KUSTOMER_TOKEN = "your-api-key"
HEADERS = {"Authorization": f"Bearer {KUSTOMER_TOKEN}"}
def extract_paginated(endpoint, params=None):
"""Extract all records from a paginated Kustomer endpoint."""
all_records = []
url = f"{KUSTOMER_BASE}{endpoint}"
page_params = {"page[size]": 100}
if params:
page_params.update(params)
page_count = 0
while url and page_count < 100: # 100-page ceiling
resp = requests.get(url, headers=HEADERS, params=page_params)
if resp.status_code == 429:
retry_after = int(resp.headers.get("retry-after", 60))
time.sleep(retry_after)
continue
resp.raise_for_status()
data = resp.json()
all_records.extend(data.get("data", []))
# Cursor-based pagination
links = data.get("links", {})
url = links.get("next")
page_params = {} # Next URL includes params
page_count += 1
time.sleep(0.07) # Respect rate limits (~850 RPM)
return all_records
# Extract customers
customers = extract_paginated("/customers")
# Extract conversations
conversations = extract_paginated("/conversations")
# Extract full messages for each conversation
for conv in conversations:
conv_id = conv["id"]
conv["messages"] = extract_paginated(f"/conversations/{conv_id}/messages")For datasets exceeding the 100-page ceiling (10,000+ records per query), implement windowed queries — sort by updated_at and break your extraction into date-range slices:
from datetime import datetime, timedelta
def extract_windowed(endpoint, start_date, end_date, window_days=7):
"""Extract records in date-range windows to bypass 100-page ceiling."""
all_records = []
current = start_date
while current < end_date:
window_end = min(current + timedelta(days=window_days), end_date)
params = {
"updatedAt_gte": current.isoformat() + "Z",
"updatedAt_lt": window_end.isoformat() + "Z",
"sort": "updatedAt"
}
records = extract_paginated(endpoint, params)
all_records.extend(records)
current = window_end
return all_recordsStep 2: Transform data
def transform_customer_to_person(kustomer_customer):
"""Map Kustomer Customer to Deskpro Person."""
attrs = kustomer_customer.get("attributes", {})
emails = attrs.get("emails", [])
phones = attrs.get("phones", [])
return {
"name": attrs.get("displayName") or attrs.get("name", "Unknown"),
"primary_email": emails[0]["email"] if emails else None,
"phone": phones[0].get("phone") if phones else None,
"custom_fields": transform_custom_attrs(attrs.get("custom", {})),
"_kustomer_id": kustomer_customer["id"] # Keep for ID mapping
}
def transform_conversation_to_ticket(conversation, id_map):
"""Map Kustomer Conversation to Deskpro Ticket."""
attrs = conversation.get("attributes", {})
status_map = {
"open": "awaiting_agent",
"snoozed": "awaiting_agent",
"done": "resolved",
"closed": "resolved"
}
messages = conversation.get("messages", [])
first_message = messages[0] if messages else {}
return {
"subject": attrs.get("name", "No Subject"),
"person": id_map.get(attrs.get("customer")),
"status": status_map.get(attrs.get("status"), "awaiting_agent"),
"message": {
"message": first_message.get("attributes", {}).get("body", ""),
"format": "html"
},
"labels": attrs.get("tags", []),
"date_created": attrs.get("createdAt") # Preserve original timestamp
}Step 3: Load into Deskpro
DESKPRO_BASE = "https://yourhelpdesk.deskpro.com/api/v2"
DESKPRO_KEY = "1:YOUR_API_KEY"
DP_HEADERS = {
"Authorization": f"key {DESKPRO_KEY}",
"Content-Type": "application/json"
}
def create_deskpro_record(endpoint, payload):
"""Create a record in Deskpro with retry logic."""
for attempt in range(3):
resp = requests.post(
f"{DESKPRO_BASE}{endpoint}",
headers=DP_HEADERS,
json=payload
)
if resp.status_code == 201:
return resp.json().get("data", {})
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 30))
time.sleep(retry_after)
continue
if resp.status_code >= 500:
time.sleep(2 ** attempt) # Exponential backoff for server errors
continue
# Log 4xx errors with full response for debugging
if resp.status_code >= 400:
print(f"Error {resp.status_code} on {endpoint}: {resp.text}")
resp.raise_for_status()
raise Exception(f"Failed after 3 retries: {endpoint}")
# Load order matters: Organizations → People → Tickets → Messages
id_map = {} # kustomer_id → deskpro_id
for org in transformed_orgs:
result = create_deskpro_record("/organizations", org)
id_map[org["_kustomer_id"]] = result["id"]
for person in transformed_people:
result = create_deskpro_record("/people", person)
id_map[person["_kustomer_id"]] = result["id"]
for ticket in transformed_tickets:
result = create_deskpro_record("/tickets", ticket)
ticket_id = result["id"]
# Load subsequent messages
for msg in ticket["_remaining_messages"]:
create_deskpro_record(f"/tickets/{ticket_id}/messages", msg)A production loader needs exponential backoff, checkpointing (save progress to disk/DB after each batch so you can resume after failures), idempotent re-runs (check if a record exists before creating), and a dead-letter queue for failed records. The code above is a structural reference — production scripts should also handle network timeouts, partial failures, and state resumption.
Step 4: Validate
After loading, run count comparisons and field-level spot checks. See the Validation section below for the full process.
Edge Cases and Challenges
Duplicate records
Kustomer allows multiple Customer records with the same email address. Deskpro deduplicates on primary_email — if you try to create a Person with an email that already exists, the API returns the existing record (HTTP 200 instead of 201). Your migration script must handle this by checking for existing People before creating and merging conversation history onto the correct Person.
Multi-channel conversation threads
A single Kustomer Conversation can span email, chat, SMS, and social channels within one timeline. Deskpro Tickets have a primary channel. Pick the originating channel for the Ticket and add subsequent cross-channel messages as replies — the content is preserved even if the channel metadata is simplified.
KObject relationship chains
In Kustomer, KObjects can relate to Customers, Conversations, and other KObjects. Deskpro has no object-relationship layer. If you flatten KObjects into custom fields, the relationships between KObjects are lost. Document these relationships externally (e.g., in a reference table or your data warehouse) if they're important for reporting.
Attachment migration at scale
Kustomer stores attachment URLs as signed URLs that may expire. Download all attachments during extraction and store locally before beginning the Deskpro upload phase. Deskpro's two-step blob upload process means attachment-heavy tickets (10+ files) can take significantly longer per ticket. Budget 3–5x the time per ticket compared to text-only tickets.
Inline images
Images pasted directly into Kustomer emails are stored as base64 or secure links. You must parse the HTML, download the image, upload it to Deskpro as a blob, and rewrite the <img> tag in the message body with the new Deskpro URL. A regex-based approach to find <img src="..."> tags is fragile — use an HTML parser like BeautifulSoup (Python) or cheerio (Node.js).
Agent attribution
If an agent has left your company, their account won't exist in Deskpro. You must either create placeholder agent profiles in Deskpro for historical attribution or map their actions to a generic "System Account." Without this, historical ticket assignments and agent replies lose their author context.
Message ordering
Kustomer Messages have createdAt timestamps, but Deskpro may reorder messages during import. Set date_created explicitly on each message and verify chronological order after loading.
Conversation exports missing message bodies
This bears repeating: Kustomer's CSV/reporting exports only include a preview field with truncated message text. You must use the API endpoint GET /v1/conversations/{id}/messages to get full message bodies. This is the single most common data-loss mistake in Kustomer migrations.
For a detailed source-side export walkthrough, see How to Export Data from Kustomer: Methods, API Limits & Data Portability.
Limitations and Constraints
Be honest with your stakeholders about what will change post-migration.
What Deskpro doesn't have vs. Kustomer
- No custom objects. Deskpro does not have an equivalent to KObjects. All custom data must be expressed as custom fields on Tickets, People, or Organizations.
- No customer timeline. Kustomer's unified timeline view — showing conversations, KObjects, and events in one chronological feed — has no Deskpro equivalent. Conversation history is ticket-scoped.
- Simpler status model. Kustomer's conversation statuses and custom status fields may not map cleanly to Deskpro's fixed status set (
awaiting_agent,awaiting_user,resolved,closed,hidden). - Ticket-centric architecture. Deskpro organizes around Tickets, not Customers. Agents see ticket queues, not customer profiles. This is a workflow shift, not just a data shift.
- No native KObject triggers. Highly customized Kustomer workflows relying on custom object triggers must be rebuilt using Deskpro's Triggers and Webhooks.
API rate limits
| Platform | Limit | Scope |
|---|---|---|
| Kustomer | 1,000 RPM (standard API keys) | Org-wide, shared across all keys |
| Kustomer | 100 RPM (machine users) | Per machine user |
| Kustomer | 100 pages max per search query | Per query |
| Deskpro | Configurable per API key | Per key (hourly/daily) |
If you have 500,000 tickets averaging 5 messages each, that's 2.5M+ message API calls on the Kustomer side alone. At 1,000 RPM, pure extraction takes ~42 hours of continuous API calls — before any transformation or loading. The migration script cannot run at maximum concurrency. You must batch requests and expect the full data load to take several days.
Potential data loss scenarios
- Using CSV export instead of API (message bodies truncated)
- Exceeding Kustomer's 100-page pagination limit without implementing windowed queries (records silently skipped)
- KObject data that can't be flattened into Deskpro's custom field types (e.g., nested arrays, multi-level object hierarchies)
- Attachments with expired signed URLs if not downloaded promptly during extraction
- Business Rules, Workflows, and Saved Searches that are not documented before decommissioning Kustomer (there is no export option for these)
- Custom conversation statuses that don't map to Deskpro's fixed status set — data isn't lost but semantic meaning may change
Validation and Testing
Do not rely on spot-checking. Implement a programmatic validation process.
Record count comparison
| Object | Kustomer Count (Source) | Deskpro Count (Target) | Match? |
|---|---|---|---|
| Customers / People | GET /v1/customers count |
GET /api/v2/people?count=1 → pagination.total |
|
| Companies / Organizations | GET /v1/companies count |
GET /api/v2/organizations?count=1 → pagination.total |
|
| Conversations / Tickets | GET /v1/conversations count |
GET /api/v2/tickets?count=1 → pagination.total |
|
| Messages | Sum of all messages | Sum of all ticket messages | |
| KB Articles | GET /v1/kb/articles count |
GET /api/v2/articles?count=1 → pagination.total |
Note: Deskpro People count may differ from Kustomer Customer count due to email-based deduplication. Document expected deltas.
Field-level validation
For a random sample of 50–100 records per object type:
- Compare customer name, email, phone in both systems
- Verify conversation/ticket subject, status, assignee, tags/labels
- Open 10 tickets in Deskpro and read through the full message thread — confirm all messages are present and in correct chronological order
- Check custom field values against source data
- Verify attachments are downloadable in Deskpro
- Confirm that
date_createdtimestamps on tickets match Kustomer'screatedAt— do not accept all tickets showing the migration date
UAT process
- Have 2–3 support agents work in Deskpro for 48 hours using migrated data
- Agents should search for known customers and verify history is intact
- Test replying to migrated tickets (confirm no automation mis-fires)
- Validate that Organization→Person relationships are correct
- Check that KB articles render correctly with images and formatting
Rollback planning
Deskpro supports import undo for CSV imports, but API-created records must be deleted via the API. Before loading:
- Tag all migrated records with a label like
kustomer-migration-batch-1 - This allows bulk deletion via
POST /api/v2/tickets/actions/mass_deletewith a label filter if a rollback is needed - For People and Organizations, use
DELETE /api/v2/people/{id}andDELETE /api/v2/organizations/{id}— there is no bulk delete for these object types, so script it - Keep your staging data intact until validation is complete
For a comprehensive post-migration test suite, see Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration.
Post-Migration Tasks
Rebuild automations
Kustomer's Business Rules, Workflows, and Saved Searches cannot be exported. You must manually recreate them as:
- Deskpro Triggers — fire on ticket creation or update (equivalent to Kustomer Business Rules)
- Deskpro Automations — time-based rules (equivalent to Kustomer time-based Workflows)
- Deskpro Escalations — SLA-based routing
- Deskpro Ticket Filters / Queues — equivalent to Kustomer Saved Searches
Document every Kustomer Business Rule before decommissioning. There is no export option — screenshot or manually record the conditions and actions.
User training
The shift from Kustomer's customer-timeline view to Deskpro's ticket-queue view is the biggest workflow change agents will experience. Plan training around:
- Finding a customer's full history (People → Tickets tab instead of a unified timeline)
- Using Departments instead of Queues for routing
- Custom field locations on the ticket panel
- New keyboard shortcuts and agent interface navigation
Monitoring
For the first 2 weeks after go-live:
- Monitor for orphaned tickets (tickets without a linked Person)
- Watch for custom field values that didn't map correctly
- Check that email routing is working (Deskpro email accounts must be configured independently)
- Verify that no Kustomer automations are still firing (disable webhooks and integrations in Kustomer)
- Keep an eye on Deskpro's API logs for the first 48 hours to ensure integrations (Jira, Salesforce, etc.) are syncing correctly to the new ticket IDs
Security and Compliance During Migration
For teams in regulated industries — the primary driver for choosing Deskpro's on-premise deployment — migration itself is a data-handling event that requires compliance attention:
- Encryption in transit: Ensure all API calls to both Kustomer and Deskpro use TLS 1.2+ (both platforms enforce HTTPS by default).
- Staging data security: If you stage extracted data locally or in a cloud warehouse, encrypt at rest. Kustomer data contains PII (names, emails, phone numbers, support transcripts).
- Data residency: For GDPR-regulated organizations, confirm that your staging infrastructure and any third-party migration tools process data within compliant jurisdictions.
- Post-migration cleanup: Delete all staging files, local attachment copies, and intermediate databases after validation is complete. Document the deletion for audit purposes.
- Access control: Restrict API keys used during migration. Rotate Kustomer API keys after the migration is complete; tighten Deskpro API key rate limits and permissions once loading is done.
- Audit trail: Log every record created in Deskpro with its source Kustomer ID. This provides traceability for compliance reviews.
Best Practices
- Back up everything before migration. Export full Kustomer data via API to local storage. This is your safety net.
- Never migrate directly to production. Always run a full migration to a Deskpro sandbox environment first. Validate before touching production.
- Validate incrementally. Don't wait until the end. Check counts and sample records after each object type loads.
- Disable automations in Deskpro during import. Triggers that fire on ticket creation will spam agents and customers with notifications if left active during bulk loading.
- Preserve original timestamps. Set
date_createdon tickets and messages to match Kustomer'screatedAt. Without this, all migrated tickets appear as "just created." - Preserve legacy IDs. Store the
Kustomer_Conversation_IDin a custom field on the Deskpro Ticket. This is mandatory for troubleshooting, delta loads, and post-migration API integrations. - Freeze Kustomer configurations. Implement a change freeze in Kustomer two weeks before the migration. No new custom fields, no new tags.
- Plan the cutover window. Pick a low-traffic period. Run a delta sync after the initial migration to catch records created during the migration window.
- Keep Kustomer active (read-only) for 30 days post-migration. Agents may need to reference historical data that didn't migrate or wasn't needed.
- Automate repetitive tasks. Write scripts for extraction, transformation, and loading. Manual copy-paste does not scale and introduces errors.
Choosing Your Path
Migrating from Kustomer to Deskpro is a data-model translation problem, not a simple import. If you are moving current contacts and a light queue, CSV may be enough. If you are moving support history agents will actually search, use the API. If you are moving KObjects, attachments, multiple brands, or years of history, run this as an ETL program with sample, full, and delta phases.
The practical line between a simple import and a real migration is whether your agents need working context — not just rows — on the other side.
Frequently Asked Questions
- Can I migrate full conversation history from Kustomer to Deskpro?
- Yes, but only via the Kustomer REST API. CSV exports only include message previews, not full message bodies. You must use the GET /v1/conversations/{id}/messages endpoint to extract complete message content, then load it into Deskpro via the Tickets and Messages API.
- How do I handle Kustomer KObjects in Deskpro?
- Deskpro has no custom object system equivalent to KObjects. Your options are: flatten key KObject attributes into custom fields on Tickets, People, or Organizations; serialize the full KObject as JSON into a text field or pinned note; store KObject data in an external database; or archive and exclude it from migration.
- What are the API rate limits for a Kustomer to Deskpro migration?
- Kustomer enforces 1,000 requests per minute org-wide (shared across all API keys) with a 100-page maximum per search query. Machine users have an independent 100 RPM limit. Deskpro's rate limits are configurable per API key with hourly and daily thresholds. Plan extraction and loading as separate, independently throttled processes.
- How long does a Kustomer to Deskpro migration take?
- Expect 2–3 weeks for under 50K conversations with no KObjects, and 3–5 weeks for larger datasets with complex KObject schemas and attachments. A managed migration service can typically compress this to 5–10 business days.
- Does Deskpro have a native Kustomer import tool?
- No. Deskpro offers a CSV importer for Users, Organizations, and Tickets, but ticket CSV imports only support ticket properties and the first message — not full conversation threads or attachments. For a complete migration, you need the API or a third-party migration tool.

