The Complete Guide to Migrating from Enchant to Help Scout
Migrating from Enchant to Help Scout? Learn the exact API sequence to map customers, transfer conversation threads, and handle attachments with zero data loss
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
Making the switch from Enchant to Help Scout requires a clear technical plan. This guide focuses on using the available APIs to ensure your historical context remains intact while you transition to your new workspace.
Define Your Migration Scope
Most of your high-volume data — tickets, messages, customers, and attachments — can be handled programmatically. These objects have clear counterparts in both systems.
Certain structural elements require manual setup before the migration begins:
- Users: Help Scout's API manages existing users rather than creating new ones, so team members must be added manually with correct permissions.
- Mailboxes (Inboxes): Every Enchant ticket belongs to an inbox. You must recreate these as mailboxes in Help Scout before migration so you can map
inbox_id→mailboxIdduring import.
For data that is no longer relevant — old spam, expired drafts — archive it in Enchant before you begin rather than carrying noise into Help Scout.
Data Schema: Enchant → Help Scout Field Mapping
The table below shows the core object and field mapping between the two platforms.
| Enchant Object | Enchant Field / Endpoint | Help Scout Equivalent | Help Scout Field / Endpoint | Notes |
|---|---|---|---|---|
| Ticket | GET /tickets |
Conversation | POST /v2/conversations |
Status, subject, created_at map directly |
| Inbox | inbox_id |
Mailbox | mailboxId |
Must be pre-created manually |
| Message (reply) | type: reply |
Thread | POST /v2/conversations/{id}/reply |
Customer-facing |
| Message (note) | type: note |
Thread (note) | POST /v2/conversations/{id}/notes |
Internal only |
| Customer | GET /customers |
Customer | POST /v2/customers |
Multiple contacts (email, phone) map to Help Scout's contact array |
| Label | label_id |
Tag | tags [] on conversation |
String-based in Help Scout |
| Attachment | GET /attachments/{id} |
Attachment | POST /v2/attachments → returns attachmentId |
Must be pre-uploaded; one attachment per message |
Known gaps — data with no direct API equivalent in Help Scout:
- Collision detection settings (no equivalent object)
- Enchant workflow trigger types that depend on Enchant-specific ticket states
- Enchant's credit-weighted action log (no equivalent audit trail object)
If you cannot map a field, document it explicitly before the migration runs. Dropping it silently creates invisible data loss.
Migration Sequence
Order matters. Creating a thread before the parent customer record exists will produce orphaned or failed records. Follow this dependency chain:
1. Customers → POST /v2/customers
2. Attachments → POST /v2/attachments (capture returned attachmentId)
3. Conversations → POST /v2/conversations (reference customerId + mailboxId)
4. Threads → POST /v2/conversations/{id}/reply or /notes
(embed attachmentId captured in step 2)
5. Tags → PATCH /v2/conversations/{id} (apply tag array)
Pseudocode: attachment-safe thread creation
for ticket in enchant_tickets:
customer_id = upsert_customer(ticket.customer) # Step 1
attachment_ids = []
for attachment in ticket.attachments:
result = helpscout.post('/v2/attachments', {
'fileName': attachment.filename,
'mimeType': attachment.mime_type,
'data': base64_encode(attachment.content)
})
attachment_ids.append(result['attachmentId']) # Step 2
conversation_id = helpscout.post('/v2/conversations', {
'subject': ticket.subject,
'mailboxId': inbox_id_map[ticket.inbox_id],
'customer': {'id': customer_id},
'status': map_status(ticket.status),
'createdAt': ticket.created_at
})['id'] # Step 3
for message in ticket.messages:
endpoint = '/reply' if message.type == 'reply' else '/notes'
helpscout.post(f'/v2/conversations/{conversation_id}{endpoint}', {
'body': message.body,
'isHtml': message.html, # Enchant boolean field
'attachments': attachment_ids if message.has_attachments else []
}) # Step 4
helpscout.patch(f'/v2/conversations/{conversation_id}', {
'tags': [label_map[l] for l in ticket.labels]
}) # Step 5Rate Limits and Volume Planning
Both APIs throttle requests, and a large migration will hit those limits without pacing logic.
Enchant uses a credit-based system. Each API call costs credits against a limit of 100 credits per minute. Operations that embed or count related objects (e.g., fetching a ticket with ?embed=messages,attachments) consume more credits than a plain fetch.
Help Scout uses a request-count limit per rolling window. The exact limit depends on your plan and the number of active mailboxes. Monitor the X-RateLimit-Remaining and X-RateLimit-Reset response headers on every call — they tell you how many requests remain in the current window and when it resets.
Volume estimate for planning purposes:
| Operation | API Calls per 1,000 Tickets (estimate) |
|---|---|
| Customer upsert | 1,000 (one per unique customer) |
| Attachment upload | Varies; assume 2–4 per ticket average = 2,000–4,000 |
| Conversation create | 1,000 |
| Thread create | 3–8 per ticket average = 3,000–8,000 |
| Tag apply | 1,000 |
| Total | ~8,000–15,000 calls per 1,000 tickets |
For a 10,000-ticket migration, budget 80,000–150,000 API calls. At Enchant's 100-credit/minute ceiling, the export phase alone can take 14+ hours without parallelism or credit-efficient embedding. Build sleep/retry logic around the rate limit headers from day one.
Failure Modes and Error Handling
This is where most migrations break silently.
Attachment upload fails mid-migration: If POST /v2/attachments returns an error, do not proceed to thread creation for that message. Log the failed attachment with its Enchant ID, skip it, and queue it for retry. Creating the thread without the attachment ID means the attachment is permanently lost with no indication in the Help Scout record.
Conversation creation fails: If the POST /v2/conversations call fails (e.g., invalid mailboxId, missing customer), log the full Enchant ticket ID and payload. Do not attempt to create threads for that conversation — they will have no parent and the API will reject them anyway.
Enchant HTML boolean: Enchant tracks whether a message body is HTML via a boolean field on the message object. If your migration script ignores this and sends HTML content as plain text (or vice versa), the thread body will render as broken markup in Help Scout. Read this field explicitly and pass isHtml: true/false on every thread creation call.
Attachment re-use: An attachment uploaded to Help Scout is linked to a single message. If the same file appears on multiple Enchant messages, you must upload it separately for each thread. Attempting to reuse the same attachmentId across threads will produce errors or missing files.
Retry logic: Use exponential backoff on any 429 (rate limited) or 5xx (server error) response. A simple retry without backoff will continuously hit the rate limit ceiling and stall indefinitely.
Post-Migration Configuration
After data transfer completes, rebuild the following manually inside Help Scout:
- Workflows: Automation logic must be recreated in the Help Scout interface. The API does not support programmatic workflow creation.
- Saved replies: Recreate these manually. The API can read them but the content is best reviewed and updated during the rebuild.
Validation Checklist
Do not close the migration until you have verified the following:
- Total conversation count in Help Scout matches total ticket count exported from Enchant
- Spot-check 20–30 conversations: confirm thread count, attachment count, and tag labels match source records
- Confirm internal notes are marked as notes (not customer-visible replies) in Help Scout
- Verify at least one attachment per mailbox opens correctly
- Check timestamp alignment:
createdAton Help Scout conversations should matchcreated_aton Enchant tickets - Confirm no customer records are duplicated (check by email address)
- Verify tag strings match the label names from Enchant (casing differences can create duplicate tags)
If you'd rather focus on your revenue instead of wrestling with mapping sheets and pagination loops, ClonePartner can handle the full migration for you. Every project gets a dedicated engineer who understands the nuances of both APIs and ensures zero downtime.