---
title: "Help Scout to Deskpro Migration: A Technical Guide"
slug: help-scout-to-deskpro-migration-a-technical-guide
date: 2026-08-07
author: Raaj
categories: [Help Scout, Migration Guide, Help Desk]
excerpt: "Migrate from Help Scout to Deskpro with this technical guide covering API extraction, data model mapping, thread handling, attachments, rate limits, and validation."
tldr: "Help Scout to Deskpro migration requires API-to-API extraction — no built-in importer exists. Map Mailboxes to Departments, Conversations to Tickets, and Threads to Messages. Budget 1–4 weeks depending on dataset size."
canonical: https://clonepartner.com/blog/help-scout-to-deskpro-migration-a-technical-guide/
---

# Help Scout to Deskpro Migration: A Technical Guide


# Help Scout to Deskpro Migration: A Technical Guide

> [!NOTE]
> **TL;DR — Help Scout to Deskpro Migration**
>
> A Help Scout to Deskpro migration translates Help Scout's **mailbox-centric, shared-inbox architecture** into Deskpro's **department-centric ticket model**. Conversations become Tickets, Threads become Messages, Customers become People, and Mailboxes map to Departments. There is **no built-in Help Scout importer in Deskpro** — you need a Help Scout Mailbox API v2 → Deskpro REST API v2 pipeline. Help Scout's native CSV export strips message bodies, thread content, and attachments, making API extraction mandatory. Workflows, Saved Replies, and Beacon configurations must be rebuilt manually. Realistic timeline: **1–2 weeks** for under 20K conversations; **3–4 weeks** for larger datasets with custom fields and knowledge base articles.
>
> *Verified against Help Scout Mailbox API v2 and Deskpro REST API v2. Rate limit figures are empirically observed; see sourcing notes inline.*

## What Is a Help Scout to Deskpro Migration?

