Skip to content

Intercom to Gladly Migration: The Complete Technical Guide

A technical guide to migrating from Intercom to Gladly. Covers data model mapping, API constraints, Gladly's text-only import limits, and validation.

Abdul Abdul · · 28 min read
Intercom to Gladly Migration: The Complete 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
Info

TL;DR — Intercom to Gladly Migration

Migrating from Intercom to Gladly is a data-model translation project, not a lift-and-shift. Intercom stores interactions as discrete Conversations and Tickets, each with its own lifecycle. Gladly collapses all customer interactions into a single, lifelong Conversation Timeline with no ticket numbers. The critical constraint: Gladly's historical import is text-only — no images, recordings, attachments, metrics, or routing assignments are imported. Every imported record must be associated with a valid email address, or it's rejected. On the Intercom extraction side, the native CSV export does not include conversation transcripts — you need the REST API for full message content.

Typical timeline for 50,000–200,000 conversation parts:

Phase Duration
Data audit & scope definition 2–3 days
Extraction from Intercom API 2–4 days (rate-limit dependent)
Transformation & validation 3–5 days
Test load (sandbox) 2–3 days
Validation & UAT 2–3 days
Production cutover 1 day
Delta sync & monitoring 2 weeks
Total 2–4 weeks

Written July 2026. Targets Intercom REST API v2.11+ and Gladly REST API.

Why Teams Migrate from Intercom to Gladly

The most common reasons behind an Intercom-to-Gladly move:

  • Person-centric support model. Gladly is built around a single customer profile where every interaction — email, chat, voice, SMS, social — threads into one continuous timeline. Teams that want to eliminate the "can you give me your ticket number?" experience move to Gladly for this reason alone.
  • Native voice. Gladly includes native voice with programmable IVR. Intercom requires a third-party telephony integration (e.g., Aircall, Dialpad) for voice workflows.
  • Simplified agent experience. Agents see one lifelong conversation per customer instead of searching across dozens of discrete conversations and tickets.
  • Channel consolidation. Gladly natively supports voice, SMS/MMS, email, chat, social (Facebook, Instagram, Twitter/X), and WhatsApp without bolting on third-party channel providers.
  • Cost structure at scale. Intercom's per-seat pricing with add-on charges for AI features (Fin) and advanced workflows can become expensive for large contact-center teams. Gladly's per-"hero" (agent) pricing with bundled channels is structured differently for high-volume B2C operations — typically more predictable at 50+ agents.
Warning

When NOT to migrate: If your team relies heavily on Intercom's product tours, in-app messaging SDK, custom bots, Series automation, or custom objects for complex data modeling, Gladly does not have equivalents. Evaluate whether you can replace those workflows before committing. Intercom's Messenger SDK for mobile and web has no Gladly analog beyond the chat widget.

The Core Data Model Clash: Discrete Conversations vs. Lifelong Timelines

This is the architectural mismatch that defines every decision in this migration.

Intercom organizes support interactions as discrete Conversations and Tickets. Each Conversation has a unique ID, a lifecycle (open → snoozed → closed), and a list of Conversation Parts (messages, notes, assignments, state changes). Tickets are a separate object type (introduced in API v2.9) with custom states and ticket type attributes. A single Contact can have hundreds of separate Conversations and Tickets over time. Intercom's native conversation CSV export is metadata only — full message content requires the Conversations API or cloud export.

Gladly operates on the opposite principle. Every customer has a single profile, and every interaction across every channel threads into one continuous Conversation Timeline. There are no ticket numbers. An agent sees the entire customer history as a single scrollable stream. See Gladly's Conversation model documentation for the full schema.

You cannot import an Intercom Conversation as a "Ticket" in Gladly. You must extract the conversation, format it as a series of Conversation Items, and append them to the Gladly Customer's timeline.

Concept Intercom Gladly
Core unit of work Conversation / Ticket Conversation Item (within a lifelong timeline)
Identity Contact (User or Lead) + Company Customer Profile
Ticket numbers Yes (Tickets have IDs) No
Conversation lifecycle Open → Snoozed → Closed Continuous timeline; Contacts (interactions) are opened/closed
Channels Chat, Email, Social, SMS (via integrations) Voice, Email, Chat, SMS, Social — all native
Attachments in history Stored per Conversation Part Not imported for historical data
Custom objects Supported (API v2.11+) Not supported
Company object First-class Company entity with many-to-many Contact relationships No native equivalent — use custom attributes or Lookup Adaptor

Migration Approaches: Which Method Fits Your Team

Approach 1: Gladly's Native Historical Import (CSV-Based)

How it works: Extract your Intercom data, transform it into Gladly's CSV import template, upload the CSV to a Gladly-provided Dropbox link, and Gladly's implementation team processes the import. Because Intercom's CSV export does not include conversation transcripts, you still need the Intercom REST API for full message content — so this is not purely CSV-to-CSV.

When to use it: You're a new Gladly customer going through onboarding, your dataset is under 500K conversation items, and you don't need attachments in Gladly history.

Pros:

  • Gladly handles the import processing
  • No need to interact with the Gladly API directly
  • Works well for clean, structured datasets

Cons:

  • Text-only. No images, recordings, attachments, or metrics are imported.
  • Every record must have a valid email address — records without one are rejected.
  • Imported data is not searchable or reportable in Gladly beyond customer contact info (name, phone, email).
  • Subject to a Professional Services fee from Gladly (typically $5K–$20K depending on volume and complexity).
  • Bodies are truncated at 25,000 characters; subjects at 100 characters.

Processing time: A 500K-row CSV import typically takes Gladly's team 3–7 business days to process after upload. Gladly recommends batches of 500K rows.

