LiveChat to Help Scout Migration: The Technical Guide
Step-by-step technical guide to migrating from LiveChat to Help Scout. Covers API constraints, data mapping, edge cases, rate limits, and migration approaches.
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
LiveChat to Help Scout Migration: The Technical Guide
TL;DR: Migrating from LiveChat to Help Scout is a data model translation, not a file import. LiveChat's chat → thread → event hierarchy must be flattened into Help Scout's conversation → thread structure. There is no native import path between the two systems. Every full-fidelity approach requires the LiveChat Agent Chat API for extraction and the Help Scout Mailbox API v2 for loading — with the imported: true flag set on every record to preserve historical timestamps without triggering notifications. Plan for Help Scout's 100-thread-per-conversation cap, write rate limits that count 2x, and the manual rebuild of all automations.
Why Teams Move from LiveChat to Help Scout
Teams migrate from LiveChat to Help Scout for three common reasons:
- Consolidation into a shared inbox. Help Scout's conversation model treats every interaction — email, chat, phone — as a unified thread. Teams running LiveChat alongside a separate email tool want a single pane of glass for all support channels.
- Simpler pricing and agent experience. Help Scout charges per user with unlimited contacts and an intentionally minimal interface. Teams that find LiveChat's higher-tier per-seat pricing expensive, or who don't need LiveChat's e-commerce and sales features, often downsize.
- Knowledge base integration. Help Scout Docs and the Beacon widget provide a self-service layer tied directly to the inbox. Teams that want help center content and chat in one system land here.
Before committing, understand what you're giving up. LiveChat has specific capabilities that Help Scout's Beacon does not replicate:
| Feature | LiveChat | Help Scout Beacon |
|---|---|---|
| Real-time visitor monitoring (pages viewed, time on site) | ✅ | ❌ |
| Sneak peek (see what customer is typing before send) | ✅ | ❌ |
| Proactive chat triggers (page time, URL, visit count) | ✅ (Auto-greetings) | Limited (Messages, no URL/time conditions) |
| E-commerce integrations (Shopify cart data, order lookup) | ✅ (native) | Via third-party integrations |
| Chat routing by URL or department | ✅ | ❌ (manual assignment or workflows) |
| Visitor queuing with estimated wait time | ✅ | ❌ |
| Agent-to-agent chat transfer mid-conversation | ✅ | Reassignment only (no live transfer) |
| Typing indicators (agent → customer) | ✅ | ✅ |
If your team relies heavily on proactive chat triggers and sales-oriented features, validate that Beacon covers your use cases before starting the migration.
Cost matters, but the pricing shapes are different. LiveChat is priced per agent per month ($20–$59/agent on annual billing as of 2025) and sells ChatBot separately; Help Scout is priced per user ($25–$75/user on annual billing), prices inbox capacity differently, and charges AI Answers per resolution. Model the bill using your actual seat count, inbox count, and AI usage before you assume one platform is cheaper. (livechat.com)
Core Data Model Differences
The fundamental mismatch between these two platforms drives every migration decision.
LiveChat's data model is chat-centric. The primary object is a Chat, which contains one or more Threads (a thread is a single conversation session — e.g., a customer returning to the same chat creates a new thread). Each thread contains Events — messages, system messages, file uploads, and filled forms. Customers are identified by email or a generated visitor ID. Chats belong to Groups (departments) and are handled by Agents.
Help Scout's data model is conversation-centric. The primary object is a Conversation, which lives inside a Mailbox. Each conversation contains Threads — but in Help Scout's terminology, a thread is an individual message (customer reply, agent reply, note, or chat message). Customers are stored as Customer records with associated emails, phone numbers, and company associations.
The critical terminology collision: LiveChat "thread" = a conversation session containing many messages. Help Scout "thread" = a single message within a conversation. Every LiveChat event (message) within a thread maps to one Help Scout thread.
| LiveChat Concept | Help Scout Equivalent | Notes |
|---|---|---|
| Chat | Conversation | One chat maps to one conversation |
| Thread (session) | Conversation | LiveChat threads within a chat may need to be flattened |
| Event (message) | Thread (message) | Each message becomes a Help Scout thread |
| Customer / Visitor | Customer | Match on email; visitors without email need handling |
| Agent | User | Must be pre-created in Help Scout before migration |
| Group | Mailbox | Create mailboxes before import |
| Tag | Tag | Direct 1:1 mapping; normalize to lowercase |
| Canned Response | Saved Reply | Must be rebuilt manually |
| Pre-chat survey fields | Custom Field or Customer Property | Max 10 custom fields per inbox (Plus plan required) |
| Chat rating | — | Limited import support; store as tag or note |
| Chat properties (custom) | Custom Fields or Customer Properties | 10 custom fields per inbox; 50 customer/company properties globally |
Help Scout limits each conversation to 100 threads maximum. If any LiveChat chat has more than 100 individual messages (events), the API will return HTTP 412 when you attempt to add thread 101. You must split these into multiple Help Scout conversations or consolidate messages before loading.
Migration Approaches
Five approaches exist. The right one depends on your data volume, engineering capacity, and tolerance for data loss.
1. CSV Export + Manual Import
How it works: Export chat reports from LiveChat as CSV files (available on Team plan and above). Use the data for reference, audits, or analytics.
When to use it: You only need metadata — chat dates, agent names, customer emails, ratings — not actual conversation content.
Limitations: LiveChat's CSV export includes metadata only, not message bodies or full transcripts. The raw Chats export is capped at the latest 100,000 records per license per export, so larger datasets need date-windowed exports. Help Scout has no native CSV import for conversations. (livechat.com)
Complexity: Low | Scalability: Small datasets only | Risk: High data loss
2. Help Scout's In-Product Importer (Import2)
How it works: Create users and inboxes in Help Scout first, then use Help Scout's in-product import flow — powered by Import2 — to connect LiveChat and run a sample import followed by a full migration. LiveChat is listed as a supported source. (docs.helpscout.com)
What it imports:
- Chat transcripts with message bodies
- Customer profiles (name, email)
- Tags and labels
- Attachments
What it does NOT import:
- Users/agents (must be pre-created)
- Inbox settings and routing rules
- Knowledge base content
- Custom field values beyond the tool's default mapping
- Chat ratings or custom chat properties
Pricing: Imports up to 500,000 records are included. Above that threshold, Import2 bills $500 per additional 500,000 records.
Known limitations:
- You cannot customize field-level mapping beyond the tool's defaults — if LiveChat uses custom properties extensively, those values may be dropped
- The sample import covers a small subset; discrepancies in edge cases (anonymous visitors, rich messages, multi-agent chats) may not surface until the full run
- Rollback requires manual deletion of imported conversations
- No published SLA on import duration for large datasets
When to use it: You want the lowest internal engineering load, your data is mostly standard chats with identified customers, and you have fewer than ~50,000 chats. Run the sample import and verify field coverage before committing to a full run.
Complexity: Low–Medium | Scalability: Small to mid-market | Risk: Low for standard setups, higher for custom schemas
3. API-Based Custom Migration
How it works: Write a script that reads from LiveChat's Agent Chat API (list_archives endpoint) and writes to Help Scout's Mailbox API v2, creating conversations and appending threads with preserved timestamps, tags, custom fields, and attachments. (platform.text.com)
When to use it: You need full-fidelity migration with message bodies, attachments, timestamps, tags, and customer records — and have engineering capacity.
Pros:
- Full control over data mapping and transformation
- Preserves message bodies, attachments, and metadata
- Handles edge cases with custom logic
- Supports incremental and phased migration
Cons:
- Requires engineering time (typically 2–4 weeks for a production-ready script)
- Must handle pagination, rate limits, retries, and error logging
- Help Scout write requests count as 2x toward the rate limit
- You own the bugs
Complexity: High | Scalability: Enterprise-grade | Risk: Medium (if tested properly)
4. Custom ETL Pipeline
How it works: Build an extract-transform-load pipeline with a staging database, checkpoints, replay queues, and validation tables. This is the API method with proper infrastructure — orchestration, monitoring, and intermediate storage for safe restarts.
When to use it: Large-scale migrations (100K+ chats) where you need staging, validation, audit trails, and phased cutover.
Pros:
- Full control and auditability
- Validation passes before loading
- Supports incremental loads and rollback
- Intermediate storage enables debugging without re-extracting
Cons:
- Highest engineering effort
- Requires infrastructure (database, orchestration, monitoring)
- Over-engineered for small datasets
- LiveChat bills private API usage per request after an initial free top-up of credits (exact pricing varies by plan — check your Developer Console for current balance) (platform.text.com)
Complexity: High | Scalability: Enterprise-grade | Risk: Low (if well-tested)
5. Middleware Platforms (Zapier, Make)
How it works: Use Zapier or Make to connect LiveChat triggers to Help Scout actions. Both platforms support triggers like "Chat Started" and "Chat Changed" from LiveChat, and actions like "Create Conversation" in Help Scout.
When to use it: Ongoing sync of new chats going forward during a transition period. Not suitable for historical data migration.
Limitations: These tools are strictly trigger-based. You cannot batch-process historical chats without hitting task quotas and rate throttling. LiveChat transcript forwarding only sends future transcripts — it does not backfill. (zapier.com)
Complexity: Low | Scalability: Low (ongoing sync only) | Risk: Medium
Approach Comparison
| Approach | Complexity | Full Transcripts | Attachments | Historical Data | Scalability | Cost |
|---|---|---|---|---|---|---|
| CSV Export | Low | ❌ | ❌ | Partial | Small | Free |
| Import2 (in-product) | Low–Med | ✅ | ✅ | ✅ | Mid-market | Per-record above 500K |
| API Script | High | ✅ | ✅ | ✅ | Enterprise | Dev time |
| ETL Pipeline | High | ✅ | ✅ | ✅ | Enterprise | Dev + infra |
| Zapier/Make | Low | ❌ | Partial | ❌ | Low | Subscription |
Recommendations:
- Small team, < 5,000 chats, low eng bandwidth: Start with Help Scout's in-product importer. Run the sample import and verify edge case coverage.
- Mid-size, 5,000–50,000 chats, has a developer: API script with a staging database.
- Enterprise, 50,000+ chats, dedicated team: Full ETL pipeline with validation.
- Ongoing sync during transition: Zapier or Make for new chats, API migration for history.
Pre-Migration Planning
Data Audit Checklist
Before writing any code, inventory what you have in LiveChat and what Help Scout can accept.
- Chats: Total count, date range, chats with vs. without customer email
- Customers: Total unique customers, customers with email vs. anonymous visitors
- Agents: Active and inactive agents — all must be pre-created as Help Scout Users (the API cannot create users)
- Groups: List all LiveChat groups; map each to a Help Scout Mailbox
- Tags: Export full tag list; check for duplicates and naming inconsistencies
- Custom properties: Document all custom chat properties and pre/post-chat survey fields; Help Scout has hard limits on these
- Attachments: Estimate total file count and size; check for expired CDN links
- Canned responses: These cannot be migrated via API — export and rebuild manually as Help Scout Saved Replies
- Chats exceeding 100 messages: Query LiveChat to identify these upfront; they require splitting logic
Also confirm your LiveChat plan's data retention. Starter plans list 60-day history, Team plans 12 months, while Business and Enterprise tiers offer longer or unlimited retention. If you're on a Starter plan, extract data immediately — it may expire before your migration completes. (livechat.com)
Define Migration Scope
Not everything needs to move. Categorize your data:
- Migrate via API: Chat transcripts, customer records, tags, custom field values, attachments
- Rebuild manually: Canned responses → Saved Replies, Auto-greetings → Help Scout Messages, Routing rules → Help Scout Workflows
- Leave behind: Chat analytics/reports (re-derive from Help Scout), visitor monitoring data, goal tracking data, bot interactions without human involvement
Choose a Migration Strategy
- Big bang: Migrate everything over a weekend, cut over Monday morning. Works for < 20,000 chats. Requires a change freeze on LiveChat.
- Phased: Migrate historical data first, run Zapier/Make for new chats during transition, then cut over. Reduces risk but increases complexity.
- Incremental: Run daily syncs for a defined period, validate continuously, cut over when confident. Best for large datasets.
Change freeze: Once you approve the mapping schema, do not alter tags, routing rules, or custom properties in LiveChat. Changes during migration create drift between source and target that is expensive to reconcile.
Data Mapping: LiveChat Fields to Help Scout
Conversation-Level Mapping
| LiveChat Field | Help Scout Field | Transform Notes |
|---|---|---|
chat.id |
Tag or note | Store as livechat-{id} for traceability |
chat.threads [0].created_at |
conversation.createdAt |
Convert ISO 8601 timestamp; must include timezone offset or use UTC |
chat.users [type=customer].email |
conversation.customer.email |
Required; handle chats without email separately |
chat.users [type=customer].name |
conversation.customer.firstName/lastName |
Split on first space |
chat.users [type=agent].email |
conversation.assignee |
Map to Help Scout User ID (integer) via lookup |
chat.tags |
conversation.tags |
Normalize to lowercase, remove spaces |
chat.properties.rating.score |
— | Import as tag (rating:good) or note |
chat.threads [].active |
conversation.status |
false → closed, true → active |
Thread-Level Mapping
| LiveChat Event | Help Scout Thread Type | Notes |
|---|---|---|
message (customer) |
customer thread |
Set imported: true to prevent reopening |
message (agent) |
reply thread |
Map author_id to Help Scout User ID |
system_message |
note thread |
Agent-only; not customer-facing |
file event |
Attachment on thread | Base64-encode; max 10 MB per file |
filled_form (pre-chat) |
note or Custom Field |
Max 10 custom fields per inbox |
rich_message |
customer or reply |
Flatten to HTML or plain text; preserve card/button text as structured content |
Customer-Level Mapping
| LiveChat Field | Help Scout Field | Notes |
|---|---|---|
customer.email |
customer.emails [].value |
Primary identifier; Help Scout deduplicates on email |
customer.name |
customer.firstName + customer.lastName |
Split on first space |
customer.fields (custom) |
Customer Properties | Requires Plus plan; max 50 globally |
customer.last_visit.ip |
— | Not importable; store in notes if needed |
| Company / domain | Organization + Company Properties | Use organizationId for company-level grouping |
Help Scout does not support user creation via the API. All agents must be invited through the Help Scout UI (or via SCIM on the Pro plan) before migration. Build a User ID lookup map (agent email → Help Scout integer User ID) before starting the import. Query GET /v2/users to retrieve IDs for all pre-created users.
Migration Architecture
Data Flow
LiveChat Agent Chat API (v3.5+)
↓ list_archives (POST, paginated, max 100 chats/page)
↓ Each chat contains chat.threads[] array
↓ Each thread contains thread.events[] array
↓ Download file attachments from CDN URLs immediately
↓
Staging Layer (JSON files or database)
↓ Transform: split names, map agents, flatten threads[].events[]
↓ Validate: check for missing emails, oversized threads (>100 events)
↓ Store source chat ID → target conversation ID mapping for idempotency
↓
Help Scout Mailbox API v2
↓ Create/match Customers (POST /v2/customers or GET /v2/customers?email=)
↓ Create Conversations with first thread (POST /v2/conversations, imported: true)
↓ Add remaining threads (POST /v2/conversations/{id}/chats|reply|notes)
↓ Update custom fields (PUT /v2/conversations/{id}/fields)
Rate Limit Budget
Plan your throughput around the tighter constraint:
| Platform | Limit | Effective Throughput |
|---|---|---|
| LiveChat | Rate-limited with too_many_requests (HTTP 429) responses; use adaptive backoff |
Varies by plan; monitor Retry-After header |
| Help Scout (reads) | 400 requests / minute / account | 400 req/min |
| Help Scout (writes) | Writes count 2x toward the 400/min limit; burst cap of 12 writes / 5 seconds | ~200 write calls/min (counting as 400), but burst-constrained to ~144/min sustained |
Throughput estimates by migration size:
| Dataset Size | API Calls (approx.) | Estimated Execution Time | Infrastructure Notes |
|---|---|---|---|
| 10,000 chats × 8 msgs avg | ~80,000 writes | 9–10 hours | Single script, local staging |
| 50,000 chats × 8 msgs avg | ~400,000 writes | 46–50 hours | Staging DB, checkpoint/resume, run over 2–3 days |
| 100,000 chats × 10 msgs avg | ~1,000,000 writes | 115–120 hours | Full ETL with orchestration, parallel extraction, sequential loading |
Build in backoff logic. Monitor X-RateLimit-Remaining headers on both sides. (developer.helpscout.com)
Help Scout API Error Codes in Migration Context
| HTTP Status | Cause in Migration Context | Recommended Response |
|---|---|---|
| 201 | Success (conversation or thread created) | Log mapping; continue |
| 400 | Malformed payload (missing required field, invalid email format) | Log error with payload; skip record; fix in transform |
| 404 | Invalid conversation ID when adding thread (conversation was deleted or ID mismatch) | Re-query for conversation; log for manual review |
| 409 | Conflict — duplicate resource | Check idempotency map; skip if already created |
| 412 | Precondition failed — 100-thread limit exceeded | Split conversation; log original chat ID |
| 422 | Validation error (e.g., invalid tag format, unrecognized custom field) | Log payload and error detail; fix transform logic |
| 429 | Rate limit exceeded | Read Retry-After header; sleep and retry |
| 500/503 | Help Scout server error | Exponential backoff with max 3 retries; log for manual retry |
Authentication
LiveChat: Generate a Personal Access Token (PAT) in the Developer Console under Tools → Personal Access Tokens. Required scopes: chats--all:ro and chats--access:ro for list_archives.
Help Scout: Create an OAuth 2.0 application under Your Profile → My Apps. Use the Client Credentials flow for server-to-server migration scripts. Tokens are valid for 48 hours — implement automatic token refresh for long-running migrations.
Step-by-Step Migration Process
Step 1: Extract Data from LiveChat
Use the list_archives endpoint to pull all historical chats. The response returns a chats array where each chat contains a threads array, and each thread contains an events array. Store raw JSON payloads locally before attempting any transformation — this gives you a restart point if anything fails downstream.
import requests
import json
import time
import os
LIVECHAT_PAT = "your_pat_token"
LIVECHAT_API = "https://api.livechatinc.com/v3.5/agent/action/list_archives"
def extract_chats(output_dir="./raw_chats"):
"""Extract all archived chats from LiveChat.
LiveChat v3.5 response structure:
{
"chats": [
{
"id": "PJ0MRSHTDG",
"threads": [
{
"id": "K600PKZON8",
"created_at": "2019-12-17T07:57:13.000000Z",
"events": [
{"type": "message", "text": "...", "author_id": "..."},
{"type": "file", "url": "..."},
{"type": "filled_form", "fields": [...]}
]
}
],
"users": [
{"id": "visitor_id", "type": "customer", "email": "...", "name": "..."},
{"id": "agent@company.com", "type": "agent", "name": "..."}
],
"properties": {"rating": {"score": {"value": "good"}}},
"tags": ["vip", "billing"]
}
],
"next_page_id": "..."
}
"""
os.makedirs(output_dir, exist_ok=True)
all_chats = []
page_count = 0
payload = {"limit": 100, "sort_order": "asc"}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {LIVECHAT_PAT}"
}
while True:
response = requests.post(LIVECHAT_API, json=payload, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
continue
response.raise_for_status()
data = response.json()
chats = data.get("chats", [])
all_chats.extend(chats)
# Save each page to disk for restart safety
page_count += 1
with open(f"{output_dir}/page_{page_count:04d}.json", "w") as f:
json.dump(chats, f)
print(f"Page {page_count}: extracted {len(chats)} chats (total: {len(all_chats)})")
next_page = data.get("next_page_id")
if not next_page:
break
payload["page_id"] = next_page
time.sleep(0.6) # Respect rate limits
return all_chatsStep 2: Transform Data
Convert each LiveChat chat into a Help Scout conversation payload. The key structural difference: a LiveChat chat has chat.threads [] (sessions), each containing thread.events [] (messages). All events across all threads must be flattened into a single ordered list of Help Scout threads.
def transform_chat(chat, agent_map, mailbox_id):
"""Transform a LiveChat chat into Help Scout conversation + threads.
Args:
chat: Raw LiveChat chat object
agent_map: Dict of agent_email -> Help Scout User ID (integer)
mailbox_id: Target Help Scout Mailbox ID (integer)
"""
customer_email = None
customer_name = ""
for user in chat.get("users", []):
if user.get("type") == "customer":
customer_email = user.get("email")
customer_name = user.get("name", "")
break
if not customer_email:
return None # Handle anonymous chats separately (see Edge Cases)
name_parts = customer_name.split(" ", 1)
first_name = name_parts[0] if name_parts else "Unknown"
last_name = name_parts[1] if len(name_parts) > 1 else ""
# Flatten all events across all LiveChat threads into Help Scout threads
hs_threads = []
for lc_thread in chat.get("threads", []):
for event in lc_thread.get("events", []):
if event.get("type") == "message":
author_id = event.get("author_id", "")
is_customer = (author_id == customer_email or
author_id in [u["id"] for u in chat.get("users", [])
if u.get("type") == "customer"])
hs_thread = {
"type": "customer" if is_customer else "reply",
"text": event.get("text", ""),
"imported": True,
"createdAt": event.get("created_at")
}
if is_customer:
hs_thread["customer"] = {"email": customer_email}
else:
hs_user_id = agent_map.get(author_id)
if hs_user_id:
hs_thread["user"] = hs_user_id
hs_threads.append(hs_thread)
elif event.get("type") == "system_message":
hs_threads.append({
"type": "note",
"text": event.get("text", ""),
"imported": True,
"createdAt": event.get("created_at")
})
elif event.get("type") == "filled_form":
form_text = "\n".join(
f"{f.get('label', 'Field')}: {f.get('answer', 'N/A')}"
for f in event.get("fields", [])
)
hs_threads.append({
"type": "note",
"text": f"Pre-chat form data:\n{form_text}",
"imported": True,
"createdAt": event.get("created_at")
})
if not hs_threads:
return None
# Enforce 100-thread limit — split into multiple conversations if needed
conversation_batches = []
for i in range(0, len(hs_threads), 100):
batch = hs_threads[i:i + 100]
part_num = (i // 100) + 1
total_parts = (len(hs_threads) + 99) // 100
subject = f"LiveChat: {customer_name} - {chat['id'][:8]}"
if total_parts > 1:
subject += f" (Part {part_num}/{total_parts})"
# Determine assignee from first agent found
assignee_id = None
for user in chat.get("users", []):
if user.get("type") == "agent":
assignee_id = agent_map.get(user.get("email"))
if assignee_id:
break
# Build tags — include rating if present
tags = [t.lower().replace(" ", "-") for t in chat.get("tags", [])]
tags.append(f"livechat-{chat['id'][:8]}")
rating = (chat.get("properties", {})
.get("rating", {})
.get("score", {})
.get("value"))
if rating:
tags.append(f"rating:{rating}")
if total_parts > 1:
tags.append(f"split-chat-{chat['id'][:8]}")
conversation = {
"subject": subject,
"customer": {
"email": customer_email,
"firstName": first_name,
"lastName": last_name
},
"mailboxId": mailbox_id,
"type": "chat",
"status": "closed",
"createdAt": batch[0].get("createdAt"),
"threads": [batch[0]],
"tags": tags,
"imported": True
}
if assignee_id:
conversation["assignTo"] = assignee_id
conversation_batches.append({
"conversation": conversation,
"additional_threads": batch[1:],
"source_chat_id": chat["id"]
})
return conversation_batchesStep 3: Handle Attachments
LiveChat file events contain CDN URLs. Help Scout requires attachments as Base64-encoded strings within the thread payload, with a 10 MB per-file limit.
Download every file referenced in file events during extraction — not during loading. After LiveChat account cancellation, CDN-hosted files may become permanently inaccessible. Store downloaded files locally and encode them during the transform phase.
import base64
def download_attachment(file_url, output_dir="./attachments"):
"""Download file from LiveChat CDN immediately during extraction."""
os.makedirs(output_dir, exist_ok=True)
filename = file_url.split("/")[-1].split("?")[0]
filepath = os.path.join(output_dir, filename)
resp = requests.get(file_url, timeout=30)
if resp.status_code == 200:
file_size = len(resp.content)
if file_size > 10 * 1024 * 1024: # 10 MB limit
return {"error": "exceeds_10mb", "url": file_url, "size": file_size}
with open(filepath, "wb") as f:
f.write(resp.content)
return {
"fileName": filename,
"mimeType": resp.headers.get("Content-Type", "application/octet-stream"),
"data": base64.b64encode(resp.content).decode("utf-8"),
"size": file_size
}
return {"error": f"download_failed_{resp.status_code}", "url": file_url}Step 4: Load into Help Scout
Create conversations with the first thread included, then append remaining threads individually. The critical requirement: set imported: true on every conversation and thread to prevent Help Scout from reopening closed conversations, firing workflows, or sending customer notifications. (developer.helpscout.com)
import time
HS_TOKEN = "your_oauth_token"
HS_API = "https://api.helpscout.net/v2"
# Idempotency tracking: source chat ID → Help Scout conversation ID
migration_map = {}
def load_conversation(payload, max_retries=3):
"""Create a conversation and append threads with rate limit handling."""
source_id = payload.get("source_chat_id")
# Idempotency check
if source_id in migration_map:
print(f"Skipping {source_id} — already migrated as {migration_map[source_id]}")
return migration_map[source_id]
headers = {
"Authorization": f"Bearer {HS_TOKEN}",
"Content-Type": "application/json"
}
for attempt in range(max_retries):
resp = requests.post(
f"{HS_API}/conversations",
json=payload["conversation"],
headers=headers
)
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 10))
print(f"Rate limited on create. Waiting {wait}s...")
time.sleep(wait)
continue
elif resp.status_code == 201:
break
elif resp.status_code in (400, 422):
print(f"Validation error for {source_id}: {resp.status_code} {resp.text}")
return None
elif resp.status_code >= 500:
time.sleep(2 ** attempt)
continue
else:
print(f"Unexpected error for {source_id}: {resp.status_code} {resp.text}")
return None
else:
print(f"Failed to create conversation for {source_id} after {max_retries} retries")
return None
conv_id = resp.headers.get("Resource-ID")
migration_map[source_id] = conv_id
# Add remaining threads
for idx, thread in enumerate(payload["additional_threads"]):
thread_type = thread.get("type", "note")
if thread_type == "customer":
endpoint = f"{HS_API}/conversations/{conv_id}/chats"
elif thread_type == "reply":
endpoint = f"{HS_API}/conversations/{conv_id}/reply"
else:
endpoint = f"{HS_API}/conversations/{conv_id}/notes"
for attempt in range(max_retries):
thread_resp = requests.post(endpoint, json=thread, headers=headers)
if thread_resp.status_code == 429:
wait = int(thread_resp.headers.get("Retry-After", 10))
time.sleep(wait)
continue
elif thread_resp.status_code == 412:
print(f"Thread limit hit on {conv_id} at thread {idx}. "
f"Source: {source_id}")
break # Stop adding threads to this conversation
elif thread_resp.status_code in (201, 200):
break
elif thread_resp.status_code >= 500:
time.sleep(2 ** attempt)
continue
else:
print(f"Thread error on {conv_id}: {thread_resp.status_code} "
f"{thread_resp.text}")
break
time.sleep(0.45) # Stay within 12 writes / 5 seconds burst limit
return conv_idFor reruns, persist the migration_map dictionary to disk (JSON or database) after each conversation. Log attachment count and batch status for every run.
Step 5: Validate
After loading, run validation checks:
- Record count: Compare total chats extracted vs. conversations created in Help Scout
- Thread count: Spot-check 20–50 conversations to confirm all messages imported
- Customer matching: Verify customer records are deduplicated by email
- Tag integrity: Export Help Scout tags and compare against source
- Attachment spot-check: Open 10 conversations with files and confirm downloads work
- Timestamp accuracy: Verify
createdAtdates match source data (timezone offsets are a common source of drift)
When auditing migrated chat threads, do not rely on embed=threads from the conversation list endpoint — Help Scout truncates chat threads there. Use the individual thread endpoint (GET /v2/conversations/{id}/threads) for full verification. (developer.helpscout.com)
Edge Cases and Challenges
Anonymous Visitors (No Email)
LiveChat allows chats without a pre-chat form, meaning many visitors have no email address. Help Scout requires an email to create a conversation. Options:
- Skip anonymous chats. If they're low-value, exclude them from migration. Log the count and chat IDs in your exceptions file.
- Generate placeholder emails. Use a pattern like
anonymous+{visitor_id}@yourdomain.com. This preserves the conversation but creates phantom customer records. - Consolidate into notes. Import anonymous transcripts as notes on a catch-all conversation per day or per agent.
Chats with 100+ Messages
Help Scout enforces a hard limit of 100 threads per conversation. For long chats:
- Split into multiple conversations (e.g., "Chat Part 1", "Chat Part 2") and link them with a shared tag like
split-chat-{id}. The transform code above handles this automatically. - Concatenate messages into fewer, larger threads. This loses per-message timestamps but preserves content within the 100-thread limit.
File Attachments and CDN Expiration
LiveChat hosts uploaded files on its CDN. After account cancellation, these URLs become inaccessible. During extraction:
- Download every file referenced in
fileevents immediately — do not defer to the load phase - Base64-encode files for Help Scout's attachment API
- Respect Help Scout's 10 MB per-file limit; log oversized files in an exception report with the original URL
- Store files locally as a permanent backup regardless of migration outcome
Pre-Chat and Post-Chat Survey Data
LiveChat captures form data as filled_form events. Help Scout has no direct equivalent for form submissions. Options:
- Map specific fields to Help Scout Custom Fields (max 10 per inbox, Plus plan required)
- Store form data as a structured note on the conversation (recommended for most cases)
- Map to Help Scout Customer Properties for data describing the customer (e.g., company name, plan type — max 50 globally)
Chat Ratings
LiveChat stores ratings as chat properties (rating.score and rating.comment). Help Scout's satisfaction ratings are generated natively and cannot be imported via API. Store ratings as tags (e.g., rating:good, rating:bad) or append both score and comment to a note on the conversation.
Rich Messages and Custom Events
LiveChat exposes rich messages (cards, buttons, quick replies), custom events, and system messages that have no direct Help Scout counterpart. Flatten these into text or HTML, preserve the original event type in a note prefix (e.g., [Rich Message - Card]), and store any structured data as formatted text. (platform.text.com)
Duplicate Customers
If the same customer chatted multiple times with slightly different name spellings, LiveChat may have multiple visitor records. Help Scout deduplicates on email address, so matching on email is safe. Normalize names during the transform step (consistent casing, trim whitespace) to avoid inconsistencies carrying over.
High-Volume Customer Records
A single customer can hit a 5,000-conversation modification lock in Help Scout — the API will reject updates to conversations belonging to that customer while the lock is active. Watch for high-volume shared addresses like support@, info@, or marketplace relay emails that accumulate large conversation counts during import. Mitigation: batch conversations for high-volume customers with delays between batches, or import them last. (developer.helpscout.com)
Multi-Agent Chats
If a LiveChat conversation was transferred between multiple agents, each agent's messages appear as separate events with different author_id values. Map each event's author_id to the correct Help Scout User ID using your agent lookup map. Help Scout allows different user values on individual threads within the same conversation — this is the correct way to represent transfers.
HTML in Messages
LiveChat sometimes includes HTML markup in messages and system messages. Sanitize content in your transformation layer — strip potentially dangerous tags while preserving basic formatting (<b>, <i>, <a>, <br>, <p>) — to prevent broken rendering in the Help Scout UI.
Help Scout Limitations to Plan For
These constraints affect every LiveChat migration:
| Constraint | Limit | Impact |
|---|---|---|
| Max threads per conversation | 100 | Long chats must be split or consolidated; API returns HTTP 412 at thread 101 |
| Max custom fields per inbox | 10 | LiveChat's unlimited custom properties must be triaged; use customer/company properties for overflow |
| Max customer/company properties (global) | 50 | Shared across all inboxes |
| Custom fields are inbox-specific | — | Moving a conversation between mailboxes drops custom field data — define inboxes before import |
| No API-based user creation | — | All agents must be invited via UI (or SCIM on Pro) before migration |
| Write requests toward rate limit | Count as 2x | 200 write calls = 400 against the 400/min limit |
| Burst write limit | 12 writes / 5 seconds | Must throttle even within the per-minute budget |
| API access minimum plan | Standard | Free plan has no API access |
| Custom fields minimum plan | Plus | Budget for the right plan tier |
| Native LiveChat import tool | None | All migrations are API-based, importer-assisted, or service-managed |
| Customer conversation lock | 5,000 conversations | High-volume customers may block modification during import |
| Max attachment size | 10 MB per file | Oversized files need external hosting or exception handling |
Validation and Testing
Pre-Migration Test Run
Always run a test migration first. Pick a sample of 50–100 chats that include:
- A chat with many messages (near the 100-thread limit)
- A chat with file attachments
- A chat with pre-chat survey data
- A chat from an anonymous visitor
- A chat with a customer who appears in multiple chats
- A chat that was transferred between agents
- A chat with rich messages or custom events
Import these into a test Help Scout mailbox. Have a support lead review them in the Help Scout UI — verify timestamps, agent assignments, customer matching, and attachment accessibility — before proceeding with the full migration.
Post-Migration Validation Checklist
- Total record count matches (source vs. destination, allowing for intentionally skipped records)
- 5% random sample passes field-level comparison (timestamps, tags, assignees)
- All agents are correctly mapped (no conversations assigned to wrong users)
- Attachments are downloadable from Help Scout
- Custom fields are populated on conversations that had pre-chat data
- Customer records are not duplicated beyond expected levels
- No conversations are in
activestatus that should beclosed - Timestamps are accurate (no timezone offset errors)
- Split conversations are linked by shared tags
For a detailed post-migration testing framework, see the Help Desk Data Migration QA Checklist.
Rollback Planning
Help Scout's Delete Conversation API endpoint (DELETE /v2/conversations/{id}) allows cleanup if a migration run goes wrong. Bulk deletion is rate-limited like any other write operation (counts 2x).
For large failed migrations:
- Tag all migrated conversations with a unique run identifier (e.g.,
migration-run-20260715) - If rollback is needed, filter by tag and delete in batches
- Customer records created during a failed migration cannot be bulk-deleted via API. Help Scout provides no bulk customer delete endpoint. Options: manually delete via UI, or leave orphaned customer records and merge them post-cleanup. Plan for this before starting — if your migration creates 10,000+ customer records, a failed run creates a messy customer database that requires significant manual cleanup or a support request to Help Scout.
- Re-run the migration from your staged JSON files using the idempotency map to avoid duplicates
Post-Migration Tasks
Rebuild Automations
Nothing from LiveChat's automation layer transfers to Help Scout:
| LiveChat Feature | Help Scout Equivalent | Rebuild Method |
|---|---|---|
| Auto-greetings | Messages (Beacon) | Configure in Beacon settings; limited to basic triggers |
| Routing rules | Workflows | Recreate in Help Scout Workflows (Plus plan required) |
| Canned responses | Saved Replies | Export text from LiveChat, recreate manually in Help Scout |
| Chat surveys (pre/post) | Beacon forms | Reconfigure in Beacon settings |
| Chat transfer rules | Assignment rules | Rebuild using Workflows or manual process |
| ChatBot flows | — | No native equivalent; evaluate third-party integrations |
Delta Migration
If you ran a phased migration, execute a final delta sync to capture any chats that occurred in LiveChat during the execution window. Use the list_archives endpoint with filters.from and filters.to date parameters to pull only new records.
Agent Onboarding
Help Scout's interface is fundamentally different from LiveChat's. Plan for:
- Keyboard shortcut training (Help Scout is keyboard-driven)
- Workflow and Saved Reply setup walkthroughs
- Beacon configuration for live chat functionality
- Collision detection awareness (Help Scout shows when another agent is viewing a conversation)
- Understanding the difference between conversation statuses (Active, Pending, Closed) vs. LiveChat's simpler open/closed model
For broader onboarding guidance, see the Help Desk Data Migration Training Plan.
Monitor for 30 Days
After cutover, watch for:
- Conversations appearing in the wrong mailbox
- Customer records that should have been merged
- Missing attachments surfacing as broken links
- Workflows firing on imported conversations (the
imported: trueflag should prevent this, but verify) - Custom field data missing on conversations moved between mailboxes
Decommission LiveChat
Do not cancel LiveChat immediately after migration:
- Revoke API tokens used for migration
- If you have compliance retention requirements, downgrade the account rather than deleting it
- Verify all CDN-hosted file attachments have been downloaded to local storage — they become inaccessible after cancellation
- Export any remaining reports or analytics data you need
- Allow a 30-day overlap period before final cancellation to catch any migration gaps
Best Practices Summary
- Always run a test migration with a representative sample (50–100 chats covering all edge cases) before committing to a full run
- Back up LiveChat data by extracting to local JSON files before canceling your account — CDN files disappear after cancellation
- Set
imported: trueon every conversation and thread to prevent reopening, workflow triggers, and customer notifications - Pre-create all Help Scout users before running the migration script — the API cannot create users; use
GET /v2/usersto build your ID lookup map - Normalize customer data during the transform phase — fix name casing, deduplicate emails, remove test accounts
- Tag every migrated conversation with a run identifier for easy filtering and potential rollback
- Plan for the 100-thread limit before you hit HTTP 412 at runtime — identify oversized chats during the audit phase
- Don't migrate canned responses via API — it's faster to recreate 20 saved replies manually than to build an importer for them
- Keep an exceptions file for records you intentionally did not migrate, so the team knows what was left behind and why
- Persist your idempotency map (source ID → destination ID) to disk after every conversation — this enables safe restarts without duplicates
- Download all attachments during extraction — not during loading — to avoid CDN expiration issues
When to Use a Managed Migration Service
Build in-house if you have a developer with 2–4 weeks of availability, fewer than 20,000 chats, and simple data (no custom properties, few attachments, most customers have email addresses).
Use a managed service if any of these apply:
- Volume exceeds 50,000 chats — rate limit management and error recovery at scale requires stateful architecture with checkpoint/resume
- You can't afford extended downtime — a migration service can run delta syncs to minimize the cutover window
- Your data has significant edge cases — high percentage of anonymous visitors, rich messages, multi-language content, or heavy attachment use
- No engineering bandwidth — your developers are shipping product features
- Compliance requirements — you need a documented audit trail and guaranteed data integrity
- Failed migration cleanup is too costly — customer records created during a failed run can't be bulk-deleted via API
API rate limits, undocumented vendor constraints, and data mapping edge cases routinely stretch initial estimates. If a script fails halfway through, identifying the delta and resuming without creating duplicate records requires a stateful architecture that most teams don't build for single use.
At ClonePartner, we handle LiveChat to Help Scout migrations with full transcript fidelity, customer context preservation, and edge case handling — including rate limit management, anonymous visitor resolution, attachment backup, and the 100-thread split logic. Typical timeline: 3–7 business days from kickoff to cutover for datasets under 100,000 chats.
For migration planning resources, see the Help Desk Data Migration Playbook and the Help Scout Migration Checklist.
Frequently Asked Questions
- Can I migrate LiveChat data to Help Scout using CSV only?
- Not with full fidelity. LiveChat's CSV export includes metadata only — not message bodies or full transcripts. Help Scout has no native CSV import for conversations. For full transcript migration, use Help Scout's in-product importer (Import2), the Mailbox API v2, or a managed migration service.
- How do I prevent Help Scout from sending email notifications during migration?
- Set imported: true on every conversation and thread you create via the API. This flag prevents Help Scout from reopening closed conversations, firing workflows, and sending customer notifications.
- What data can't be migrated from LiveChat to Help Scout?
- Visitor monitoring data, real-time analytics, goal tracking, auto-greetings, routing rules, and canned responses cannot be migrated via API. Canned responses must be rebuilt as Help Scout Saved Replies manually. Automations and workflows must be recreated from scratch.
- How long does a LiveChat to Help Scout migration take?
- A 10,000-chat migration takes roughly 9–10 hours of API execution time due to Help Scout's write rate limits (writes count 2x, with a burst cap of 12 per 5 seconds). Including planning, testing, and validation, expect 3–7 business days end-to-end.
- Does Help Scout have a limit on how many messages a conversation can have?
- Yes. Help Scout limits each conversation to 100 threads (messages). If a LiveChat chat has more than 100 messages, you must split it into multiple Help Scout conversations or concatenate messages to stay under the limit.

