Skip to content

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.

Roopi Roopi · · 26 min read
LiveChat to Help Scout Migration: The Technical Guide
TALK TO AN ENGINEER

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

Info

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:

  1. 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.
  2. 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.
  3. 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
Warning

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:

  1. Migrate via API: Chat transcripts, customer records, tags, custom field values, attachments
  2. Rebuild manually: Canned responses → Saved Replies, Auto-greetings → Help Scout Messages, Routing rules → Help Scout Workflows
  3. 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.
Warning

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 falseclosed, trueactive

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
Info

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_chats

Step 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_batches

Step 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_id

For 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 createdAt dates 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:

  1. Skip anonymous chats. If they're low-value, exclude them from migration. Log the count and chat IDs in your exceptions file.
  2. Generate placeholder emails. Use a pattern like anonymous+{visitor_id}@yourdomain.com. This preserves the conversation but creates phantom customer records.
  3. 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:

  1. Download every file referenced in file events immediately — do not defer to the load phase
  2. Base64-encode files for Help Scout's attachment API
  3. Respect Help Scout's 10 MB per-file limit; log oversized files in an exception report with the original URL
  4. 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

  1. Total record count matches (source vs. destination, allowing for intentionally skipped records)
  2. 5% random sample passes field-level comparison (timestamps, tags, assignees)
  3. All agents are correctly mapped (no conversations assigned to wrong users)
  4. Attachments are downloadable from Help Scout
  5. Custom fields are populated on conversations that had pre-chat data
  6. Customer records are not duplicated beyond expected levels
  7. No conversations are in active status that should be closed
  8. Timestamps are accurate (no timezone offset errors)
  9. 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:

  1. Tag all migrated conversations with a unique run identifier (e.g., migration-run-20260715)
  2. If rollback is needed, filter by tag and delete in batches
  3. 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.
  4. 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: true flag should prevent this, but verify)
  • Custom field data missing on conversations moved between mailboxes

Decommission LiveChat

Do not cancel LiveChat immediately after migration:

  1. Revoke API tokens used for migration
  2. If you have compliance retention requirements, downgrade the account rather than deleting it
  3. Verify all CDN-hosted file attachments have been downloaded to local storage — they become inaccessible after cancellation
  4. Export any remaining reports or analytics data you need
  5. 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: true on 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/users to 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.

More from our Blog

Help Scout Migration Checklist
Checklist/Help Scout

Help Scout Migration Checklist

Planning a Help Scout migration? Use our step-by-step checklist to map ticket history, configure mailboxes, and protect your data for a seamless, error-free transition.

Tejas Mondeeri Tejas Mondeeri · · 8 min read
Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration
Help Desk

Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration

Ensure your help desk migration is a success with this comprehensive 20-point post-migration QA checklist. This expert guide details the 20 essential tests needed to validate your data integrity, system functionality, user-friendliness, and performance . Learn exactly how to check everything from ticket data, attachments, and knowledge base articles to critical workflows, automations, and integrations before you go live. This process is your final line of defense against lost tickets, broken workflows, and unhappy customers.

Raaj Raaj · · 8 min read