Complexity: Low to Medium

Approach 2: API-Based Migration (Intercom REST API → Gladly REST API)

How it works: Write custom scripts to extract data from Intercom's REST API, transform it into Gladly's data model, and load it via Gladly's Customer and Conversation APIs.

When to use it: You need maximum control over mapping logic, have a dedicated engineering team, or need to run incremental delta syncs alongside a historical backfill.

Pros:

  • Full control over data transformation
  • Can handle complex mapping logic (e.g., splitting Intercom Tickets vs. Conversations into different timeline entries)
  • Best approach for delta syncs and ongoing profile enrichment

Cons:

  • High engineering effort (200–400 engineer-hours for datasets of 100K+ conversations)
  • Must handle Intercom's rate limits (10,000 calls/min per private app, distributed in 10-second windows)
  • Must handle Gladly's rate limits (429 errors; monitor X-RateLimit-Remaining and X-RateLimit-Reset headers)
  • Gladly API retrieval caps: 100 conversations per customer, 1,000 items per conversation via GET
  • API-created customer activities in Gladly are not native searchable conversations — they behave differently from data loaded via the historical import path
  • Updating phones or emails in Gladly requires sending the full array (replace-style, not patch-per-item)

Performance benchmark: Extracting 100K Intercom conversations with full parts at rate limit takes approximately 4–8 hours (depending on average parts-per-conversation and parallelism). Loading 100K customer profiles into Gladly via API takes approximately 2–4 hours with proper backoff.

Complexity: High

Approach 3: Managed Migration Service

How it works: A migration service provider handles extraction, transformation, loading, and validation. The provider builds custom scripts, manages rate limits, handles edge cases, and runs test migrations before production cutover.

When to use it: You need zero downtime, have limited engineering bandwidth, your dataset exceeds 100K conversations, you have complex data (multiple channels, custom attributes, many-to-many relationships), or you can't afford a failed migration.

Pros:

  • No engineering time required from your team
  • Provider handles rate limiting, pagination, error recovery
  • Test migrations before production cutover
  • Experience with edge cases across dozens of similar migrations

Cons:

  • External cost (typically $10K–$50K+ depending on volume, complexity, and provider)
  • Requires coordination and access provisioning
  • Ask the provider exactly which target ingest path they use — if the answer is only the public Gladly REST API, historical fidelity may be lower than via the CSV import path

Complexity: Low for your team (handled by the provider)

Approach 4: Custom ETL Pipeline

How it works: Land raw Intercom exports in object storage or a data warehouse (Snowflake, BigQuery), normalize contacts, companies, and conversations, generate Gladly historical-import CSVs for the backfill, then use the Gladly API or Lookup Adaptor for profile enrichment, topics, notes, and live context.

When to use it: Enterprise migrations, regulated data, custom objects, multi-brand support, or any project that needs replayable runs and auditable validation.

Pros:

  • Full control over mapping, archives, reruns, and exception handling
  • Leverages existing data infrastructure
  • Highly auditable — every transformation step is logged and replayable

Cons:

  • Highest engineering cost — you own idempotency, retries, and cutover tooling
  • Over-engineered for a standard helpdesk migration unless auditability or replay requirements justify it

Complexity: High

Approach 5: Middleware / iPaaS (Zapier, Make)

How it works: Use Zapier or Make to connect Intercom and Gladly, mapping triggers and actions between the two platforms.

When to use it: Ongoing sync of new records only — not for historical migration. Useful as a temporary bridge during a transition period.

Pros:

  • No-code setup
  • Fast to wire up for lightweight profile sync

Cons:

  • Cannot handle bulk historical data
  • No transformation logic for complex field mapping
  • Rate limit handling is limited
  • Cost scales with volume (Zapier charges per task)
  • Not a migration engine — it's a workflow orchestrator

Complexity: Low

Migration Approach Comparison

Criteria Gladly CSV Import API-Based Managed Service Custom ETL Middleware
Historical data ✅ (lower fidelity)
Attachments ❌ (Gladly limitation)
Engineering effort Low High (200–400 hrs) None Very High (400+ hrs) Low
Transformation control Limited Full Full (via provider) Full Minimal
Scale (>100K records) ⚠️ Batch at 500K rows ✅ With patience
Zero downtime N/A
Cost Gladly PS fee ($5K–$20K) Internal eng time Service fee ($10K–$50K+) Internal eng time Per-task pricing

Recommendations:

  • Small team, <50K conversation items, clean data: Gladly's native CSV import.
  • Mid-size, 50K–500K items, dedicated dev team: API-based migration with test runs, or CSV import for backfill combined with API for profile enrichment.
  • Enterprise, >500K items, zero downtime, limited eng bandwidth: Managed migration service.
  • Ongoing sync during transition: Middleware for new records only, combined with one of the above for historical data.
  • Most serious projects end up using multiple patterns together: historical import for the backfill, APIs for deltas, and Lookup Adaptor for live context.

Common DIY Migration Failures

DIY migrations fail in predictable ways:

  • Silent transcript loss. Intercom's CSV and S3 exports do not include conversation transcripts. Teams discover this after cutover when agents report empty conversation histories.
  • Email validation rejects. Gladly rejects any imported record without a valid email. Intercom Leads often lack email addresses — they're created when anonymous visitors start a Messenger conversation. On a typical Intercom workspace, 15–40% of Leads have no email.
  • Attachment gap surprise. Stakeholders expect attachments in Gladly history. Gladly's import is text-only. Without upfront communication, this becomes a post-migration escalation.
  • Rate limit cascades. Intercom distributes its rate limits in 10-second windows (~1,666 calls per 10s for a private app). Scripts without backoff logic hit 429 errors and corrupt partial imports.
  • Character truncation. Gladly truncates bodies at 25,000 characters. Long email threads with HTML markup can exceed this easily — a 10KB HTML email renders to ~3KB of text, but threads with 10+ replies can blow past 25K. Strip tags and check counts pre-load.
  • Data truncation on long threads. Intercom's API returns a maximum of 500 parts per conversation. If your script doesn't check total_count against returned parts, you silently lose messages from your longest-running support threads.