A Help Scout to Deskpro migration extracts Conversations, Threads, Customers, Companies, Tags, Custom Fields, Users, Teams, and Docs articles from Help Scout and reconstructs them to fit [Deskpro's ticket-centric model](https://clonepartner.com/blog/blog/kustomer-to-deskpro-migration-the-complete-technical-guide/) of Tickets, Messages, People, Organizations, Labels, Custom Fields, Agents, Agent Teams, and Knowledgebase Articles.

The core question before you start: **do you need searchable thread history with full message bodies, or just ticket metadata?** If agents need to reference historical conversation context after cutover — and they almost always do — this is an API-to-API ETL (Extract, Transform, Load) job, not a CSV shuffle.

**Help Scout** is an email-first customer support platform built around shared inboxes (Mailboxes). Every customer interaction is a Conversation containing Threads — customer messages, agent replies, internal notes, and forwards. It abstracts away complex ITIL concepts in favor of simplicity. API access requires a paid plan. ([Help Scout API documentation](https://developer.helpscout.com/mailbox-api/))

**Deskpro** is a full-featured helpdesk platform available as both cloud-hosted and self-hosted (on-premise). It uses a department-centric architecture where Tickets are assigned to Departments, supports complex SLAs, and relies on a hierarchical structure of Departments, Agent Teams, and User Organizations. ([Deskpro developer documentation](https://developer.deskpro.com/))

Teams typically make this move for Deskpro's on-premise deployment option, its DPQL reporting language, multi-brand support, or when they've outgrown Help Scout's automation capabilities.

This is not a simple lift-and-shift. The two platforms have different enough data models that copy-paste won't work — but they're similar enough that a clean API pipeline handles the translation well.

## Help Scout vs. Deskpro: Data Model Mapping

Before writing any extraction code, map every Help Scout entity to its Deskpro equivalent. This mapping drives the entire migration pipeline.

| Help Scout Entity | Deskpro Equivalent | Migration Notes |
|---|---|---|
| Mailbox | Department | 1:1 mapping. Deskpro supports nested departments (e.g., Support > Technical, Support > Billing). |
| Conversation | Ticket | Status mapping requires translation (see below). |
| Thread (customer, reply, note) | Message / Note | Thread `type` field determines message direction. |
| Customer | Person | De-duplicate by email before import. |
| Company | Organization | Help Scout companies are loose associations; Deskpro orgs are first-class entities with formal membership. |
| Tag | Label | Direct 1:1 mapping. |
| Custom Field | Custom Field | Must be pre-created in Deskpro with matching types. Note the field IDs for API writes. |
| User (agent) | Agent | Create agents in Deskpro first; map by email. |
| Team | Agent Team | Rebuild team membership manually. |
| Saved Reply | Snippet | No bulk export API — must be recreated manually. |
| Workflow | Trigger / Automation | No export — must be rebuilt from scratch. |
| Docs Article | Knowledgebase Article | Separate API (Docs API v1) required for extraction. |

The migration order matters: create Organizations before People (so you can link them), create People before Tickets (so you can assign them), and create Agents before anything that needs an assignee.

### Status Mapping

Help Scout conversations have four statuses: **active**, **pending**, **closed**, and **spam**. Deskpro's core ticket statuses are **Awaiting Agent**, **Awaiting User**, and **Resolved**.

| Help Scout Status | Deskpro Status | Rationale |
|---|---|---|
| `active` | Awaiting Agent | Needs agent action. |
| `pending` | Awaiting User | Waiting on customer response. |
| `closed` | Resolved | Completed tickets. |
| `spam` | *(skip or archive)* | Don't migrate spam into a clean system. |

> [!WARNING]
> Deskpro does not support arbitrary custom ticket statuses. Its status model is deliberately constrained to three core states. If your team relied on Help Scout's `pending` status in a non-standard way (e.g., as an internal hold state), you'll need to use a Deskpro custom field or workflow state to replicate that behavior.

## Why Help Scout's Native Export Won't Work

Many teams try to use Help Scout's built-in export before realizing it won't get the job done. Whether you are moving to Deskpro or performing a [Help Scout to HubSpot Service Hub migration](https://clonepartner.com/blog/blog/help-scout-to-hubspot-service-hub-migration-technical-guide/), the native export (under Reports → Export) generates CSV/XLSX files, but **it does not include message bodies, thread replies, internal notes, or attachments**. If you import this data into Deskpro, your agents will see thousands of tickets with subject lines like "Need help with my account" — but the ticket bodies will be entirely empty.

The native export is useful for reporting snapshots — it is not a migration tool. For a migration-ready dataset, you must use the **[Help Scout Mailbox API v2](https://developer.helpscout.com/mailbox-api/)**.

Deskpro offers a **built-in Zendesk importer**, but there is no equivalent Help Scout importer. The migration path is exclusively: **Help Scout API → your transformation layer → Deskpro API**.

## Extraction: Getting Data Out of Help Scout

### Authentication Setup

Help Scout's Mailbox API v2 uses **OAuth 2.0 client credentials flow** — there is no static API key option for server-to-server access. Full documentation: [developer.helpscout.com/mailbox-api/overview/authentication/](https://developer.helpscout.com/mailbox-api/overview/authentication/).

1. Go to **Your Profile → My Apps** in Help Scout
2. Create a new OAuth application
3. Note the `App ID` and `App Secret`
4. Exchange credentials for a bearer token — tokens expire after **48 hours**

```bash
curl -X POST https://api.helpscout.net/v2/oauth2/token \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_APP_ID" \
  -d "client_secret=YOUR_APP_SECRET"
```

The response includes `access_token`, `expires_in` (seconds), and `token_type: bearer`.

> [!TIP]
> Cache your token and handle `401` responses by refreshing automatically. Implement `429` handling by honoring the `X-RateLimit-Retry-After` header. For bulk extraction spanning multiple hours, implement a checkpoint/resume pattern that persists the last successfully processed conversation ID to disk. Token expiry mid-extraction — and the silent data loss it causes when retry logic isn't implemented — is the most common cause of failed DIY migrations.
>
> **Checkpoint pattern (pseudocode):**
> ```
> load last_conversation_id from checkpoint.json
> resume pagination from that cursor
> on successful batch: write updated cursor to checkpoint.json
> on 401: refresh token, retry current batch
> on 429: sleep X-RateLimit-Retry-After seconds, retry
> ```

### Rate Limits by Plan

Help Scout's API rate limits are **per-account** (shared across all tokens and apps on the same account), not per-token. The following figures are empirically observed in bulk extraction workloads and not explicitly published in Help Scout's documentation — treat them as operational estimates, not contractual guarantees:

| Plan | Observed Rate Limit | Notes |
|---|---|---|
| Standard | ~200 requests/min | Write operations count double against quota. |
| Plus | ~400 requests/min | Write operations count double against quota. |
| Pro | ~800 requests/min | Best for large extractions. |

When you receive a `429` response from Help Scout, the `X-RateLimit-Retry-After` header specifies the number of seconds to wait before retrying. The response body is:
```json
{
  "message": "Too Many Requests",
  "retryAfter": 30
}
```

For a 50K-conversation account on the Plus plan: extracting conversations, then threads per conversation (one API call each), then customers, then attachments at ~400 req/min takes roughly **4–6 hours** of sustained API calls assuming no retries. Token refresh will occur at least twice during this window, making checkpoint/resume non-optional.

### Extraction Order

Extract in this sequence to preserve relational integrity:

1. **Mailboxes** — `GET /v2/mailboxes` (provides department mapping IDs)
2. **Users** — `GET /v2/users` (agents — needed for assignee mapping)
3. **Customers** — `GET /v2/customers` (paginated via HAL-style `_links.next`)
4. **Tags** — `GET /v2/tags` (flat list)
5. **Custom Fields** — `GET /v2/mailboxes/{id}/fields` (per-mailbox)
6. **Conversations** — `GET /v2/conversations?status=all&mailbox={id}&sortField=createdAt&sortDirection=asc` (include all statuses; use `sortField` and `sortDirection` for deterministic ordered extraction)
7. **Threads** — `GET /v2/conversations/{id}/threads` (per-conversation — this is where the actual message content lives)
8. **Attachments** — Download from thread attachment URLs before Help Scout access expires
9. **Docs articles** — Docs API v1: `GET https://docsapi.helpscout.net/v1/collections/{id}/articles` (uses **HTTP Basic auth with your API key as the username and "X" as the password** — not OAuth)

**Performance optimization:** The conversations list endpoint supports an `embed=threads` parameter that returns thread summaries inline, reducing round trips for low-thread-count conversations. For conversations with many threads, you still need the dedicated threads endpoint to get past the summary truncation.

> [!WARNING]
> The List Conversations endpoint returns a `preview` field containing only a truncated text snippet of the first message — **not the full message body**. You **must** call `GET /v2/conversations/{id}/threads` for each conversation to retrieve actual content. This is the single most common cause of empty ticket bodies in Help Scout migrations. Skipping the threads call produces tickets with correct metadata and subject lines but zero message content.

### Handling Merged Conversations

Help Scout allows conversations to be merged. When you request a merged (source) conversation via the API, it returns `HTTP 301 Moved Permanently` with a `Location` header pointing to the target conversation URL. Your extraction script **must follow these redirects automatically**, or you'll receive a silent `404` on the subsequent request and lose that conversation's data entirely.

```python
# Python example: always allow redirects
response = requests.get(
    f"https://api.helpscout.net/v2/conversations/{conv_id}",
    headers={"Authorization": f"Bearer {token}"},
    allow_redirects=True  # critical
)
if response.history:
    # log the redirect chain for auditing
    log_redirect(conv_id, response.url)
```

### Thread Type Mapping

Help Scout threads carry a `type` field that determines how they should be imported into Deskpro:

| Help Scout Thread Type | Deskpro Equivalent | Notes |
|---|---|---|
| `customer` | Message (from person) | Inbound customer message. |
| `reply` | Message (from agent) | Agent reply — map `createdBy.id` to agent. |
| `note` | Note (internal) | Internal-only; not visible to customer. |
| `message` | Message (from agent) | Agent-initiated outbound (proactive). |
| `forwardparent` | *(metadata only)* | Log as internal note with forward context. |
| `forwardchild` | *(new ticket reference)* | Handle as a linked ticket or note. |
| `phone` | Message | Treat as inbound; tag with "phone" channel. |
| `chat` | Message | Treat as inbound; tag with "chat" channel. |

Help Scout enforces a **[maximum of 100 threads per conversation](https://clonepartner.com/blog/blog/hubspot-service-hub-to-help-scout-migration-technical-guide/)**. Verify thread counts post-import for any conversations near this limit — pagination behavior at the boundary is a known edge case.

## Loading Data Into Deskpro

### Authentication

Deskpro's API v2 uses **API key authentication**. Create a key under **Admin → Apps & Integrations → API Keys**. Full documentation: [support.deskpro.com/en/guides/developers/api](https://support.deskpro.com/en/guides/developers/api).

```bash
curl -H "Authorization: key 2:YOUR_API_KEY" \
  https://your-instance.deskpro.com/api/v2/tickets
```

For migrations, create a **superuser API key** to use the `X-DeskPRO-Agent-ID` header for agent impersonation on write operations. This attributes migrated replies to the correct original agent rather than having every message appear as sent by the migration account.

**Cloud vs. self-hosted authentication note:** Both deployment types use the same API key format. For self-hosted instances, ensure your API key is created against the correct base URL (your internal hostname or external domain) and that SSL termination is configured correctly if you're running the migration pipeline externally.

### Common Deskpro API Error Responses

Understanding the error taxonomy prevents wasted debugging time:

| HTTP Status | Typical Cause | Resolution |
|---|---|---|
| `400 Bad Request` | Field type mismatch (e.g., sending string to integer custom field), missing required field, or malformed JSON | Check response body — Deskpro returns a `errors` array with field-level detail |
| `401 Unauthorized` | Invalid or expired API key | Verify key format: `key {key_id}:{key_secret}` |
| `403 Forbidden` | Agent lacks permission for the target department | Use superuser key or verify department access |
| `404 Not Found` | Referencing a Department, Person, or Agent ID that doesn't exist | Check entity creation order; verify IDs from creation responses |
| `409 Conflict` | Duplicate email address on Person creation | Fetch the existing Person by email, use their ID |
| `429 Too Many Requests` | Rate limit exceeded | Back off per `Retry-After` header; request limit increase from Deskpro support for cloud instances |

A field type mismatch `400` response body looks like:
```json
{
  "status": 400,
  "code": "invalid_input",
  "errors": {
    "fields": [
      {
        "id": 42,
        "error": "Field value must be one of: enterprise, growth, starter"
      }
    ]
  }
}
```

### Load Sequence

Create entities in dependency order:

1. **Departments** — Create departments matching Help Scout mailboxes (or redesign hierarchy now — Deskpro's nested departments support structures Help Scout cannot express)
2. **Organizations** — `POST /api/v2/organizations`
3. **People** — `POST /api/v2/people` (match customers by email; link to organizations; `409` on duplicate email means the Person already exists — fetch their ID with `GET /api/v2/people?primary_email=email@example.com`)
4. **Agents** — Create via admin UI or invite flow (Deskpro's API does not expose direct agent creation on standard cloud plans; confirm with Deskpro support for your tier)
5. **Labels** — Create labels matching Help Scout tags
6. **Custom Fields** — Pre-create under **Admin → Ticket Structure → Ticket Fields** with matching field types; note the integer field IDs — these are required in ticket payloads
7. **Tickets with Messages** — `POST /api/v2/tickets` with initial message, then `POST /api/v2/tickets/{id}/messages` for subsequent threads in chronological order
8. **Attachments** — Upload as blobs via `POST /api/v2/blobs`, then reference blob IDs in message payloads
9. **Knowledgebase Articles** — `POST /api/v2/knowledgebase/articles`

> [!CAUTION]
> **Disable outbound email before importing.** Under **Admin → Emails → Email Settings**, disable all outbound triggers before starting the load phase. If you skip this step, Deskpro will send email notifications to every customer whose ticket is created or updated during import — including new-account welcome emails for every Person record created. There is no unsend. Undo means manually contacting affected customers. Re-enable only after QA validation is complete.

### Creating Tickets with Historical Threads

Create the ticket with its first thread, then append subsequent threads in chronological order using `POST /api/v2/tickets/{id}/messages`. Always include `date_created` — omitting it causes every migrated ticket to show the migration run date as its creation timestamp, destroying all historical context.

```json
POST /api/v2/tickets
{
  "subject": "Original Help Scout Subject",
  "person": "customer@example.com",
  "department": 5,
  "agent": 12,
  "status": "resolved",
  "date_created": "2022-03-15T09:23:11Z",
  "message": {
    "message": "<p>First customer message body (HTML)</p>",
    "format": "html",
    "person": "customer@example.com",
    "date_created": "2022-03-15T09:23:11Z"
  },
  "fields": [
    { "id": 42, "value": "enterprise" }
  ]
}
```

Map Help Scout `createdBy.id` to the corresponding Deskpro Agent ID for each thread using your agent email-to-ID lookup table. If the original agent no longer exists in Deskpro, map those threads to a dedicated "Legacy Agent" account — do not silently drop the `createdBy` attribution.

**Delta sync implementation:** For the final cutover delta, use Help Scout's `modifiedSince` query parameter on the conversations endpoint: `GET /v2/conversations?modifiedSince=2024-01-15T00:00:00Z`. This returns all conversations created or updated after the specified timestamp. Clock skew risk between your migration server and Help Scout's servers is typically under 1 second — use a 60-second buffer on your cutover timestamp to be safe (i.e., set `modifiedSince` to 60 seconds before your actual initial sync end time).

> [!NOTE]
> Deskpro accepts HTML in message bodies, and Help Scout thread bodies are already HTML. You can pass them through directly — but you must sanitize `<img>` tags that reference Help Scout's CDN (`secure.helpscout.net`). Those URLs are authenticated and will return `403` errors after you cancel your Help Scout subscription. See the attachment section below for the correct handling pattern.

### Handling Attachments and Inline Images

Attachments are the most common point of failure in help desk migrations. Help Scout returns attachment metadata (filename, MIME type, size, URL) within the thread payload under `_embedded.attachments`.

**Standard attachment migration to Deskpro:**

1. Download the file from the Help Scout attachment URL (authenticated — include your bearer token)
2. Upload to Deskpro: `POST /api/v2/blobs` with `Content-Type: multipart/form-data`
3. Capture the returned `blob_id` integer
4. Include `"attachments": [{"blob_id": 12345}]` in the message payload when creating the ticket thread

**Inline image migration:**

Thread HTML often contains `<img src="https://secure.helpscout.net/...">` tags for images pasted directly into email bodies. These are authenticated CDN URLs that will return `403` after migration.

1. Parse the HTML thread body with an HTML parser (not regex)
2. Extract all `<img src="...">` values matching `secure.helpscout.net` or `attachments.helpscout.net` domains
3. Download each image with your Help Scout bearer token
4. Upload to Deskpro via `POST /api/v2/blobs`
5. Replace the original `src` attribute value in the HTML with the new Deskpro blob URL: `https://your-instance.deskpro.com/api/v2/blobs/{blob_id}/content`
6. Use the rewritten HTML as the message body in the Deskpro ticket creation payload

Skipping step 1–5 produces broken image placeholders across every historical ticket that contained inline images.

### Deskpro Rate Limits

For **cloud instances**, Deskpro enforces a default global rate limit. `429` responses include a `Retry-After` header. Contact Deskpro support to request a temporary rate limit increase for your migration window — this is a standard request and typically approved with a few days' notice.

For **self-hosted Deskpro**, you control the infrastructure. Rate limits are configurable at the application layer. For bulk import performance, consider:
- Increasing PHP-FPM worker count (default is often 5–10; increase to 20–30 for migration)
- Adjusting MySQL/PostgreSQL connection pool limits to match worker count
- Using the Deskpro CLI import tools if available for your version, which bypass the HTTP API entirely and write directly to the database

Self-hosted deployments also allow you to temporarily disable background job processing during import to reduce database contention, then re-enable it after load is complete.

## Knowledge Base Migration: Docs to Deskpro Knowledgebase

Help Scout's knowledge base (Docs) uses a **separate API — the Docs API v1** — with its own authentication (HTTP Basic: API key as username, `"X"` as password — not OAuth). Base URL: `https://docsapi.helpscout.net/v1/`. Documentation: [developer.helpscout.com/docs-api/](https://developer.helpscout.com/docs-api/).

The hierarchy mapping:

- **Help Scout:** Site → Collection → Category → Article
- **Deskpro:** Knowledgebase → Category → Article

The mapping is straightforward, but watch for these edge cases:

- **Article slugs and URLs change.** Set up 301 redirects from old Help Scout Docs URLs to new Deskpro Knowledgebase URLs, or you'll lose organic search traffic. For Apache: `Redirect 301 /docs/old-article-slug /kb/new-article-path`. For nginx: `return 301 /kb/new-article-path;` inside a location block matching the old Docs URL pattern.
- **Embedded images** in articles reference Help Scout CDN URLs. Download and re-upload to Deskpro, then rewrite `src` attributes using the same pattern as inline images above. If you leave the original URLs in place, those images break the moment you cancel your Help Scout subscription.
- **Article status** — Help Scout articles can be `published` or `notpublished`. Map to Deskpro's published/draft states respectively.
- **Docs API rate limits** are scoped per 10-minute window and tied to the number of Docs sites on your account, not your plan tier.

## What Cannot Be Migrated Automatically

Some Help Scout data has no API export path and must be rebuilt manually in Deskpro:

| Help Scout Feature | Why It Can't Be Migrated | Deskpro Rebuild Approach |
|---|---|---|
| **Workflows** | No export API. Stored as platform-internal configuration. | Rebuild as Deskpro Triggers and Automations. Deskpro's automation engine separates by event type: New Ticket Triggers, Ticket Update Triggers, and Time-based Escalations. |
| **Saved Replies** | Not accessible via API in bulk. | Recreate as Deskpro Snippets or Macros. |
| **Satisfaction Ratings** | Exportable as aggregate metrics only — no per-conversation CSAT data via API. | Migrate as custom field values (e.g., integer rating) or internal notes appended to each ticket, if the data is worth preserving. |
| **Beacon Configuration** | Widget config, not data. | Configure Deskpro Messenger from scratch; deploy new JavaScript snippet. Beacon and Deskpro Messenger are functionally equivalent (embedded chat + help widget), but configuration is not portable. |
| **Report Dashboards** | Platform-specific metric calculations not exportable. | Rebuild using Deskpro's DPQL report builder. DPQL only calculates metrics on tickets processed by Deskpro — migrated tickets will have timestamps but no SLA breach history. |

> [!TIP]
> Before migration, export every active Help Scout Workflow and Saved Reply to a structured spreadsheet: trigger conditions, matching criteria, actions taken, and frequency of use. This document becomes your Deskpro rebuild checklist and also gives you a chance to prune automations that are no longer serving a purpose.

**Historical reporting:** If SLA compliance history, CSAT scores, or time-to-resolution metrics matter for compliance or business review, export them from Help Scout's Reports section before cutover. Deskpro cannot retroactively calculate these metrics for migrated tickets.

## Migration Timeline and Planning

### Realistic Timeline Estimates

| Dataset Size | Estimated Duration | Phase Breakdown |
|---|---|---|
| Under 10K conversations | 1–2 weeks | 2–3 days extraction, 2–3 days transformation, 3–5 days loading + validation |
| 10K–50K conversations | 2–3 weeks | 3–5 days extraction, 3–4 days transformation, 1 week loading + validation |
| 50K+ conversations | 3–5 weeks | Add time for rate limit pacing, attachment volume, checkpoint/resume implementation, and extended QA |

Knowledge base migration adds **2–4 days** depending on article count and embedded media volume. Attachments are the primary driver of extraction time — a 10K conversation dataset with 3 attachments per conversation takes significantly longer than a 50K conversation dataset with no attachments.

### Pre-Migration Checklist

1. **Audit your Help Scout data.** Count conversations by status, customers, tags, custom fields, and Docs articles. Count attachment-bearing conversations separately — this drives the bulk of extraction time.
2. **Set up Deskpro departments.** Mirror your Help Scout mailbox structure — or redesign it now. Deskpro supports nested departments, so this is your opportunity to add hierarchy that Help Scout couldn't express.
3. **Pre-create custom fields in Deskpro.** Match field types (text, dropdown, date, checkbox) exactly. Integer field IDs from the Admin UI are required for API writes. Type mismatches produce `400` errors on every affected ticket.
4. **Create agents and map emails.** Build an email → Deskpro Agent ID lookup table. Agents must exist before tickets can be assigned to them. Create a "Legacy Agent" placeholder for departed staff.
5. **Disable all outbound email in Deskpro** before importing.
6. **Set up a delta sync window.** Agree on a freeze time for Help Scout — the point at which agents stop making changes. Your delta script will use this timestamp as the `modifiedSince` parameter.
7. **Test the pipeline on a single mailbox.** Migrate one Help Scout mailbox completely before starting the full run. Verify entity counts, timestamps, attachments, and inline images in that subset.

## Cutover Strategy

A zero-downtime cutover requires a **delta migration approach**:

1. **Initial Sync:** Migrate all historical data while your team continues working in Help Scout. Depending on volume, this runs for days. Use checkpoint/resume; do not attempt this as a single-run script.
2. **Delta Sync:** Schedule a cutover window (typically over a weekend). Pause incoming mail to Help Scout, run a final delta script using `GET /v2/conversations?modifiedSince={freeze_timestamp}` to catch everything created or updated since the initial sync completed, and load those tickets into Deskpro.
3. **DNS / Email Routing Cutover:** Update your email forwarding rules and MX records (or Help Scout → Deskpro inbox forwarding) to route mail to Deskpro.
4. **Go Live:** Agents log into Deskpro. All historical context is present; new mail routes correctly.

The delta sync is what separates a clean cutover from an ugly one. Without it, you either lose recent tickets or force agents to work in both systems simultaneously while you reconcile the gap.

## Validation and Post-Migration QA

Run these checks after loading before re-enabling email notifications:

- **Conversation count match.** Total tickets in Deskpro should equal total conversations exported from Help Scout (minus spam, if excluded). A discrepancy means missed conversations or redirect-following failures on merged conversations.
- **Thread count per ticket.** Spot-check 50+ tickets: does each have the correct number of messages and notes? Pay special attention to tickets with forwarded threads.
- **Attachment integrity.** Download a random sample of migrated attachments; compare file size and SHA-256 checksum against the originals downloaded during extraction.
- **Customer-to-ticket linkage.** Verify tickets are associated with the correct Person record — not a duplicate created by a casing difference in the email address.
- **Custom field values.** Spot-check dropdown selections, date values, and text fields against source data. Type mismatches during import silently zero out field values rather than erroring in some Deskpro versions.
- **Timestamp accuracy.** Confirm `date_created` on migrated tickets matches the original Help Scout conversation creation date — not the import run date.
- **Agent assignment.** Verify tickets show the original assigned agent, not the migration superuser account.
- **Inline image rendering.** Open historical tickets with known embedded images and confirm images render from Deskpro blob URLs, not broken Help Scout CDN references.

Re-enable outbound email only after all checks pass.

## Common Edge Cases and Failure Modes

**Merged conversations.** API returns `301 Moved Permanently` for source conversations. Scripts that don't follow redirects receive `404` and silently drop the conversation. Always set `allow_redirects=True` (or equivalent) and log the redirect chain for audit purposes.

**Thread limit.** Help Scout enforces a maximum of 100 threads per conversation. Verify thread counts match post-import for any conversations that approached this limit during extraction.

**Inline images in thread bodies.** Thread HTML contains authenticated `<img src="https://secure.helpscout.net/...">` tags. These return `403` post-migration. Download, re-upload to Deskpro blobs, and rewrite `src` attributes. HTML parsing required — regex is not sufficient for this.

**Duplicate customers.** Help Scout allows multiple Customer records with different email addresses for the same person. Deskpro de-duplicates People by primary email. Run a de-duplication pass before import, or you'll create orphaned Person records that agents cannot easily merge.

**Company-to-Organization mismatch.** Help Scout Companies are loosely associated with Customers via a reference field. Deskpro Organizations are first-class entities requiring explicit People membership. Create each Organization first, then explicitly set the `organization_id` on each Person record during import. Implicit association does not transfer.

**Clock skew on delta sync.** Help Scout's `modifiedSince` filter compares against server time. Use a 60-second buffer on your cutover timestamp (i.e., set `modifiedSince` to 60 seconds before your actual freeze time) to avoid edge cases where tickets modified in the final seconds of the initial sync window are missed.

**Docs API authentication.** The Docs API uses HTTP Basic authentication (API key as username, literal string `"X"` as password), not the OAuth bearer tokens used by the Mailbox API. Attempting OAuth on Docs API endpoints returns `401`.

## DIY vs. Managed Migration: When to Get Help

**DIY is viable when:**
- Fewer than 10K conversations
- No custom fields or minimal custom field usage
- No knowledge base to migrate
- An engineer available to commit 1–2 weeks full-time
- Comfortable implementing OAuth token refresh, HAL-style pagination, checkpoint/resume, and `301` redirect following

**Managed migration makes sense when:**
- Dataset exceeds 20K conversations
- Complex custom field schemas, multiple mailboxes, or a large Docs site
- Agents cannot afford extended downtime or a botched cutover requiring re-migration
- You need a tested delta sync implementation
- You're encountering rate limits that extend extraction beyond your cutover window

The failure modes that burn DIY teams on first attempts are consistently: merged conversations dropping silently due to `301` handling, OAuth tokens expiring mid-extraction without checkpoint/resume, and inline images left as broken CDN references because the HTML rewriting step was skipped.

## Making the Decision

A Help Scout to Deskpro migration is fundamentally a **data model translation problem**. The biggest risks are data loss from using the conversation `preview` field instead of calling the threads endpoint, notification floods from skipping the email-disable step, and broken inline images from unresolved Help Scout CDN URLs.

Treat it as an engineering project. Map your data model before writing any code. Implement checkpoint/resume before running against production data. Respect the API rate limits. Disable outbound email before the load phase. And verify the output against source counts before you flip the switch.

> **Planning a Help Scout to Deskpro migration?** Our engineers have run this exact playbook dozens of times. We map the schemas, extract the threads, fix the inline images, and guarantee your data is accurate. Book a 30-minute technical scoping call — timeline, edge cases, and cost — with no commitment.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Does Deskpro have a built-in Help Scout importer?

No. Deskpro offers a built-in Zendesk importer but has no equivalent for Help Scout. You must build a custom pipeline using the Help Scout Mailbox API v2 for extraction and the Deskpro REST API v2 for loading.

### Can I use Help Scout's CSV export to migrate to Deskpro?

No. Help Scout's native CSV export does not include message bodies, thread replies, internal notes, or attachments. It only covers reporting-level metadata. You must use the Mailbox API v2 to extract full conversation data.

### How long does a Help Scout to Deskpro migration take?

For under 10K conversations, expect 1–2 weeks. For 10K–50K conversations, plan for 2–3 weeks. Datasets over 50K conversations with attachments and knowledge base articles can take 3–5 weeks, largely driven by API rate limits and QA time.

### How do Help Scout statuses map to Deskpro statuses?

Help Scout's 'active' maps to Deskpro's 'Awaiting Agent', 'pending' maps to 'Awaiting User', and 'closed' maps to 'Resolved'. Deskpro does not support arbitrary custom statuses — it uses a fixed three-state model. Spam conversations should be skipped.

### What Help Scout data cannot be migrated to Deskpro automatically?

Workflows, Saved Replies, Beacon configurations, per-conversation satisfaction ratings, and report dashboards cannot be exported via API. These must be manually recreated in Deskpro as Triggers, Snippets, Messenger config, and DPQL reports.
