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.
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
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.
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-RemainingandX-RateLimit-Resetheaders) - 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_countagainst 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
- Must migrate: Contacts with email, conversation history (last 12–24 months), tags, notes.
- Nice to have: Full conversation history, help center articles.
- Archive only: Events, custom objects, product tours, bot configurations.
- 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.
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_countagainst 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 viaGET /companies/{id}/contacts, then search conversations bycontact_ids.
Gladly API: Loading Details
- Rate limits: Gladly returns 429 errors for excessive requests. Monitor
X-RateLimit-RemainingandX-RateLimit-Resetresponse 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-Dataresponse 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 # SuccessFor 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,IntercomStep 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-Dataheader 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
- Run the migration on a Gladly sandbox environment first — never migrate directly to production.
- Have 3–5 agents review their most recent 20 conversations in Gladly.
- Ask agents to search for specific customers by name and email and verify history completeness.
- Test that Gladly's routing, Topics, and Answers work as expected.
- Verify that Lookup Adaptor returns expected company/order data.
- 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
- 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.
- 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.
- Strip HTML early. Don't wait for Gladly to truncate your content silently. Strip HTML tags and check character counts during the transformation phase.
- 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.
- 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.
- 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.
- 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.
- Keep Intercom read-only for 30 days. Don't decommission the Intercom workspace until you've validated the migration and completed UAT.
- 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.
- 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.