Pre-Migration Planning

Data Audit Checklist

Before extracting anything, inventory what you have in Intercom and what you actually need in Gladly. Use Intercom's /counts endpoint to quantify your data before making scope decisions:

Object Intercom Action
Contacts (Users) Active users with email Migrate → Gladly Customer Profiles
Contacts (Leads) May lack email Flag for email validation; reject or enrich from CRM
Companies Company records Map to custom attributes or Lookup Adaptor
Conversations Chat/email/social threads Migrate → Gladly Conversation Items
Tickets Tracked async issues Migrate → Gladly Conversation Items or Tasks
Tags Categorization labels Migrate → Gladly Topics
Notes Internal notes on contacts Migrate → internal notes in timeline
Articles (Help Center) KB articles Migrate → Gladly Answers
Custom Attributes Contact/Company metadata Map to Gladly custom attributes (limited to string, number, boolean, date types)
Custom Objects Intercom-specific relational data ❌ No equivalent in Gladly — flatten or archive
Data Events Behavioral tracking ❌ No equivalent — archive only
Product Tours In-app experiences ❌ No equivalent — rebuild or drop
Series / Workflows Automation sequences ❌ Rebuild in Gladly as rules/routing
Bots (Custom Bots, Fin) AI/automation ❌ Rebuild using Gladly Sidekick

Alternative Extraction: GDPR Data Portability

For EU-based Intercom workspaces, you can also request a GDPR data portability export as an alternative extraction method. This provides a bulk JSON export but does not include real-time delta capability and may take 24–72 hours to generate. It is useful as a backup or audit copy but is not a substitute for API-based extraction if you need incremental syncs.

Define Migration Scope

  1. Must migrate: Contacts with email, conversation history (last 12–24 months), tags, notes.
  2. Nice to have: Full conversation history, help center articles.
  3. Archive only: Events, custom objects, product tours, bot configurations.
  4. Drop: Test data, spam conversations, duplicate contacts.

Migration Strategy

  • Big bang: Export everything, transform, load into Gladly over a weekend. Best for datasets under 200K items.
  • Phased: Migrate contacts first, then conversation history in batches, then run a delta sync before cutover. Reduces risk, extends timeline by 1–2 weeks.
  • Incremental: Migrate recent data first (last 6 months), go live on Gladly, then backfill older history. Lets you start using Gladly sooner.

Gladly recommends conducting the import in batches — the main batch two weeks before launch, then a delta import two days after launch to cover the gap.

Warning

Risk mitigation: Take a full JSON backup of all Intercom data via the API before starting. You cannot re-extract after the Intercom workspace is decommissioned. If Intercom is carrying CRM-like account or opportunity data, do not model those objects as Gladly conversations. Keep the system of record outside Gladly and surface only the fields agents need via custom attributes or Gladly's Lookup Adaptor.

Data Model & Object Mapping

Object-Level Mapping

Intercom Object Gladly Equivalent Notes
Contact (User) Customer Profile Requires valid email; phone and name optional
Contact (Lead) Customer Profile Only if email exists; anonymous leads are dropped. Preserve lead/user role as a custom attribute if it matters operationally.
Company No direct object Use custom attributes or Lookup Adaptor to surface company data
Conversation Conversation Items (in timeline) Each part becomes a timeline item
Ticket Conversation Items or Task Route based on whether the ticket needs follow-up
Conversation Part Conversation Item Map body → item content, preserve timestamps
Tag Topic Gladly Topics replace tags for categorization
Note Note (in timeline) Internal notes map 1:1
Article (Help Center) Answer Requires re-formatting for Gladly's Answer structure
Custom Attribute Custom Attribute Limited to string, number, boolean, date types in Gladly
Admin/Teammate Agent Recreate agent accounts manually; match by exact email
Team Team/Inbox Recreate manually; map for routing rules
Segment People Match rules Rebuild as Gladly People Match configurations

Field-Level Mapping: Contacts → Customer Profiles

Intercom Field Gladly Field Transformation
email emails [].original Required; reject if missing
name name Split into first/last if needed
phone phones [].original Format to E.164
external_id externalCustomerId Direct map
created_at createdAt Unix → ISO 8601
custom_attributes.* customAttributes.* Validate type compatibility (string, number, boolean, date only)
avatar.image_url N/A Not imported
location.* address Map city/country to address fields
tags Topics (linked at conversation level) Tags are contact-level in Intercom; Topics are conversation-level in Gladly

Handling Custom Objects and Companies

Custom Objects: Gladly does not support relational custom objects. Any custom relational data from Intercom must be flattened into Custom Attributes on the Gladly Customer profile. If you have complex relational data (e.g., orders, subscriptions with multiple line items), you will lose that relational structure. Consider maintaining the data in an external system and surfacing it via Gladly's Lookup Adaptor, which makes a real-time API call to your backend when an agent views a customer profile.

Companies: Gladly does not have a native Company object like Intercom. If your team relies on Company-level reporting or grouping, use Gladly's Lookup Adaptor to pull company data from an external source (CRM, ERP) into the customer profile at query time. Storing the company name as a custom attribute works, but it loses the relational link. Intercom supports many-to-many relationships between Contacts and Companies — Gladly has no equivalent for this structure. For migration, choose a primary company per contact and store it as a custom attribute; maintain the full many-to-many mapping in your CRM.

