LiveAgent to Intercom Migration: The Technical Guide
Technical guide to migrating from LiveAgent to Intercom — covering API constraints, data model mapping, object relationships, rate limits, and validation.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
LiveAgent to Intercom Migration: The Technical Guide
Migrating from LiveAgent to Intercom means moving from a traditional multi-channel helpdesk built around email-centric ticketing into a conversation-first platform with a fundamentally different data model. LiveAgent treats every interaction — email, chat, call, social message — as a ticket with messages attached. Intercom separates conversations (back-and-forth messaging) from tickets (trackable requests with states and SLAs), and centers everything around a Contact timeline.
This architectural mismatch means a simple CSV export will not work for anything beyond basic contact records. Preserving historical context, attachment integrity, and multi-relational links (Contact → Company → Conversation → Teammate) requires a programmatic, API-led approach.
If you want the short version: LiveAgent's ticket-centric architecture does not map 1:1 to Intercom's conversation and contact model. The hard parts are custom fields, attachments, multi-message thread reconstruction, and LiveAgent's 180-request-per-minute API rate limit on the extraction side. (support.liveagent.com)
Why Companies Migrate from LiveAgent to Intercom
Based on patterns across hundreds of migrations, here are the concrete reasons teams make this move:
- Conversational support model: Intercom's messenger-first architecture supports in-app messaging, proactive outreach, and AI-powered resolution via Fin — which can autonomously resolve up to 50% of support volume according to Intercom's published benchmarks. LiveAgent has no native equivalent to embedded in-app messaging or AI resolution agents.
- Product-led growth alignment: Teams building SaaS or subscription products need Intercom's behavioral event tracking, product tours, and event-driven messaging tied directly to their app. LiveAgent lacks native user-event ingestion for triggering contextual messages.
- Consolidation: Companies already using Intercom for marketing or sales want support on the same platform. Running LiveAgent alongside Intercom means paying for two platforms, maintaining two integrations, and splitting customer context across systems.
- Automation sophistication: Intercom's visual workflow builder supports branching logic, conditional paths, API webhooks, and Fin AI agent handoffs. LiveAgent's automation is limited to flat trigger-action rules without visual orchestration or AI components.
Core Architecture Differences
The architectural gap matters more than feature checklists. If you skip this model shift, you end up with clean imports and unusable data.
| Concept | LiveAgent | Intercom |
|---|---|---|
| Primary object | Ticket (email-centric) | Conversation / Ticket (two distinct objects) |
| Customer record | Contact + Company (1:1 relationship) | Contact (User or Lead) + Company (many-to-many) |
| Agent grouping | Departments | Teams |
| Custom fields | Contact fields, Ticket fields | Custom Data Attributes (CDAs) — 250 active limit, 255-char text cap, 35-option dropdown cap |
| Knowledge base | KB Articles + Categories | Help Center Articles + Collections |
| Automation | Rules (trigger-based, flat) | Workflows (visual builder with branching) |
| Chat history | Stored as ticket messages | Conversation parts (500 max per conversation) |
| API architecture | REST v3 (key-based auth, 180 req/min) | REST v2.11+ (OAuth / token, 10K req/min) |
| Call recordings | Native VoIP with stored recordings | No native call recording object |
LiveAgent exposes channel-rich ticket history and lets each contact belong to one company. Intercom splits work between conversations and tickets, makes companies visible only after a contact is associated, and uses a simpler operational schema. (support.liveagent.com)
Migration Approaches: Which Method Fits Your Scenario
1. Native CSV Export + Manual Import
How it works: Export contacts and ticket lists from LiveAgent's UI as CSV files. Transform the data in a spreadsheet or script, then use Intercom's API to import contacts. Intercom does not have a CSV import UI for conversations — you still need the API on the import side. (intercom.com)
When to use it: Small datasets under ~5,000 contacts with minimal ticket history you need to preserve.
Pros:
- No code required for the export step
- Good for contact-only migrations
Cons:
- LiveAgent's CSV export captures ticket metadata but not full message threads with attachments (support.liveagent.com)
- Intercom has no native CSV import for conversations
- No relationship preservation (contact-to-company links, ticket-to-contact associations)
- Intercom's CSV importer has a 20 MB limit and can create duplicates if
user_idusage is inconsistent - Company CSV import is not supported in Intercom
Complexity: Low (export) / Medium (import) · Scalability: Poor beyond a few thousand records
2. API-to-API Migration (Custom Script)
How it works: Write a script that reads from LiveAgent's REST API v3 and writes to Intercom's REST API. Extract tickets, contacts, companies, and tags; transform the data structure; load into Intercom in dependency order.
When to use it: Mid-size to large migrations where you need full message history, attachments, and relationship preservation.
Pros:
- Full control over data transformation
- Can preserve message threads, timestamps, and agent attribution
- Supports delta runs for catching records created during migration
Cons:
- LiveAgent's cloud API is rate-limited to 180 requests per minute per API key, and GET
/ticketsis capped at 10,000 results per filtered request — extracting 100K tickets at this rate takes approximately 9.3 hours of pure API time, not counting message extraction (support.liveagent.com) - Intercom has no bulk create endpoint for contacts — each contact requires an individual API call
- Requires solid error handling, retry logic with exponential backoff, and state management with checkpointing
- Typical engineering effort: 2–4 weeks for one developer, including testing and validation
Complexity: High · Scalability: Good with proper batching and rate-limit management
3. Third-Party Migration Tools
How it works: Services like Help Desk Migration offer automated wizards that connect to both platforms and handle mapping. Current Intercom first-party docs describe people CSV imports, a Zendesk import, and scripted historical migrations — not a LiveAgent-specific one-click importer. (intercom.com)
When to use it: When you want a faster turnaround without writing custom code and your data model is relatively standard.
Pros:
- Pre-built connectors for both platforms
- Visual mapping interface
- Delta migration support
Cons:
- Limited customization for complex field transformations
- May not handle all edge cases (inline images, custom objects, call recordings)
- Per-record pricing can get expensive at scale (typically $2–$10 per 1,000 records depending on provider and complexity tier)
- Many tools are fine at record creation and weak at replaying thread history with correct chronological ordering
Complexity: Low to Medium · Scalability: Moderate
4. Custom ETL Pipeline
How it works: Build an extract-transform-load pipeline using Python scripts, Node.js workers, or data pipeline frameworks (Airflow, Dagster, Prefect). Extract from LiveAgent API (or database dump), transform in a staging layer (PostgreSQL, SQLite, or flat JSON files), load via Intercom API. LiveAgent can provide a database dump to paying customers leaving the platform, but the dump is raw MySQL tables and LiveAgent still recommends the API for structured extraction. (support.liveagent.com)
When to use it: Enterprise migrations with complex data transformations, multiple data sources, or regulatory requirements (GDPR, SOC 2).
Pros:
- Maximum flexibility for data transformation
- Can incorporate data from multiple systems
- Staging layer allows validation, PII auditing, and data quality checks before loading
- Strongest rollback and observability story
- Supports encryption at rest in the staging layer for compliance
Cons:
- Highest engineering effort (typically 3–6 weeks for one developer)
- Requires infrastructure for orchestration, logging, and monitoring
Complexity: High · Scalability: Excellent
5. Middleware Platforms (Zapier, Make)
How it works: Use integration platforms to connect LiveAgent triggers/actions with Intercom actions. Zapier's current Intercom app exposes actions like Create Conversation, Reply to Conversation, Create Ticket, and Create/Update User. (zapier.com)
When to use it: Real-time, forward-looking sync between both platforms during a transition period — not for bulk historical migration.
Pros:
- No-code setup
- Good for event-driven sync (new ticket → create conversation)
Cons:
- Not designed for bulk historical data migration
- Per-task pricing makes large volumes expensive (Zapier charges per task; 300K tasks at Professional tier costs ~$600/month)
- Limited error handling and no rollback capability
- Cannot reconstruct multi-message ticket threads in chronological order
Complexity: Low · Scalability: Poor for migration; acceptable for ongoing sync
Approach Comparison
| Method | Complexity | Best For | Full History | Relationships | Cost |
|---|---|---|---|---|---|
| CSV Export | Low/Med | <5K contacts, no tickets | Partial | No | Free |
| API-to-API Script | High | Full migrations with history | Yes | Yes | Dev time (2–4 weeks) |
| Third-Party Tools | Low/Med | Standard migrations | Yes | Partial | Per-record ($2–10/1K) |
| Custom ETL | High | Enterprise, multi-source | Yes | Yes | Dev time (3–6 weeks) + infrastructure |
| Middleware (Zapier) | Low | Ongoing sync only | No | No | Per-task (~$600/mo at scale) |
Recommendations by Scenario
- Small business (<5K tickets, <2K contacts): Third-party migration tool or managed service. The engineering effort of a custom script is not justified at this scale.
- Mid-market (5K–100K tickets): API-to-API script or managed migration service. You need full thread preservation and custom field mapping. Expect 1–3 days of pure extraction time from LiveAgent at 180 req/min.
- Enterprise (100K+ tickets): Custom ETL pipeline or managed service with staging validation. LiveAgent's 180 req/min rate limit and 10,000-ticket filter cap mean extraction alone for 500K tickets can take 4–7 days. Factor in GDPR/data residency requirements for the staging layer.
- Ongoing sync during transition: Middleware platform for new records, with a separate bulk migration for historical data.
When to Use a Managed Migration Service
Building a LiveAgent-to-Intercom migration script sounds straightforward until you hit the edge cases. Here is when the engineering cost of DIY exceeds the cost of a managed service:
- Your ticket volume exceeds 50K records and you cannot afford a multi-day extraction window with retry logic and checkpointing
- You have complex custom fields that require transformation (LiveAgent picklists → Intercom CDAs with different value formats and a 35-option limit per dropdown)
- Attachments and inline images need to be downloaded from LiveAgent's authenticated URLs, re-hosted on publicly accessible storage, and re-linked in Intercom conversations
- Your team does not have spare engineering bandwidth — a migration script is a 2–4 week project for one developer, with testing
- You need zero downtime — running both systems in parallel during cutover requires orchestration, deduplication logic, and a defined switchover protocol
The hidden cost of DIY is not the extraction loop. It is duplicate remediation in Intercom (where email uniqueness conflicts silently merge records), relationship rebuilds, notification leakage during historical replay, and edge cases like LiveAgent's mixed note IDs after version 5.62 or Intercom's company visibility rules. Based on our experience, teams that underestimate migration complexity typically spend 40–60% more engineering hours debugging than they originally scoped. (support.liveagent.com)
At ClonePartner, we have completed 1,500+ data migrations across helpdesk, CRM, and SaaS platforms. For LiveAgent migrations, we handle the full pipeline: extraction (API or database dump), transformation (field mapping, data cleaning, relationship reconstruction), and loading into Intercom with full validation. Read more about how we run migrations at ClonePartner.
Pre-Migration Planning
A successful migration is 80% planning and 20% execution. Before touching any data, audit what you have and define what you need.
Data Audit
| LiveAgent Object | Count It | Notes |
|---|---|---|
| Tickets | Total + by status | Include resolved/archived. Note the 10K filter cap — use date windowing if total exceeds this. |
| Contacts | Total + duplicates | Check for contacts with no tickets and contacts sharing the same email across channels. |
| Companies | Total | Verify contact-company links. Orphaned companies will not surface in Intercom UI. |
| Tags | Total | Map to Intercom tags. Identify and consolidate synonymous tags. |
| Custom fields (Contact) | List all with data types | Check for fields exceeding 255 characters or picklists with >35 options. |
| Custom fields (Ticket) | List all with data types | Map to Intercom CDAs. Count total — must stay under 250 active CDAs. |
| Knowledge base articles | Total + categories | Map categories to Intercom collections. Note internal links and embedded images. |
| Attachments | Total count + total size in GB | Estimate re-hosting needs. Budget ~1 second per attachment for download/upload. |
| Agents / Departments | Total active + inactive | Map to Intercom admins/teams. Decide how to handle inactive agents. |
| Call recordings | Total count + total size | These have no Intercom destination — plan external archival. |
If the team has been using companies, tags, or ticket states as pseudo-accounts or pseudo-pipelines, document that now instead of pretending there is a clean CRM-style object model.
What to Leave Behind
- Spam tickets — filter these out before migration
- Test/internal tickets — unless they contain real customer data
- Duplicate contacts — merge before migration, not after. Intercom's deduplication behavior on import can silently merge records in unexpected ways.
- Unused custom fields — Intercom has a soft limit of 250 active CDAs; exceeding this requires contacting Intercom support for a limit increase, and performance may degrade
- Resolved tickets older than your retention policy — archive to CSV or cloud storage instead of migrating
- Call recordings — archive to S3, GCS, or Azure Blob Storage; link via custom attribute in Intercom
Data Privacy and Compliance Considerations
Migrating customer data between SaaS platforms has regulatory implications:
- GDPR (EU): Ensure both LiveAgent and Intercom Data Processing Agreements (DPAs) cover the transfer. If using a staging layer, encrypt PII at rest and in transit. LiveAgent stores data in the region selected at account creation; Intercom offers US, EU, and AU data hosting — verify alignment.
- Data residency: If your staging layer (local disk, cloud VM, database) stores customer data temporarily, it must comply with the same data residency requirements as your production systems.
- PII handling: During transformation, minimize PII exposure. Do not log email addresses, phone numbers, or message bodies in plain text to migration logs. Use hashed identifiers in log output.
- Retention: If you are migrating historical data that customers have requested be deleted under GDPR Article 17, exclude those records from the migration set.
- DPA with migration vendors: If using a third-party migration tool or managed service, ensure they have a DPA in place covering data processing during the migration.
Migration Strategy
- Big bang: Migrate everything at once over a weekend. Works for datasets under ~20K tickets. Risk: if something fails, you are in limbo with partially loaded data in Intercom and a live LiveAgent instance.
- Phased: Migrate contacts and companies first, then tickets in batches by date range or department. Lower risk, longer timeline (typically 1–2 weeks for mid-size datasets).
- Incremental: Migrate historical data first, then run a delta sync to catch records created during the migration window. This is the approach Intercom documents in its own historical migration guidance. (intercom.com)
For most teams, we recommend the phased approach with a delta sync. Migrate contacts and companies first (they are the foundation), then tickets, then knowledge base. Run a final delta sync before cutover.
The lowest-risk migration pattern: load closed history first into a non-live Intercom workspace, run UAT with 2–3 agents, then execute a final delta run for records changed after the cutoff, and switch DNS/routing to Intercom.
Data Model and Object Mapping
This is where the migration gets real. LiveAgent and Intercom have fundamentally different schemas.
Conversation vs. Ticket: The First Design Decision
Intercom has two target objects for LiveAgent tickets. You need to decide before the first load:
- Use Conversations when the source record is a customer-facing thread (email, chat, social message)
- Use Tickets when it behaves more like tracked work, a back-office workflow, or a request that progresses through defined states with SLAs
Intercom requires ticket types to exist before you can create ticket records, and ticket types are created via the Intercom admin UI (not the API), so this decision must happen during planning. (intercom.com)
Decision framework:
- If >80% of your LiveAgent tickets are customer-facing email/chat threads → default to Conversations
- If you have distinct ticket categories with defined workflows (e.g., refund requests, bug reports) → create those as Intercom Ticket Types
- If mixed, split by LiveAgent department or tag — map specific departments to Ticket Types and the rest to Conversations
Object-Level Mapping
| LiveAgent Object | Intercom Object | Notes |
|---|---|---|
| Contact | Contact (role: user) | Match on email. Keep identifier strategy consistent to avoid duplicate matching. |
| Company | Company | Create via API with a stable company_id. Companies become visible only after a contact is attached. (intercom.com) |
| Agent | Admin | Cannot be created via API — must exist in Intercom first. Map inactive agents to a generic "Legacy Agent" admin to preserve attribution without consuming a paid seat. |
| Department | Team | Create manually in Intercom before migration. |
| Ticket | Conversation or Ticket | See design decision above. Use POST /conversations for conversations, POST /tickets for tickets. |
| Ticket message | Conversation Part | Each message becomes a part. Max 500 parts per conversation. |
| Internal note | Conversation Part (note) | Use message_type: "note" for internal notes, message_type: "comment" for public replies. Handle both old and new LiveAgent note-type conventions. (support.liveagent.com) |
| Tag | Tag | Create via API, then attach to conversations/contacts. |
| KB Article | Article | Map categories to Intercom collections. See Knowledge Base Migration section. |
| Custom contact field | Custom Data Attribute | Must be created before importing data. |
| Custom ticket field | Conversation Data Attribute | Limited to supported types. List attributes expect target list item IDs, not labels. (developers.intercom.com) |
| Ticket attachment | Conversation part attachment | Must be re-uploaded; URLs are not transferable. |
Field-Level Mapping: Contacts
| LiveAgent Field | Intercom Field | Type | Notes |
|---|---|---|---|
| string | Primary identifier. Lowercase and trim whitespace before import. | ||
| firstname + lastname | name | string | Concatenate — Intercom uses a single name field |
| phone | phone | string | Direct mapping |
| company_id | company.company_id | reference | Must link after company creation |
| city | custom_attributes.city | string | No native city field in Intercom |
| custom fields | custom_attributes.* | varies | Create CDAs first |
| dateCreated | signed_up_at | timestamp | Convert to Unix epoch |
| contactid | external_id | string | Preserve for deduplication and audit |
Field-Level Mapping: Tickets → Conversations
| LiveAgent Field | Intercom Field | Type | Notes |
|---|---|---|---|
| subject | source.subject | string | Only on first message. Intercom's standard Conversations do not use subject lines — the subject is typically prepended to the first message body. Intercom's newer Tickets object does support formal titles via the title field. |
| status (New/Answered) | state (open) | enum | |
| status (Postponed) | state (snoozed) | enum | |
| status (Resolved/Deleted) | state (closed) | enum | |
| department_id | team_assignee_id | reference | Map department → team via lookup table |
| agent_id | assignee.id | reference | Agent must exist as admin. Use GET /admins to build ID lookup. |
| tags | tags | array | Attach after conversation creation |
| dateCreated | created_at | timestamp | Convert to Unix epoch. Use Intercom's historical import endpoint to set this correctly. |
| messages [] | conversation_parts [] | array | Each message = one part. Preserve chronological order. |
Field-Level Mapping: Tickets → Intercom Tickets
When using Intercom's Ticket object instead of Conversations:
| LiveAgent Field | Intercom Ticket Field | Type | Notes |
|---|---|---|---|
| subject | title | string | Direct mapping — Tickets support formal titles |
| status | ticket_state | enum | Map to: submitted, in_progress, waiting_on_customer, resolved |
| department_id | team_assignee_id | reference | Same as conversations |
| agent_id | assignee.id | reference | Same as conversations |
| ticket type | ticket_type_id | reference | Must pre-create ticket types in Intercom admin UI |
| custom ticket fields | ticket_attributes | varies | Create as ticket-level CDAs |
Handling Custom Fields
LiveAgent supports custom fields on both contacts and tickets. Intercom uses Custom Data Attributes (CDAs) on contacts, companies, and conversations.
Intercom CDAs have strict limitations: text values are capped at 255 characters, dropdown lists are limited to 35 options per attribute, and the soft limit is 250 active CDAs per workspace. Exceeding 250 requires contacting Intercom support — they can raise the limit, but query performance may degrade. If your LiveAgent instance has long text fields or picklists with many values, you will need to transform the data — use text CDAs instead of dropdowns, truncate with a suffix marker (e.g., [truncated]), or move overflow text into conversation notes. CDAs must be created in Intercom before importing data, or values will be silently discarded with no error returned.
Company attributes cannot be enforced as required-on-close in Intercom. That toggle applies only to conversation and ticket attributes, so move close-critical data onto those objects instead of company fields. (intercom.com)
Handling Relationships
LiveAgent links contacts to companies and tickets to contacts. In Intercom, this creates a strict dependency chain:
- Create companies first — you need the Intercom-generated
company_idto link contacts. - Create contacts second — attach to companies using the company ID.
- Create conversations last — you need the Intercom contact ID to associate conversations.
This order is non-negotiable. If you try to create conversations before contacts exist, the API will return a 404 or 422 error. If you try to attach a contact to a non-existent company, the attachment silently fails. (intercom.com)
Knowledge Base Migration
Knowledge base migration is often treated as an afterthought, but it has its own set of challenges.
Extraction
LiveAgent's KB articles are organized into Categories. Use the API to extract:
- Article title, content (HTML), status (published/draft), category
- Any embedded images (these are typically hosted on LiveAgent's CDN)
Transformation
| LiveAgent KB Concept | Intercom Help Center Concept | Notes |
|---|---|---|
| Category | Collection | 1:1 mapping. Intercom collections can be nested one level deep. |
| Subcategory | Section within Collection | If LiveAgent has deeper nesting, flatten to two levels. |
| Article | Article | Map to a collection. |
| Article status (published) | Article state (published) | Direct mapping. |
| Internal articles | Article state (draft) | Keep as draft; do not publish internal docs to the Help Center. |
Key Gotchas
- Embedded images: Images hosted on LiveAgent's CDN must be downloaded and re-uploaded. Use Intercom's article API to upload images, or host them on your own CDN and reference via URL.
- Internal links: Links between KB articles that reference LiveAgent URLs must be rewritten to point to the new Intercom Help Center URLs. Build a URL mapping table (old article URL → new article URL) and do a find-replace across all article bodies.
- SEO redirects: If your LiveAgent KB was public-facing, set up 301 redirects from old article URLs to new Intercom Help Center URLs. This preserves search engine ranking and prevents broken links from external sites.
- Intercom article body format: Intercom's article API accepts HTML, but only a subset of tags. Strip unsupported tags (e.g.,
<script>,<iframe>, customdata-*attributes) before import. - Article author: Intercom requires an
author_id(an admin ID) for each article. Map LiveAgent article authors to Intercom admins, or default to a single admin for all migrated articles.
Code Example
def transform_kb_article(la_article, collection_id_map, admin_id):
return {
"title": la_article["title"],
"body": rewrite_internal_links(
strip_unsupported_html(la_article["content"])
),
"author_id": admin_id,
"state": "draft", # Import as draft, review before publishing
"parent_id": collection_id_map.get(la_article["category_id"]),
"parent_type": "collection"
}Import all articles as drafts first. Review for formatting issues, broken images, and dead links before publishing.
Migration Architecture
Data Flow
LiveAgent API (v3) Staging Layer Intercom API (v2.11+)
──────────────── ────────────── ────────────────────
GET /tickets → JSON/DB store → POST /companies
GET /tickets/{id}/messages Transform & → POST /contacts
GET /customers Validate → POST /conversations
GET /tags PII audit → POST /conversations/{id}/reply
GET /companies Deduplication → POST /tags
GET /knowledgebase → POST /articles
LiveAgent API Constraints
- Rate limit: 180 requests per minute per API key for cloud-hosted accounts (support.liveagent.com)
- Effective throughput: ~3 requests per second. For ticket extraction with message fetch, expect ~2 tickets per second (1 list call + 1 message call per ticket).
- Pagination: GET
/ticketsreturns max 1,000 rows per page; use_pageand_perPageparameters - Result cap: v3
/ticketsis capped at 10,000 results per filtered request — use date-windowed batching for larger datasets (support.liveagent.com) - API versions: v1 is deprecated; use v3 for all new integrations
- Authentication: API key passed via
apikeyheader or query parameter - No bulk export endpoint: Each ticket's messages require a separate API call (
GET /tickets/{ticketId}/messages) - Timezone: API timestamps default to the account timezone unless you send a
Timezone-Offsetheader. Always normalize to UTC in your staging layer. - Database dump: Available by contacting LiveAgent support — contains raw MySQL database tables, useful for very large datasets where API extraction would take multiple days
Extraction Time Estimates
| Dataset Size | Tickets | Estimated Extraction Time (API) | Notes |
|---|---|---|---|
| Small | <5K | ~1 hour | Single-pass, no date windowing needed |
| Medium | 5K–50K | 3–8 hours | Requires date-windowed batching |
| Large | 50K–200K | 1–3 days | Consider database dump |
| Enterprise | 200K+ | 3–7 days | Database dump strongly recommended |
Estimates assume average of 5 messages per ticket and 180 req/min rate limit.
Intercom API Constraints
- Rate limit: 10,000 API calls per minute per app, 25,000 per workspace, distributed in 10-second windows — watch
X-RateLimit-RemainingandX-RateLimit-Resetresponse headers instead of hardcoding sleeps - No bulk create for contacts: Each contact must be created individually via
POST /contacts - Conversation parts limit: 500 parts maximum per conversation
- Attachment URLs expire after 30 minutes — you must download and re-upload, not just copy URLs
- CDA limits: 250 active custom data attributes (soft limit), text values max 255 characters, dropdown max 35 options
- Custom objects: 15 custom objects and 100K records on all plans, but custom objects can only be populated via Data Connectors — not directly via REST API for bulk import
- API versioning: Set
Intercom-Version: 2.11header on all requests. Older versions may return different response schemas or reject newer parameters. The version header is required. - Common error codes:
409 Conflict= duplicate contact (email/external_id already exists),422 Unprocessable Entity= validation error (missing required field or invalid CDA),404 Not Found= referenced object does not exist (e.g., contact_id for conversation)
Intercom's 10,000 req/min limit sounds generous, but creating a single conversation with 20 messages requires ~21 API calls (1 to create + 20 reply calls). A 50K-ticket migration with an average of 5 messages per ticket means ~300K API calls on the Intercom side alone — approximately 30 minutes of pure API time at maximum throughput, but realistically 1–2 hours with retries and backoff.
Step-by-Step Migration Process
Step 1: Extract Data from LiveAgent
Use the LiveAgent API v3 to extract all relevant objects. Never query all tickets since day one in one shot — use date-windowed batching to stay within the 10,000-result cap.
import requests
import time
import json
import logging
LA_BASE = "https://your-account.liveagent.com/api/v3"
API_KEY = "your-api-key"
HEADERS = {"apikey": API_KEY}
RATE_LIMIT_DELAY = 0.34 # ~180 req/min = 1 req per 0.33s
def extract_tickets(date_from, date_to):
"""Extract tickets within a date range. Use 7-day windows for large datasets."""
tickets = []
page = 1
while True:
resp = requests.get(
f"{LA_BASE}/tickets",
headers=HEADERS,
params={
"_page": page,
"_perPage": 1000,
"date_from": date_from,
"date_to": date_to
}
)
if resp.status_code == 429:
logging.warning("Rate limited on LiveAgent API, sleeping 60s")
time.sleep(60)
continue
resp.raise_for_status()
data = resp.json()
if not data:
break
tickets.extend(data)
if len(data) >= 10000:
logging.warning(f"Hit 10K cap for range {date_from}-{date_to}. Narrow the window.")
page += 1
time.sleep(RATE_LIMIT_DELAY)
return tickets
def extract_messages(ticket_id):
"""Extract all messages for a single ticket."""
resp = requests.get(
f"{LA_BASE}/tickets/{ticket_id}/messages",
headers=HEADERS
)
time.sleep(RATE_LIMIT_DELAY)
if resp.status_code == 404:
logging.warning(f"Ticket {ticket_id} messages not found — may be deleted")
return []
resp.raise_for_status()
return resp.json()Step 2: Transform Data
Map LiveAgent's structure to Intercom's expected format. Normalize statuses, flatten HTML where needed, resolve authors, collapse dead tags, and build source-to-target ID maps.
from dateutil.parser import parse as parse_datetime
def transform_contact(la_contact):
name = f"{la_contact.get('firstname', '')} {la_contact.get('lastname', '')}".strip()
return {
"role": "user",
"email": la_contact.get("email", "").lower().strip(),
"external_id": str(la_contact["contactid"]),
"name": name if name else "Unknown",
"phone": la_contact.get("phone"),
"signed_up_at": int(parse_datetime(la_contact["dateCreated"]).timestamp()),
"custom_attributes": {
"la_contact_id": str(la_contact["contactid"]),
}
}
def map_ticket_status(la_status):
"""Map LiveAgent status codes to Intercom conversation states."""
status_map = {
"N": "open", # New
"T": "open", # Answered (by agent)
"P": "snoozed", # Postponed
"R": "closed", # Resolved
"X": "closed", # Deleted
"A": "open", # Answered (by customer)
"C": "open", # Chatting
"B": "open", # Calling
}
return status_map.get(la_status, "open")Treat LiveAgent note IDs as strings — version 5.62 can return mixed integer and UUID note identifiers in the same ticket history. Cast all IDs to strings in your staging layer. (support.liveagent.com)
Step 3: Prepare Intercom
Before loading operational records, create all target structures:
- Teams (mapped from departments) — create via Intercom admin UI
- Custom Data Attributes for all custom fields you plan to migrate — create via API (
POST /data_attributes) or admin UI - Tag names — create via API (
POST /tags) - Ticket types (if using Intercom's ticket object) — create via admin UI
- Help Center collections — create via API (
POST /help_center/collections)
Agents must already exist as Intercom admins. You cannot create admins via the API. Use GET /admins to retrieve the admin list and build your LiveAgent agent ID → Intercom admin ID mapping table.
Step 4: Load into Intercom in Dependency Order
Create objects in this sequence: Companies → Contacts → Conversations/Tickets → Tags → Articles.
import requests
import time
import logging
IC_TOKEN = "your-intercom-token"
IC_BASE = "https://api.intercom.io"
IC_HEADERS = {
"Authorization": f"Bearer {IC_TOKEN}",
"Content-Type": "application/json",
"Intercom-Version": "2.11"
}
def create_contact(contact_data, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
f"{IC_BASE}/contacts",
headers=IC_HEADERS,
json=contact_data
)
if resp.status_code == 200:
return resp.json()
elif resp.status_code == 409:
# Duplicate — contact already exists, search and return existing
logging.info(f"Contact {contact_data.get('email')} already exists, retrieving")
return search_contact_by_email(contact_data["email"])
elif resp.status_code == 429:
wait = int(resp.headers.get("X-RateLimit-Reset", 10))
logging.warning(f"Rate limited, waiting {wait}s (attempt {attempt+1})")
time.sleep(wait)
elif resp.status_code == 422:
logging.error(f"Validation error for contact: {resp.json()}")
return None # Send to dead-letter queue
else:
logging.error(f"Unexpected error {resp.status_code}: {resp.text}")
time.sleep(2 ** attempt) # Exponential backoff
return None
def create_conversation(contact_id, first_message_body):
resp = requests.post(
f"{IC_BASE}/conversations",
headers=IC_HEADERS,
json={
"from": {"type": "user", "id": contact_id},
"body": first_message_body
}
)
return resp.json()
def add_reply(conversation_id, admin_id, body, message_type="comment"):
resp = requests.post(
f"{IC_BASE}/conversations/{conversation_id}/reply",
headers=IC_HEADERS,
json={
"message_type": message_type,
"type": "admin",
"admin_id": admin_id,
"body": body
}
)
return resp.json()Use Intercom's external_id field to enforce uniqueness. If a script fails mid-run, you should be able to restart without creating duplicates. The 409 Conflict response is your signal that a record already exists — handle it by retrieving the existing record and continuing.
Step 5: Rebuild Relationships
- Attach contacts to companies using
POST /contacts/{id}/companies - Assign conversations to the correct team/admin using
PUT /conversations/{id} - Apply tags using
POST /conversations/{id}/tags - Set conversation state (open/closed/snoozed) using
PUT /conversations/{id}— note that closing a conversation may trigger Intercom workflows, so suppress automations during migration
Step 6: Validate and Run Delta Sync
Compare record counts, run field-level spot checks, execute agent UAT, then migrate any records created in LiveAgent during the migration window. See the Validation section below.
Full Script Structure
Here is the high-level structure for a production migration script:
# migration.py - LiveAgent to Intercom Migration
import logging
import json
from collections import defaultdict
logging.basicConfig(level=logging.INFO, filename="migration.log")
# Dead-letter queue for records that fail after max retries
dead_letter = defaultdict(list)
# Checkpoint store — use a file or database for persistence across restarts
checkpoint = {}
class LiveAgentExtractor:
"""Handles all reads from LiveAgent API v3"""
def __init__(self, base_url, api_key): ...
def get_contacts(self, page=1, per_page=200): ...
def get_companies(self): ...
def get_tickets(self, date_from, date_to): ...
def get_ticket_messages(self, ticket_id): ...
def get_tags(self): ...
def get_kb_articles(self): ...
def get_kb_categories(self): ...
class DataTransformer:
"""Maps LiveAgent schema to Intercom schema"""
def __init__(self, agent_map, department_map, tag_map):
self.agent_map = agent_map # LA agent_id → IC admin_id
self.department_map = department_map # LA dept_id → IC team_id
self.tag_map = tag_map # LA tag_name → IC tag_id
def transform_contact(self, la_contact): ...
def transform_company(self, la_company): ...
def transform_ticket(self, la_ticket, messages): ...
def transform_kb_article(self, la_article): ...
class IntercomLoader:
"""Handles all writes to Intercom API with retry and checkpointing"""
def __init__(self, access_token, version="2.11"): ...
def create_contact(self, data): ...
def create_company(self, data): ...
def create_conversation(self, contact_id, body): ...
def add_conversation_reply(self, conv_id, admin_id, body): ...
def create_ticket(self, contact_id, ticket_type_id, title, body): ...
def apply_tag(self, object_type, object_id, tag_id): ...
def create_article(self, data): ...
def save_id_mapping(object_type, source_id, target_id):
"""Persist source→target ID mapping for rollback and validation."""
...
def get_id_mapping(object_type, source_id):
"""Retrieve Intercom ID from LiveAgent ID."""
...
def main():
extractor = LiveAgentExtractor(LA_BASE_URL, LA_API_KEY)
transformer = DataTransformer(AGENT_MAP, DEPT_MAP, TAG_MAP)
loader = IntercomLoader(IC_ACCESS_TOKEN)
# Phase 1: Companies
for company in extractor.get_companies():
if checkpoint.get(f"company_{company['id']}"):
continue # Already migrated
ic_company = transformer.transform_company(company)
result = loader.create_company(ic_company)
if result:
save_id_mapping("company", company["id"], result["id"])
checkpoint[f"company_{company['id']}"] = True
# Phase 2: Contacts
for contact in extractor.get_contacts():
if checkpoint.get(f"contact_{contact['contactid']}"):
continue
ic_contact = transformer.transform_contact(contact)
result = loader.create_contact(ic_contact)
if result:
save_id_mapping("contact", contact["contactid"], result["id"])
checkpoint[f"contact_{contact['contactid']}"] = True
else:
dead_letter["contacts"].append(contact)
# Phase 3: Tickets → Conversations
for ticket in extractor.get_tickets(DATE_FROM, DATE_TO):
if checkpoint.get(f"ticket_{ticket['id']}"):
continue
messages = extractor.get_ticket_messages(ticket["id"])
ic_conv = transformer.transform_ticket(ticket, messages)
contact_ic_id = get_id_mapping("contact", ticket["owner_contactid"])
if not contact_ic_id:
# Assign to fallback "Archived User" contact
contact_ic_id = ARCHIVED_USER_IC_ID
conv = loader.create_conversation(contact_ic_id, ic_conv["first_message"])
for msg in ic_conv["replies"]:
loader.add_conversation_reply(conv["id"], msg["admin_id"], msg["body"])
save_id_mapping("ticket", ticket["id"], conv["id"])
checkpoint[f"ticket_{ticket['id']}"] = True
# Phase 4: Knowledge Base
for article in extractor.get_kb_articles():
ic_article = transformer.transform_kb_article(article)
loader.create_article(ic_article)
# Output dead-letter records for manual review
with open("dead_letter.json", "w") as f:
json.dump(dead_letter, f, indent=2)
logging.info(f"Migration complete. {len(dead_letter['contacts'])} contacts in dead-letter queue.")
if __name__ == "__main__":
main()This is a structural outline — production code needs retry logic with exponential backoff on 429 responses, checkpointing persisted to disk or database so you can resume after failures, comprehensive error logging with hashed PII, and a dead-letter queue for records that fail repeatedly.
Edge Cases and Challenges
Duplicate Contacts
LiveAgent may have multiple contact records with the same email created from different channels. Intercom enforces email uniqueness and resolves contacts in a strict identifier order: Intercom ID → user_id → email. Inconsistent user_id strategy is the fastest way to create duplicates. Deduplicate before migration — merge contacts in LiveAgent or in your staging layer. When Intercom encounters a duplicate email, it returns 409 Conflict — your script should handle this by retrieving the existing contact and continuing. (intercom.com)
Attachments
Attachments cannot be passed via URL directly if they are behind LiveAgent's authentication wall. You must:
- Download each attachment from LiveAgent to a temporary store (local disk or S3 bucket)
- Upload to a publicly accessible URL or use Intercom's attachment upload mechanism
- Reference the new URL in the conversation part body
Intercom's attachment URLs expire after 30 minutes, so you cannot simply copy URLs between systems. Budget approximately 1 second per attachment for the download/upload cycle. For a migration with 50K attachments, this adds ~14 hours of processing time.
Conversation Parts Limit
Intercom caps conversations at 500 parts. If a LiveAgent ticket has more than 500 messages (rare, but possible in long-running support threads or automated notification threads), you need to either:
- Truncate: Keep the most recent 500 messages, archive the rest as a note on the conversation
- Split: Create multiple linked conversations with a note on each referencing the other parts
- Archive: Store the full thread externally and link via custom attribute
Missing or Deleted Users
If a LiveAgent ticket belongs to a deleted user, the Intercom API will return a 404 or 422 when you try to create the conversation. Intercept these errors and assign the conversation to a fallback "Archived User" contact — create this contact in Intercom before migration starts.
Orphan Companies
Companies with no attached contact may not appear in Intercom's UI list views or search results. They exist in the database and are accessible via API, but raw company count comparisons using the UI can mislead QA. Verify with API queries (GET /companies) against your mapping table, not just UI counts.
Call Recordings and Voicemail
LiveAgent supports VoIP and stores call recordings as ticket attachments or linked files. Intercom has no native call recording object. Archive these externally (S3, Google Cloud Storage, Azure Blob Storage) and link via a custom attribute or conversation note containing the external URL.
Formatting Loss
Initial Intercom conversation creation does not accept HTML bodies in all cases — the POST /conversations endpoint accepts a body field that supports a subset of HTML. Rich email rendering (CSS styles, complex tables, embedded fonts) is best-effort, not guaranteed fidelity. Strip incompatible HTML tags (e.g., <style>, <script>, <table> with complex nesting) to prevent API validation errors. Test with 5–10 representative HTML-heavy tickets before the full run. (developers.intercom.com)
LiveAgent Chat Transcripts
Live chat conversations in LiveAgent are stored as ticket messages and map directly to Intercom conversation parts. Real-time metadata (typing indicators, chat duration, visitor page URL, pre-chat form responses) will be lost. If pre-chat form data is important, extract it as a custom attribute or prepend it to the first conversation message.
Notification Leakage
Test in a non-live workspace or suppress outbound behavior during historical replay. Replaying email-like history carelessly can trigger customer-facing notifications (email, push, in-app) for every imported conversation. In Intercom, you can suppress notifications by using the admin reply endpoint with specific parameters, but verify this in your test workspace first. This is the single most common "catastrophic" migration error — sending thousands of notifications to customers about years-old support tickets.
Mixed Character Encodings
LiveAgent tickets from email channels may contain mixed character encodings (UTF-8, ISO-8859-1, Windows-1252). Normalize all text to UTF-8 in your transformation layer. Characters that cannot be converted should be replaced with the Unicode replacement character (U+FFFD) rather than silently dropped.
Limitations and Constraints
Be honest with stakeholders about what gets lost or changed:
| Limitation | Impact | Workaround |
|---|---|---|
| No equivalent to LiveAgent's call center | Call data cannot be migrated as-is | Archive calls to cloud storage, link via notes |
| Intercom CDAs limited to 255 chars | Long text fields get truncated | Use notes for overflow text; add [truncated] marker |
| Intercom dropdown CDAs max 35 options | Large picklists will not fit | Use text CDAs instead |
| No bulk contact import API | Import speed depends on rate limits (~10K contacts/hour) | Batch with backoff |
| Custom objects require Data Connectors | Cannot bulk-load custom objects via REST API | Use Intercom's Data Connector framework |
| Ticket SLA history not transferable | Historical SLA metrics are lost | Export SLA reports from LiveAgent before decommissioning |
| Conversation parts max 500 | Very long threads may be truncated | Split into multiple conversations |
| Timestamp control | Historical conversations appear by import date unless using historical import endpoints | Use Intercom's specific import endpoints with their schema constraints |
| Forum, suggestion, and channel artifacts | Several LiveAgent-specific objects have no clean 1:1 Intercom object | Archive externally or redesign |
| LiveAgent gamification data | Agent points, badges, levels have no Intercom equivalent | Export for records, cannot migrate |
| Predefined answers (canned responses) | LiveAgent macros do not transfer | Recreate manually as Intercom Saved Replies |
Validation and Testing
Never execute a production cutover without a sandbox test. Create a free Intercom dev workspace for testing.
Record Count Validation
After migration, compare counts for every object type:
LiveAgent Contacts: 12,456
Intercom Contacts: 12,456 ✓
LiveAgent Companies: 234
Intercom Companies: 234 ✓
(Verify via API — orphan companies may not appear in UI)
LiveAgent Tickets: 45,678
Intercom Conversations: 45,678 ✓
(Check dead-letter queue for any failed records)
LiveAgent Tags: 89
Intercom Tags: 89 ✓
LiveAgent KB Articles: 156
Intercom Articles: 156 ✓
Field-Level Validation
Sample 2–5% of records (minimum 50 records per object type) and verify:
- Contact names, emails, and phone numbers match
- Custom attribute values transferred correctly (check for truncation at 255 chars)
- Conversation messages are in correct chronological order
- Attachments are accessible (not broken links or expired URLs)
- Tags are correctly applied
- Agent attribution preserved (correct admin assigned)
- Conversation states match expected status mapping
Intercom Search and Scroll APIs for Validation
For large-scale validation, use Intercom's Search API (POST /contacts/search) and Scroll API (GET /contacts/scroll) to efficiently query imported data:
- Search API: Use filters to find records by
external_idor custom attributes. Limited to 50 results per page. Use for targeted spot checks. - Scroll API: Returns all contacts in paginated batches. Use for full count validation. Note: scroll sessions expire after 1 minute of inactivity.
def validate_contact_count():
"""Compare migrated contact count against source."""
scroll_param = None
ic_count = 0
while True:
url = f"{IC_BASE}/contacts/scroll"
if scroll_param:
url += f"?scroll_param={scroll_param}"
resp = requests.get(url, headers=IC_HEADERS)
data = resp.json()
contacts = data.get("data", [])
if not contacts:
break
ic_count += len(contacts)
scroll_param = data.get("scroll_param")
return ic_countUAT Process
- Have 2–3 agents search for known customers in Intercom and verify their history is complete
- Open 10 random conversations and confirm message threading is correct and chronological
- Verify that workflows/automations in Intercom reference the correct tags and custom attributes
- Test that new incoming conversations are routed correctly alongside migrated data
- Verify that Fin AI (if enabled) can access and reference migrated Help Center articles
- Check that conversation assignment rules work with migrated team mappings
Rollback Planning
Migrations to Intercom are not easily reversible. Intercom does not have a "delete all imported data" button. Your rollback plan should include:
- Keep LiveAgent active (read-only) for at least 30 days post-migration
- Maintain a mapping file (LiveAgent ID → Intercom ID) for every record — this is mandatory for rollback
- If rollback is needed, delete records via Intercom's API using the mapping file — tag all migrated records with a
migration_batch_idtag to make bulk identification possible - Estimated rollback time: deleting via API is rate-limited to the same 10K req/min. For a 50K-record migration, expect ~5 minutes of pure API time for deletion, plus time for relationship cleanup.
- Critical: Intercom does not cascade-delete. Deleting a contact does not delete their conversations. You must delete conversations separately.
Post-Migration Tasks
Once the data is in Intercom, the operational work begins.
Rebuild Automations
LiveAgent's rule-based automation does not transfer to Intercom. Recreate:
- Assignment rules → Intercom Workflows or Assignment Rules
- SLA rules → Intercom SLA policies (set up before go-live; historical SLA data does not migrate)
- Auto-responses → Intercom Workflows with auto-reply actions
- Tag-based routing → Intercom tag-triggered workflows
- Escalation rules → Intercom Workflows with time-based conditions
Agent Onboarding
Intercom's interface is significantly different from LiveAgent. Budget 2–5 days for:
- Training agents on the Inbox, conversation handling, and the difference between conversations and tickets
- Recreating macros/saved replies (these do not migrate — export from LiveAgent and manually recreate)
- Configuring notification preferences per agent
- If using Fin AI, pointing it at your newly migrated Help Center articles and verifying it can surface correct answers
- Setting up keyboard shortcuts and inbox views to match agents' LiveAgent workflows
Monitor for Data Gaps
For the first two weeks after go-live:
- Watch for contacts appearing as "unknown" (failed contact import — check dead-letter queue)
- Monitor for conversations with missing messages (truncation or API failures during part creation)
- Check that tags are applied correctly to migrated records
- Verify that Intercom's search index has fully processed all imported data (indexing can lag 15–30 minutes for large imports)
- Track first-reply time and resolution time metrics — establish a baseline in the first week and compare against your LiveAgent benchmarks
Operational Baseline
Expect the following during the first 30 days post-migration:
- Agent productivity dip of 15–30% in week 1 as agents learn the new interface
- Search performance: Intercom's search indexes may take up to 24 hours to fully process large imports
- Fin AI accuracy: If using Fin, it needs time to index Help Center articles. Test resolution accuracy on migrated articles before enabling for customers.
- Workflow tuning: Expect to adjust routing, assignment, and SLA workflows 3–5 times in the first two weeks as real traffic reveals edge cases
Best Practices
- Back up everything before you start. Export a full database dump from LiveAgent and store it independently. This is your safety net. Store it in a separate cloud region from your production systems.
- Run a test migration first. Use a dev Intercom workspace (free to create) and migrate a subset of 500–1,000 tickets. Validate before committing to the full run. Test with your most complex tickets (HTML-heavy, multi-attachment, long threads), not just simple ones.
- Freeze the source helpdesk. Institute a total freeze on LiveAgent configuration changes (custom fields, departments, tags) one week prior to migration.
- Create CDAs in Intercom before importing data. If a custom attribute does not exist when you send data, Intercom will silently discard the value — no error returned.
- Preserve source IDs. Store the LiveAgent ticket ID and contact ID as custom attributes in Intercom. This is your only lifeline for cross-referencing data post-migration and for rollback.
- Use date-range batching for extraction. LiveAgent's API performs better when you query by date range rather than pulling the entire dataset at once. The 10,000-record cap per filtered request makes this mandatory for datasets exceeding that threshold. Use 7-day windows as a starting point; narrow to 1-day windows if any range returns close to 10K results.
- Build idempotent import scripts. If a script fails mid-run, you should be able to restart without creating duplicates. Use Intercom's
external_idfield to enforce uniqueness. Handle409 Conflictresponses by retrieving the existing record. - Use delta migrations. Run the bulk of your historical data a week before go-live. On go-live weekend, only migrate the delta — tickets created or updated since the bulk run. Use LiveAgent's
date_fromfilter to identify delta records. - Log everything. Every API call, every error, every skipped record. Hash PII in logs (do not log raw email addresses). You will need this for validation and debugging.
- Suppress notifications during import. Verify in your test workspace that historical conversation creation does not trigger customer-facing emails or push notifications. This is the highest-impact mistake you can make.
Sample Data Mapping Reference
| # | LiveAgent Field | Intercom Field | Transformation |
|---|---|---|---|
| 1 | contactid | external_id | Cast to string |
| 2 | Lowercase, trim whitespace | ||
| 3 | firstname + lastname | name | Concatenate with space; default to "Unknown" if both empty |
| 4 | phone | phone | Direct copy |
| 5 | company.name | company.name | Direct copy |
| 6 | company.id | company.company_id | Cast to string; use as stable identifier |
| 7 | ticket.subject | conversation.source.subject | Direct copy (first message only); prepend to body for Conversations |
| 8 | ticket.status | conversation.state | Map: N/T/A/C/B→open, P→snoozed, R/X→closed |
| 9 | ticket.dateCreated | conversation.created_at | Convert to Unix timestamp (seconds) |
| 10 | ticket.departmentId | conversation.team_assignee_id | Map via lookup table |
| 11 | ticket.agentId | conversation.assignee.id | Map LiveAgent agent → Intercom admin via lookup table |
| 12 | ticket.tags [] | conversation.tags [] | Match by name, create if missing |
| 13 | message.body | conversation_part.body | HTML preserved where supported; strip unsupported tags |
| 14 | message.type (internal) | conversation_part.message_type | Map to "note" |
| 15 | message.type (public) | conversation_part.message_type | Map to "comment" |
| 16 | contact.custom_field_* | contact.custom_attributes.* | Create CDA first; truncate text >255 chars |
| 17 | ticket.custom_field_* | conversation.custom_attributes.* | Create CDA first; map list values to Intercom list item IDs |
| 18 | message.attachment | conversation_part attachment URL | Download, re-upload, reference new URL |
Making the Right Call
Migrating from LiveAgent to Intercom is a one-way architectural shift. You are moving from a ticket-centric system to a conversation-first platform, and the data model differences mean there is no push-button migration.
The critical decisions are:
- What data to bring — not everything in LiveAgent has a home in Intercom. Call recordings, gamification data, forum posts, and SLA history will not migrate.
- Conversations vs. tickets — this affects your entire target schema and determines which Intercom API endpoints you use
- How to handle custom fields — Intercom's CDA limits (250 active, 255-char text, 35-option dropdown) will force prioritization
- Whether to build or buy — the engineering effort is real (2–6 weeks depending on complexity), but so is the cost of getting it wrong (notification leakage, data loss, duplicate contacts)
By respecting API limits (180 req/min LiveAgent, 10K req/min Intercom), mapping data models accurately, testing in a sandbox workspace, and thoroughly handling edge cases, you ensure your support team can transition to a modern conversational workflow without losing a single piece of customer history.
If you are planning this migration and want to skip the trial-and-error, check out our guides on Intercom migrations and helpdesk migration planning. For a deep look at post-migration QA, see our migration QA checklist.
Frequently Asked Questions
- Can I migrate LiveAgent tickets to Intercom with CSV?
- Not cleanly. Intercom's CSV importer handles people records only — not company data, conversation threads, or attachments. Full ticket history requires the LiveAgent API for extraction and the Intercom API for import. CSV is only viable for small contact-only migrations where you can start fresh with conversations.
- What are the API rate limits for LiveAgent and Intercom?
- LiveAgent's cloud API is limited to 180 requests per minute per API key, with a 10,000-record cap per filtered ticket query. Intercom allows 10,000 API calls per minute per app and 25,000 per workspace, distributed in 10-second windows. LiveAgent's lower limit is typically the bottleneck during extraction.
- Should LiveAgent tickets become Intercom conversations or tickets?
- Customer-facing threads (email, chat) usually map to Intercom conversations. Back-office or tracked work items map better to Intercom tickets, which require pre-created ticket types. This decision must be made before the first load because it affects the entire target schema.
- How long does a LiveAgent to Intercom migration take?
- Timeline depends on data volume. A small migration (under 5K tickets) can be done in 1–2 days. Mid-size migrations (5K–100K tickets) typically take 3–7 days including testing. Enterprise migrations with 100K+ tickets may take 1–2 weeks due to LiveAgent's API rate limits on extraction.
- What LiveAgent data is lost when migrating to Intercom?
- Call recordings, voicemail files, SLA history metrics, chat session metadata (typing indicators, visitor page URLs), automation rules, agent gamification data, and forum/suggestion artifacts do not have equivalents in Intercom. These should be archived externally before migration.