Gladly People Match Configuration

Intercom Segments are used to group contacts for routing and targeting. In Gladly, the equivalent is People Match, which routes incoming conversations to agents based on customer attributes, conversation channel, topic, and agent skills. People Match rules are configured in Gladly Admin under Routing and use a priority-ordered rule set. Key differences from Intercom Segments:

  • People Match operates at routing time, not as a static list
  • Rules match on customer attributes, channel, topic, and language
  • You cannot export a "segment" from People Match — it's a routing engine, not a segmentation tool
  • Rebuild your most critical Intercom Segments as People Match rules before go-live

Migration Architecture

Data Flow: Extract → Transform → Load

Intercom REST API (source)
  │
  ├─ POST /contacts/search (cursor-paginated)
  ├─ GET /conversations (paginated list, then GET /{id} for full parts)
  ├─ GET /tickets (paginated, API v2.9+)
  ├─ GET /tags
  ├─ GET /admins
  ├─ GET /articles (Help Center)
  │
  ▼
Transformation Layer (local staging DB or data warehouse)
  │
  ├─ Validate emails (reject records without valid email)
  ├─ Strip HTML from bodies, enforce 25K char limit
  ├─ Convert timestamps (Unix → ISO 8601)
  ├─ Deduplicate contacts (merge by email)
  ├─ Group conversations by contact, sort chronologically
  ├─ Map Tags → Topics
  ├─ Map Companies → custom attributes
  ├─ Download and archive attachments to S3/GCS (Intercom URLs expire)
  ├─ Maintain ID crosswalk (Intercom ID → Gladly ID)
  │
  ▼
Gladly (target)
  ├─ POST /api/v1/customer-profiles (Create Customer Profiles)
  ├─ POST /api/v1/customers/{id}/conversation-items (Add timeline items)
  ├─ OR: Upload CSV to Gladly Dropbox for native import

Intercom API: Extraction Details

  • Rate limits: 10,000 calls/min per private app, 25,000 calls/min per workspace. Distributed in 10-second windows (~1,666 calls per 10s for a private app). See Intercom rate limit documentation.
  • Pagination: Cursor-based for most endpoints. The conversations list returns metadata only — you must GET each conversation individually for full parts.
  • Conversation parts limit: Each conversation returns a maximum of 500 parts via the API. Check total_count against returned parts to detect truncation.
  • Transcript gap: The native CSV/S3 export does not include message content. Use the REST API.
  • Archived contacts: Must be unarchived via API (POST /contacts/{id}/unarchive) before data can be retrieved. Cannot be unarchived from the UI.
  • Company extraction: Intercom's conversation search does not support company_id. To extract company-scoped conversations, first resolve contacts for the company via GET /companies/{id}/contacts, then search conversations by contact_ids.

Gladly API: Loading Details

  • Rate limits: Gladly returns 429 errors for excessive requests. Monitor X-RateLimit-Remaining and X-RateLimit-Reset response headers and implement exponential backoff. See Gladly API documentation.
  • Retrieval caps: GET endpoints return at most 100 conversations per customer and 1,000 items per conversation. The Gladly-Limited-Data response header flags truncation. These are retrieval limits, not ingestion limits — you can load more data than you can retrieve via standard GET calls.
  • Import constraints: Historical imports are text-only. Bodies truncated at 25,000 characters. Subjects truncated at 100 characters. Email is mandatory.
  • Customer uniqueness: Email, mobile phone, and external ID must be unique across records. A 409 error is returned for conflicts.
  • Array updates: Updating phones or emails requires sending the full array — it's replace-style, not patch-per-item. If you PATCH a customer and send only a new phone number, existing phones are overwritten.
  • Webhook configuration: After migration, configure Gladly webhooks for any downstream integrations that were previously listening to Intercom webhooks. Gladly supports webhooks for conversation events, customer updates, and task changes.

Data Security During Migration

  • In transit: Both Intercom and Gladly APIs enforce TLS 1.2+ for all API calls. Your extraction and loading scripts should verify TLS certificates.
  • At rest: Intercom encrypts data at rest using AES-256. Gladly encrypts data at rest. Your staging database or data warehouse should also encrypt at rest — particularly important if handling PII or data subject to GDPR, CCPA, or HIPAA.
  • Data residency: Intercom offers US, EU (Dublin), and AU (Sydney) data hosting regions. Gladly's infrastructure is US-based (AWS). If your Intercom workspace is in the EU region and you have GDPR data-residency requirements, confirm Gladly's data processing agreement covers your obligations before migration.

Step-by-Step Migration Process

Step 1: Extract Data from Intercom

import requests
import time
import json
import os
 
INTERCOM_TOKEN = os.environ["INTERCOM_TOKEN"]
HEADERS = {
    "Authorization": f"Bearer {INTERCOM_TOKEN}",
    "Accept": "application/json",
    "Intercom-Version": "2.11"
}
BASE_URL = "https://api.intercom.io"
CHECKPOINT_FILE = "extraction_checkpoint.json"
 
def load_checkpoint():
    """Load extraction checkpoint for idempotent restarts."""
    if os.path.exists(CHECKPOINT_FILE):
        with open(CHECKPOINT_FILE) as f:
            return json.load(f)
    return {"last_contact_cursor": None, "extracted_conversation_ids": []}
 
def save_checkpoint(state):
    with open(CHECKPOINT_FILE, "w") as f:
        json.dump(state, f)
 
def extract_contacts():
    """Extract all contacts using search API with checkpointing."""
    state = load_checkpoint()
    contacts = []
    payload = {
        "query": {"operator": "AND", "value": [
            {"field": "role", "operator": "=", "value": "user"}
        ]}
    }
    if state["last_contact_cursor"]:
        payload["pagination"] = {"starting_after": state["last_contact_cursor"]}
 
    url = f"{BASE_URL}/contacts/search"
    while True:
        resp = requests.post(url, headers=HEADERS, json=payload)
        handle_rate_limit(resp)
        resp.raise_for_status()
        data = resp.json()
        contacts.extend(data.get("data", []))
        next_cursor = data.get("pages", {}).get("next", {}).get("starting_after")
        if next_cursor:
            payload["pagination"] = {"starting_after": next_cursor}
            state["last_contact_cursor"] = next_cursor
            save_checkpoint(state)
        else:
            break
    return contacts
 
def extract_conversation_with_parts(conversation_id):
    """Get full conversation including all parts. Check for truncation."""
    resp = requests.get(
        f"{BASE_URL}/conversations/{conversation_id}",
        headers=HEADERS
    )
    handle_rate_limit(resp)
    resp.raise_for_status()
    data = resp.json()
 
    # Check for part truncation (500-part API limit)
    parts = data.get("conversation_parts", {})
    total_count = parts.get("total_count", 0)
    returned_count = len(parts.get("conversation_parts", []))
    if returned_count < total_count:
        print(f"WARNING: Conversation {conversation_id} truncated: "
              f"{returned_count}/{total_count} parts returned")
 
    return data
 
def handle_rate_limit(response):
    """Exponential backoff on 429 using reset header."""
    if response.status_code == 429:
        reset_time = int(response.headers.get("X-RateLimit-Reset", time.time() + 10))
        wait = max(reset_time - time.time(), 1)
        print(f"Rate limited. Waiting {wait:.1f}s")
        time.sleep(wait)

Step 2: Transform Data

import re
from datetime import datetime
 
MAX_BODY_LENGTH = 25000
MAX_SUBJECT_LENGTH = 100
 
def transform_contact_to_gladly(intercom_contact):
    """Transform Intercom contact into Gladly customer payload."""
    email = intercom_contact.get("email")
    if not email or not is_valid_email(email):
        return None  # Gladly rejects records without valid email
 
    name = intercom_contact.get("name", "")
    # Split name into first/last for Gladly
    name_parts = name.strip().split(" ", 1) if name else ["", ""]
 
    return {
        "name": name,
        "emails": [{"original": email}],
        "phones": [{"original": format_e164(intercom_contact["phone"])}]
            if intercom_contact.get("phone") else [],
        "externalCustomerId": intercom_contact.get("external_id"),
        "customAttributes": transform_custom_attributes(
            intercom_contact.get("custom_attributes", {})
        )
    }
 
def is_valid_email(email):
    """Basic email validation — Gladly rejects malformed emails."""
    return bool(re.match(r'^[^@]+@[^@]+\.[^@]+$', email))
 
def format_e164(phone):
    """Format phone to E.164. Expand for production use."""
    if phone and not phone.startswith('+'):
        return f"+1{phone}"  # Default to US — adjust for your data
    return phone
 
def strip_html_and_truncate(html_body):
    """Strip HTML and enforce Gladly's 25K character limit."""
    text = re.sub(r'<[^>]+>', '', html_body or '')
    text = text.strip()
    if len(text) > MAX_BODY_LENGTH:
        text = text[:MAX_BODY_LENGTH - 50] + "\n[Truncated — see archive]"
    return text
 
def transform_conversation_part(part, customer_email, intercom_conversation_id):
    """Transform an Intercom conversation part into a Gladly-compatible record."""
    return {
        "customer_email": customer_email,
        "body": strip_html_and_truncate(part.get("body", "")),
        "timestamp": datetime.utcfromtimestamp(
            part["created_at"]
        ).isoformat() + "Z",
        "direction": "INBOUND" if part.get("author", {}).get("type") == "user"
            else "OUTBOUND",
        "agent_name": part.get("author", {}).get("name", "System"),
        "intercom_id": intercom_conversation_id  # Preserve for crosswalk
    }

Step 3: Load into Gladly

For the API route:

import logging
 
GLADLY_BASE = "https://your-org.gladly.com/api/v1"
GLADLY_AUTH = ("your-email@company.com", "your-api-token")
CROSSWALK_FILE = "id_crosswalk.json"
 
logging.basicConfig(level=logging.INFO, filename="migration.log")
 
def create_gladly_customer(payload, intercom_id):
    """Create customer profile in Gladly with logging and crosswalk."""
    resp = requests.post(
        f"{GLADLY_BASE}/customer-profiles",
        auth=GLADLY_AUTH,
        json=payload
    )
    if resp.status_code == 409:
        # Customer already exists — extract ID from conflict response
        gladly_id = handle_duplicate(resp)
        logging.info(f"Duplicate: Intercom {intercom_id} → Gladly {gladly_id}")
        return gladly_id
    if resp.status_code == 429:
        handle_rate_limit(resp)
        return create_gladly_customer(payload, intercom_id)  # Retry
    if resp.status_code >= 400:
        logging.error(f"Failed to create customer for Intercom {intercom_id}: "
                      f"{resp.status_code} {resp.text}")
        return None
    gladly_id = resp.json()["id"]
    save_crosswalk(intercom_id, gladly_id)
    return gladly_id
 
def add_conversation_items(customer_id, items):
    """Add conversation items to a customer's timeline with retry logic."""
    for item in items:
        for attempt in range(3):
            resp = requests.post(
                f"{GLADLY_BASE}/customers/{customer_id}/conversation-items",
                auth=GLADLY_AUTH,
                json=item
            )
            if resp.status_code == 429:
                handle_rate_limit(resp)
                continue
            if resp.status_code >= 400:
                logging.error(f"Failed item for customer {customer_id}: "
                              f"{resp.status_code} {resp.text} | "
                              f"Payload: {json.dumps(item)[:500]}")
                break
            break  # Success

For the CSV route, format your data per Gladly's CSV import template and upload to the Dropbox link provided by Gladly's implementation team. A sample CSV row:

customer_email,customer_name,customer_phone,title,body,activityType,occurredAt,sourceName
jane@example.com,Jane Doe,+15551234567,Intercom 12345,Refund approved for order #9876. Agent: Sarah.,EMAIL,2026-06-01T12:30:00.000Z,Intercom

Step 4: Rebuild Configuration

These cannot be migrated and must be rebuilt manually in Gladly:

  • Routing rules (replace Intercom's assignment rules) — configure in Gladly Admin → Routing
  • SLA policies (Gladly has its own SLA configuration) — configure in Gladly Admin → SLAs
  • Topics (created during import or manually) — these replace Intercom Tags for conversation categorization
  • Answers (replace Intercom's Help Center articles and macros/saved replies) — supports rich text, can be organized by category
  • People Match rules (replace Intercom's segments for routing) — priority-ordered rule set matching on customer attributes, channel, topic, and language
  • Integrations (Shopify, Salesforce, etc. — reconnect in Gladly)
  • IVR and voice routing (if using Gladly Voice)
  • Sidekick configuration (Gladly's AI assistant — trained on native Gladly data, not imported history; expect 2–4 weeks of native data accumulation before AI suggestions reach useful quality)
  • Webhooks — configure for any downstream systems that previously consumed Intercom webhook events

Step 5: Validate

Compare source and destination record counts, perform field-level validation on a sample of highly active customers, and conduct UAT. See the Validation & Testing section below.

Edge Cases & Challenges

Anonymous Leads Without Email

Intercom creates Leads automatically when anonymous visitors start a Messenger conversation. These often have no email address — typically 15–40% of Leads on a consumer-facing Intercom workspace. Gladly requires a valid email for every imported record. Decision point: drop anonymous leads (most common), enrich them from your CRM before migration, or assign a placeholder email (not recommended for production — creates noise and compliance issues).

Conversation Parts Limit

Intercom's API returns a maximum of 500 parts per conversation. Long-running conversations with extensive back-and-forth may hit this limit. When retrieving via API, check total_count against the returned parts count. If truncated, the full history is not available via the standard endpoint. Fallback options: (1) use Intercom's cloud export for the full thread, (2) archive the conversation as complete-as-possible and note the truncation, or (3) contact Intercom support for bulk export assistance.

Duplicate Contacts

Intercom allows multiple contacts with the same email in some edge cases (e.g., a user and a lead with the same email before merging). Gladly enforces strict uniqueness on email addresses, phone numbers, and external IDs. Deduplicate before loading — merge conversation histories from duplicate Intercom contacts into a single Gladly Customer Profile. A 409 error is returned for conflicts. Your transformation script should group contacts by email, merge their conversations chronologically, and pick the most complete profile data (name, phone, custom attributes) from the duplicates.

HTML Content and Character Limits

Intercom conversation parts store message bodies as HTML. Gladly's historical import is text-based. Strip HTML tags during the transformation phase. After stripping, check character counts — bodies over 25,000 characters and subjects over 100 characters are silently truncated by Gladly. Handle this yourself so you can append a " [Truncated — see archive]" note rather than losing content without a trace. A typical 10KB HTML email renders to ~3KB of plain text, but threaded conversations with 15+ replies can easily exceed 25K characters.

Attachments and Inline Images

Gladly's historical import does not support attachments, images, or recordings. Intercom conversations with screenshots, PDFs, or file uploads will lose those assets in Gladly. Mitigation: Archive attachments to S3 or a shared drive before migration — Intercom attachment URLs expire (typically within hours to days, not permanently hosted). Add a reference link in the imported conversation item body so agents can access the original file. Your extraction script must download files during extraction, not after.

Images embedded directly in Intercom chat bubbles (inline <img> tags) will break when translated to Gladly's timeline. They must be extracted and archived externally.

Intercom Tickets vs. Conversations

Since API v2.9, Intercom separates Tickets from Conversations. They use different endpoints (/conversations vs. /tickets) and different data models. Your extraction script must query both. In Gladly, both become Conversation Items in the timeline — but you should differentiate them with Topics (e.g., a "Ticket" topic for imported Intercom tickets) so agents and reporting can distinguish between them.

Tags → Topics Mismatch

Intercom Tags are applied at both the contact and conversation level. Gladly Topics are conversation-level only. Contact-level tags have no direct equivalent in Gladly — store them as custom attributes on the Customer Profile. Pre-create all Topics in Gladly before import.

Loss of Ticket IDs

Because Gladly uses a single timeline, old Intercom ticket IDs become obsolete. If your external systems (Jira, Salesforce, Slack integrations) reference Intercom Conversation IDs, those links will break. Workaround: Append the old Intercom Conversation ID as a searchable note or custom attribute on the Gladly timeline. Maintain an ID crosswalk file mapping Intercom IDs to Gladly IDs for post-migration troubleshooting and delta syncs.

Rich Media Downgrades

Intercom "Articles" inserted into chats, custom apps (carousels, date pickers), and rich message types will render as plain text or broken links in Gladly. This is a fidelity loss you must accept and communicate to stakeholders before go-live.

Limitations & Constraints

Gladly Import Constraints

  • Text-only: No images, recordings, attachments, metrics, or routing/conversation assignments.
  • Email mandatory: Every imported record must have a valid email.
  • Not searchable: Imported data (beyond contact info) is not searchable or reportable in Gladly.
  • Not AI-trainable: Imported history does not feed Gladly Sidekick or AI-powered answer suggestions. Sidekick operates on native Gladly data only. Expect a 2–4 week ramp-up period where AI suggestions improve as new native data accumulates.
  • Body truncation: 25,000 characters max. Subjects: 100 characters max.
  • Professional Services fee: Historical imports are subject to a fee from Gladly (typically $5K–$20K).

Gladly API Retrieval Caps

  • 100 conversations per customer (unpaginated GET)
  • 1,000 items per conversation (unpaginated GET)
  • The Gladly-Limited-Data header flags when results are truncated
  • These are retrieval limits, not ingestion limits — data beyond these thresholds exists but isn't returned by standard GET calls
  • Use Gladly's Export API for post-migration validation on high-volume profiles

Intercom Export Constraints

  • No transcript in CSV/S3 export. You must use the REST API for full message content.
  • 500 parts per conversation maximum via API.
  • Archived contacts must be unarchived via API before retrieval.
  • Rate limits distributed in 10-second windows.

Data That Cannot Be Migrated

Intercom Feature Status
Product Tours Not migratable — no Gladly equivalent
Custom Bots Not migratable — rebuild in Sidekick
Series (outbound automation) Not migratable — rebuild as Gladly campaigns
Data Events (behavioral tracking) Not migratable — archive only
Custom Objects Not migratable — flatten or use Lookup Adaptor
Fin AI configuration Not migratable — configure Sidekick separately
Intercom Messenger settings Not migratable — configure Gladly Chat widget
Conversation assignments / routing history Not migratable — text-only import
SLA metrics and response times Not migratable — historical metrics are lost

Validation & Testing

Gladly Sandbox Provisioning

Before running any test migration, request a Gladly sandbox environment from your Gladly account team. Sandboxes are provisioned by Gladly (not self-service) and typically take 1–3 business days. Sandbox limitations: they share the same API rate limits as production, may have reduced storage, and data is periodically purged. Never migrate directly to production without at least one full sandbox test run.

Record Count Comparison

Check How
Total contacts exported from Intercom Count from extraction script or /counts endpoint
Total Customer Profiles in Gladly Gladly Admin → Customers count or API GET /customer-profiles
Delta Should be ≤ number of contacts without email (expected rejections)
Total conversation items exported Count from transformation step
Total items in Gladly Use Gladly's Export API — not the GET endpoint (retrieval caps apply)

Do not assume a 200 OK response means the data looks correct in the UI. Validate visually.

Field-Level Validation

Sample 50–100 records across different profiles:

  • Verify email, name, phone match between Intercom source and Gladly profile
  • Verify conversation item bodies are not truncated unexpectedly
  • Verify timestamps are correct and in chronological order
  • Verify Topics are applied correctly (mapped from Intercom Tags)
  • Verify custom attributes are populated and have correct types
  • Verify the ID crosswalk is complete and bidirectional

Sampling Strategy

  • High-volume customers: Check profiles with the most conversations (>50). These are most likely to hit retrieval caps.
  • Multi-channel customers: Check profiles that had email + chat + social interactions in Intercom.
  • Edge cases: Check profiles with no email (should be rejected), profiles with special characters in names, conversations with HTML-heavy bodies, and conversations exceeding 500 parts.

UAT Process

  1. Run the migration on a Gladly sandbox environment first — never migrate directly to production.
  2. Have 3–5 agents review their most recent 20 conversations in Gladly.
  3. Ask agents to search for specific customers by name and email and verify history completeness.
  4. Test that Gladly's routing, Topics, and Answers work as expected.
  5. Verify that Lookup Adaptor returns expected company/order data.
  6. Sign off before production cutover.

Rollback Planning

Gladly does not have a native "undo import" function. Your rollback plan:

  • Keep the Intercom workspace active (read-only) for at least 30 days post-migration
  • Archive all exported data (JSON/CSV) in versioned storage (S3 with versioning, GCS, etc.)
  • If the import needs to be undone, work with Gladly's support team to bulk-delete imported profiles
  • If you used a staging database for transformation, a rollback involves deleting generated Gladly customers via API and restarting the load phase
  • Your ID crosswalk file is critical for any rollback or re-run

Post-Migration Tasks

Rebuild Automations

Intercom's workflows, assignment rules, and bot configurations do not transfer. In Gladly, rebuild:

  • Routing rules based on channel, topic, and customer attributes
  • SLA policies with Gladly's native SLA engine
  • Answers (self-service content) to replace Intercom's Help Center articles and macros
  • Sidekick configuration for AI-assisted responses (Sidekick trains on native Gladly data, not imported history — expect 2–4 weeks before AI suggestions reach useful quality)

Update Integrations

Point any external systems (Shopify, Stripe, Salesforce) to Gladly's webhooks instead of Intercom's. If you need live account, order, or CRM context in the agent UI, implement a Lookup Adaptor rather than overloading the conversation history. Gladly supports webhooks for conversation events, customer profile updates, and task lifecycle changes.

Agent Training

  • Brief agents on Gladly's conversation-based UI (no ticket numbers, single customer timeline)
  • Show agents where to find imported history vs. new native conversations
  • Explain that attachments from Intercom are in an external archive, not inline in Gladly
  • Provide a "day one" quick-reference card covering Topics, Answers, routing, and how to use Sidekick

Monitor for 2 Weeks

  • Watch for duplicate customer profiles (caused by contacts reaching out via a different email post-migration)
  • Monitor Gladly's error logs for any API failures during delta imports
  • Track agent productivity metrics — expect a 1–2 week ramp-up period
  • Verify Sidekick suggestion quality is improving as native data accumulates

Best Practices

  1. Back up everything before migration. Export all Intercom data to JSON and store in versioned cloud storage (S3 with versioning enabled). You cannot re-extract after the Intercom workspace is decommissioned.
  2. Run test migrations. Always run at least two test imports on a Gladly sandbox before production. Start with a 5% data sample, then a 25% sample.
  3. Strip HTML early. Don't wait for Gladly to truncate your content silently. Strip HTML tags and check character counts during the transformation phase.
  4. Validate emails aggressively. Every record without a valid email is rejected. Clean your contact list in Intercom before export. Use regex validation and optionally MX record checks.
  5. Communicate the attachment gap. Tell agents and stakeholders before go-live that historical attachments live in an archive, not in Gladly. Provide the archive URL in agent documentation.
  6. Use incremental delta syncs. Do the bulk historical migration over a weekend, then run a delta sync script to catch conversations created in Intercom during the migration window. Gladly recommends the delta import 2 days after launch.
  7. Log everything. Your script must log every failed API call with the exact payload, response status code, and response headers. You will need this for cleanup.
  8. Keep Intercom read-only for 30 days. Don't decommission the Intercom workspace until you've validated the migration and completed UAT.
  9. Maintain an ID crosswalk. Map Intercom Contact/Conversation IDs to Gladly Customer/Item IDs for rollback, delta runs, and post-migration troubleshooting. Store as a JSON or CSV file alongside your migration artifacts.
  10. Download attachments during extraction, not after. Intercom attachment URLs are not permanent. If you plan to archive attachments, download them during the extraction phase.

Sample Data Mapping Table

Intercom Field Type Gladly Field Type Notes
contact.email String customer.emails [].original String Required — reject if missing
contact.name String customer.name String Direct map
contact.phone String customer.phones [].original String Format to E.164
contact.external_id String customer.externalCustomerId String Direct map; preserve for delta syncs
contact.created_at Unix timestamp customer.createdAt ISO 8601 Transform
contact.custom_attributes.* Varies customer.customAttributes.* String/Number/Boolean/Date Validate type compatibility
conversation.id String N/A (timeline-based) Store as reference in notes or custom attribute; maintain in crosswalk
conversation_part.body HTML conversationItem.content Text Strip HTML, truncate at 25K chars
conversation_part.created_at Unix conversationItem.timestamp ISO 8601 Transform
conversation_part.author.type Enum conversationItem.direction Enum user → INBOUND, admin → OUTBOUND
conversation_part.author.name String conversationItem.agentName String Map for OUTBOUND items
conversation_part.attachments Array N/A Archive to S3/GCS externally; add reference link in body
tag.name String topic.name String Create Topics pre-import
article.title / article.body String/HTML answer.title / answer.bodyHtml String/HTML Reformat for Gladly Answers

When to Call In Help

If any of these apply to your migration, consider a managed service over DIY:

  • More than 200K conversation items
  • Multiple Intercom workspaces consolidating into one Gladly instance
  • GDPR data-residency requirements (Intercom has US, EU, and AU regions; Gladly is US-based — confirm DPA coverage)
  • Active Intercom Tickets that need to become actionable Tasks in Gladly
  • Attachments or voice recordings that need to be archived with reference links in Gladly
  • Zero-downtime requirement with a delta sync window
  • Long threads exceeding the 500-part API limit
  • Heavy company modeling with many-to-many relationships
  • Compliance requirements demanding auditable, replayable migration runs

Frequently Asked Questions

Can I migrate attachments from Intercom to Gladly?
No. Gladly's historical import is text-only. It does not support images, recordings, attachments, or file uploads. Archive attachments externally (e.g., S3 or a shared drive) and add reference links in the imported conversation item body so agents can access them.
How long does an Intercom to Gladly migration take?
A typical migration of 50,000–200,000 conversation items takes 2–4 weeks end-to-end, including planning, test imports, production migration, and validation. Smaller datasets can be completed in under a week. The timeline depends on data volume, complexity, and how quickly you can validate test imports.
Does Gladly support Intercom custom objects?
No. Gladly does not have a custom objects feature. You can flatten custom object data into custom attributes on the Customer Profile, or maintain the data in an external system and surface it in Gladly via a Lookup Adaptor integration.
What happens to Intercom leads without an email address during migration?
They are rejected. Gladly requires a valid email address for every imported record. Intercom Leads created by anonymous Messenger visitors often lack email addresses. Before migration, either enrich those leads with an email from your CRM or accept that they will not be imported.
Will imported Intercom history work with Gladly's AI (Sidekick)?
No. Imported historical conversation data does not feed Gladly Sidekick or AI-powered answer suggestions. Sidekick operates on native Gladly data and configured Answers only. Expect a ramp-up period where AI suggestions improve as new native data accumulates.

More from our Blog

How to Export Data from Intercom: Methods, API Limits & Transcripts
Intercom

How to Export Data from Intercom: Methods, API Limits & Transcripts

A complete guide to exporting all Intercom data — conversations, transcripts, attachments, and Help Center articles — using the Intercom UI, REST API, and cloud storage exports. Covers authentication, pagination, rate limits, attachment handling, incremental exports, security, and troubleshooting.

Roopi Roopi · · 23 min read
Gladly Migration Checklist
Checklist/Gladly

Gladly Migration Checklist

Moving to Gladly? Use our migration checklist to transition from a ticket-based system to a people-centered model. Learn how to map customer timelines and API data safely.

Tejas Mondeeri Tejas Mondeeri · · 9 min read