Skip to content

How to Migrate Intercom to HubSpot Service Hub (2026 Guide)

Step-by-step guide to migrating from Intercom to HubSpot Service Hub. Covers API rate limits, data mapping, inline image risks, and realistic timelines.

Abdul Abdul · · 22 min read
How to Migrate Intercom to HubSpot Service Hub (2026 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

What Is an Intercom to HubSpot Service Hub Migration?

An Intercom to HubSpot Service Hub migration moves your customer support operation from a messenger-first, conversation-centric platform to a CRM-integrated, ticket-pipeline platform. You are shifting from Intercom's model — where every interaction is a Conversation with threaded Conversation Parts — to HubSpot's model, where support lives inside the Help Desk workspace as Tickets associated with Contacts, Companies, and engagement records.

Teams make this move for specific, platform-level reasons:

  • CRM unification. HubSpot's Service Hub shares a single CRM database with Sales Hub and Marketing Hub. Teams consolidating from Intercom plus a separate CRM (e.g., Salesforce, Pipedrive) eliminate a data silo and a sync layer. For a deeper comparison, see our Intercom vs HubSpot Service Hub operations guide.
  • Ticket pipeline visibility. HubSpot's pipeline-and-stage model gives operations leads Kanban-style visibility into ticket status, SLA compliance, and queue health — a structure Intercom's conversation list view does not natively offer.
  • Workflow automation depth. HubSpot Workflows can trigger cross-object actions (e.g., update a Deal when a Ticket closes, enroll a Contact in a marketing sequence after resolution). Intercom's Workflows operate within the messaging context but don't natively reach CRM deal or marketing objects.
  • Knowledge Base and Customer Portal. HubSpot bundles a branded customer portal where end users can track ticket status — a feature Intercom lacks natively.

Why this migration is non-trivial

Three architectural differences make an Intercom to HubSpot Service Hub migration harder than it looks:

  1. Conversation Parts vs. Engagements. Intercom stores every message, note, and action as a conversation_part inside a single Conversation object. HubSpot stores communication as separate Engagement objects (emails, notes, calls) associated with a Ticket via the Associations API. You must decompose each Intercom Conversation into one Ticket plus N engagement records.

  2. Flat tags vs. structured pipelines. Intercom uses Tags and custom attributes for categorization. HubSpot uses Ticket Pipelines with defined Stages, plus custom properties. There is no automatic mapping — every tag must be translated into either a pipeline stage, a property value, or a HubSpot tag.

  3. History cap. Intercom's API returns only the 500 most recent conversation parts for a conversation, so very long threads need a fallback plan — either a legacy archive, exported text/PDF, or accepted truncation. (developers.intercom.com)

HubSpot Help Desk vs. legacy Tickets: which target?

HubSpot has been transitioning from its legacy Tickets experience to the new Help Desk workspace (generally available since mid-2024). This migration guide targets the Help Desk workspace, which is HubSpot's go-forward support interface. Key differences that affect migration:

  • Help Desk threads use the Conversations Inbox model. However, starting September 23, 2026, creating COMMENT messages via the Conversations API will be blocked for Help Desk threads — making the Notes API the safer archival target for historical Intercom transcripts. (developers.hubspot.com)
  • Help Desk uses the same underlying tickets CRM object and the same API endpoints (/crm/v3/objects/tickets). Your migration code does not change based on whether agents use the legacy ticket view or the Help Desk workspace.
  • SLA configuration, routing rules, and team assignment are configured within Help Desk settings, not at the API level. These must be set up manually before go-live.

If your HubSpot instance still uses the legacy ticket experience, the migration process is identical at the API layer — only the agent-facing UI differs.

How Does Intercom Data Map to HubSpot Service Hub?

The cleanest target model is one HubSpot ticket per Intercom conversation, with message history stored as HubSpot engagements (notes or emails) linked to that ticket. Do not try to stuff full transcripts into one ticket body.

Intercom Object HubSpot Service Hub Object Notes / Caveats
Contact (User/Lead) Contact Map on email as the dedup key. Intercom distinguishes Users vs. Leads by role; HubSpot uses Lifecycle Stage. Contacts without emails create orphaned records — decide in advance whether to skip, assign a placeholder, or merge them.
Company Company Use company_id or domain for matching. Intercom allows multiple companies per contact; HubSpot supports this via Associations. Intercom's CSV user export only includes the most recent company name and ID — use API data when relationships matter. (intercom.com)
Conversation Ticket + Engagements One Conversation becomes one Ticket. Individual messages (Conversation Parts) become Engagements (Notes or Emails) tied to the Ticket. Pipeline and Stage must be explicitly set. Intercom returns only the 500 most recent parts per conversation. (developers.intercom.com)
Conversation Parts Engagements (Notes / Emails) Each Part becomes a Note or Email engagement associated with the Ticket. Preserve created_at timestamps as hs_timestamp. Internal notes must map to HubSpot Note engagements — not external emails — to prevent accidental customer exposure.
Admins / Teammates Users / Owners Map by email. Intercom Admin IDs do not transfer. HubSpot Users must be created and active before migration so historical tickets can be assigned to them. If no matching HubSpot owner exists, store the Intercom assignee in a custom text field.
Teams Teams Manual setup in HubSpot first; then assign Ticket ownership by team.
Tags Custom Properties No native tag object on HubSpot tickets. Map to a multi-select custom property or single-select category depending on your reporting needs.
Articles (Help Center) Knowledge Base Articles HTML formatting requires deep cleaning. Intercom's proprietary blocks (buttons, dividers) often break in HubSpot's editor. No bulk API import for HubSpot KB articles — must be recreated via custom script against the CMS API.
Custom Attributes Custom Properties Create matching properties in HubSpot before import. Data type mapping: Intercom string → HubSpot single-line text, integer/floatnumber, booleancheckbox, datedate, listdropdown/multi-select.
Intercom Tickets Tickets (separate pipeline) Intercom's newer Tickets feature maps more directly to HubSpot Tickets, but Ticket Types become pipeline definitions and carry their own attributes.
Custom Objects Custom Objects (Enterprise only) Requires HubSpot Enterprise. Schema must be defined before migration.
Attachments Files + Note attachments Upload to HubSpot's File Manager, then attach to a note using hs_attachment_ids. File import from URL is async, and HubSpot does not support uploading multiple files in one request. (developers.hubspot.com)
CSAT Scores Custom Property Intercom's native CSAT ratings do not map to HubSpot's standard CSAT object. Create a custom property on the Ticket (e.g., "Legacy Intercom CSAT") to store historical data.

Field-level mapping: the details that matter

[For engineering] Intercom custom attributes live under custom_attributes as a flat key-value object. HubSpot custom properties require pre-creation via the Properties API (POST /crm/v3/properties/tickets) with explicit type, fieldType, and groupName. Intercom's list data type (enum/picklist) requires you to define options in HubSpot before importing values — any value not in the predefined option list will be silently rejected.

Timestamp conversion is the most common single-field failure. Intercom timestamps are Unix epoch in seconds. HubSpot expects Unix epoch in milliseconds. Failing to multiply by 1,000 causes date corruption across every migrated record. When this error occurs, HubSpot will accept the value without error — tickets will appear to have been created in January 1970.

Warning

Do not map one Intercom thread to one giant HubSpot note. HubSpot notes cap hs_note_body at 65,536 characters. The durable pattern is one ticket per conversation and one note per message part — or chunked across multiple notes for long parts. (developers.hubspot.com)

What has no clean equivalent in HubSpot

Intercom Product Tours, Outbound Messages (Series), Messenger Apps, Fin AI agent configuration, and Workflow automation logic cannot be migrated programmatically. These must be rebuilt manually in HubSpot using Workflows, Chatflows, and Breeze Customer Agent. Intercom Segments based on behavioral events do not transfer — you'll need to recreate them as HubSpot Active Lists. Behavioral events from Intercom's track API require Custom Behavioral Events (HubSpot Enterprise only).

Which Migration Approach Should You Use?

Approach How It Works Best For Key Risk Complexity Estimated Cost
Native CSV Export/Import Export contacts/companies from Intercom as CSV → import via HubSpot's native importer Contact-only migrations, <10K records Intercom conversation CSV exports are reporting metadata only — no full message content. Cannot import multi-object associations. (intercom.com) Low $0 (internal time only: ~8–16 hours)
Custom API Scripts Build an ETL pipeline using Intercom REST API (extract) + HubSpot CRM API (load) Full migrations with engineering capacity (4–6 weeks available) Rate limit management, broken relationships if order-of-operations is wrong, file re-hosting complexity High $8K–$25K in engineering time (80–250 hours at $100–$150/hr loaded cost)
Third-Party Tools (e.g., Help Desk Migration) Automated mapping tool that connects both platforms via UI Mid-size teams wanting speed, <10K conversations, minimal custom fields Limited custom field flexibility; inline image handling varies; typically charge per record ($0.50–$5 per ticket depending on volume). See our ClonePartner vs SyncMatters comparison for a detailed technical breakdown. Medium $500–$10K depending on record count
Managed Migration Service Engineer-led team handles extraction, transformation, loading, and validation Teams without spare engineering bandwidth or with complex data, >50K conversations Cost — but eliminates re-migration risk. Re-migrations typically cost 2–3x the original effort. Low (for you) $3K–$20K+ depending on volume and complexity

Clear picks:

  • Small business (<5K conversations, no custom objects): Native CSV for contacts plus a third-party tool for conversations.
  • Mid-market (10K–100K conversations, some custom attributes): Custom API scripts if you have the bandwidth. Managed service if you don't.
  • Enterprise (>100K conversations, custom objects, heavy attachments): Custom API scripts or a managed service. CSV imports will fail at this scale.
  • Ongoing sync needed: None of these approaches natively support continuous sync. You'll need a middleware layer (custom webhook handler or integration platform) for real-time data flow during a parallel-run period.

Two native export paths are commonly overestimated. Intercom's dataset CSV export does not include conversation or message content, and Intercom's content export API does not export raw message or conversation content. If you need readable history in HubSpot, start with the API. See How to Export Data from Intercom for a full breakdown of what each export method actually gives you.

How Do API Rate Limits Affect Migration Speed?

Intercom extraction limits

Intercom's API rate limit is 10,000 requests per minute per app, but this is enforced in 10-second rolling windows. Your effective ceiling is approximately 1,666 requests per 10-second window. Naive scripts that burst 2,000+ requests in a few seconds will hit 429 Too Many Requests errors even though they're under the per-minute limit. You must implement a token bucket algorithm to pace extraction. (developers.intercom.com)

API version matters. Use Intercom-Version: 2.11 or later for the conversation endpoints referenced in this guide. Earlier versions (1.x, 2.0–2.10) have different pagination models and response shapes. The 2.11+ versions use cursor-based pagination with starting_after parameters. Version 2.13 is the latest stable version as of early 2026. Set the version explicitly in your request headers — Intercom will default to the oldest version associated with your app if you omit it. (developers.intercom.com)

The critical bottleneck is the N+1 conversation pattern: listing conversations via GET /conversations returns metadata only — no message bodies. You must call GET /conversations/{id} for each conversation individually to retrieve Conversation Parts. For a workspace with 100K conversations, that's 100K+ additional API calls just for content extraction.

Extraction throughput math:

  • ~1,666 requests per 10 seconds = ~10,000/min
  • Each conversation requires 1 list call (amortized) + 1 detail call ≈ 2 calls
  • Effective throughput: ~5,000 conversations per minute with full parts
  • 100K conversations ≈ 20 minutes for metadata + parts (excluding attachments and pagination overhead)
  • With attachment downloads, the real number is closer to 45–90 minutes for 100K conversations

HubSpot loading limits

HubSpot's burst limit for private apps is 100 requests per 10 seconds on Free/Starter and 190 requests per 10 seconds on Professional/Enterprise. The daily cap is 500,000 requests for Professional and 1,000,000 for Enterprise.

The CRM Search API is capped at 5 requests per second across all object types — this becomes your primary bottleneck if you're deduplicating contacts during import. Search results are capped at 10,000 per query, and search is not guaranteed to reflect new writes immediately (indexing lag can be 30 seconds to several minutes). (developers.hubspot.com)

HubSpot's batch endpoints accept up to 100 records per call. A single batch create request counts as 1 API call against your burst limit. This is where you recover throughput.

Tip

Do not do one HubSpot search per Intercom record. At 5 searches/second, 100,000 lookups take about 5.6 hours before you create a single ticket. Preload contact, company, and owner lookup tables once, then write by ID. Always use batch endpoints (POST /crm/v3/objects/tickets/batch/create) instead of single-record creates — this alone can increase effective throughput by 50–100x.

Loading throughput math:

  • 190 requests per 10 seconds → ~1,140 requests/min (Professional/Enterprise)
  • Using batch endpoints (100 records/call): ~114,000 records/min theoretical max
  • Real-world throughput (including associations, retries, and error handling): 30,000–50,000 records per hour
  • 100K tickets + 300K–500K conversation parts ≈ 12–20 hours of loading time

Common API error responses and what they mean

HTTP Status Response Body (typical) Cause Fix
429 {"type": "error.list", "errors": [{"code": "rate_limit", "message": "You have exceeded the rate limit"}]} Burst limit exceeded within 10-second window Implement exponential backoff; read X-RateLimit-Reset header for retry timing
422 {"status": "error", "message": "Property values were not valid", "validationResults": [...]} HubSpot property value not in predefined option list, wrong data type, or missing required field Check validationResults array for specific field; pre-validate enum values
409 {"status": "error", "message": "Contact already exists"} Duplicate contact by email Use POST /crm/v3/objects/contacts/batch/upsert with idProperty: "email" instead of batch create
404 {"status": "error", "message": "Object not found"} Association target (contact/company) doesn't exist yet Enforce load order: Companies → Contacts → Tickets → Engagements → Associations

Step-by-Step Intercom to HubSpot Service Hub Migration Process

The order of operations dictates success. Creating tickets before contacts exist in HubSpot will produce orphaned records with no associations.

Step 1: Pre-migration setup

  • Create all custom properties in HubSpot (tickets, contacts, companies) via the Properties API. Freeze enum values before loading data.
  • Set up ticket pipelines and stages to match your Intercom workflow.
  • Add a unique source-ID field (e.g., source_intercom_conversation_id) for idempotent reruns.
  • Map Intercom Admin emails to HubSpot User emails. Create all HubSpot users first.
  • Provision a HubSpot sandbox environment for test migrations. Sandboxes are available on Professional and Enterprise plans via Settings → Account Defaults → Sandboxes. Sandbox API limits match your production tier. Sandbox data does not sync to production — you'll need to run the migration separately against your production portal. Test migrations in sandbox first; validate; then run against production.
  • Disable all HubSpot Workflows that trigger on ticket creation, contact creation, or property updates. Migrated records will fire every active workflow — this is one of the most common causes of accidental email blasts during migration. See how automations sabotage migrations.
  • Review GDPR and data compliance requirements. If you process EU customer data, confirm that your HubSpot data center region matches your compliance needs (HubSpot offers EU and US data hosting). Ensure your Data Processing Agreement (DPA) with HubSpot is signed before transferring personal data. If your migration involves a third-party tool or managed service, they become a data sub-processor — verify they have appropriate DPAs and SOC 2 compliance. Document the legal basis for transferring historical customer data (typically "legitimate interest" under GDPR Article 6(1)(f) for maintaining customer service continuity).

Step 2: Extract reference data from Intercom

Pull contacts, companies, admins, tags, data attributes, and ticket types before you touch conversations. This gives you stable lookup tables and enum mappings.

  • Use GET /contacts with cursor-based pagination (default 50 per page). Merged contacts disappear from list results except for the surviving target record — store canonical source IDs early. (developers.intercom.com)
  • For companies with >10,000 records, use the Scroll API (GET /companies/scroll). Only one scroll can be open per app at a time — if it's idle for 1 minute, it expires.
  • Export tags via GET /tags.
  • Pull HubSpot owners and property metadata before mapping assignees or enums.

Step 3: Load contacts and companies into HubSpot

Load companies first (POST /crm/v3/objects/companies/batch/create), then contacts (POST /crm/v3/objects/contacts/batch/create), associating contacts to companies using the primary domain. Every later ticket and engagement association depends on these IDs existing.

Step 4: Extract conversations from Intercom

Paginate through Intercom's /conversations endpoint. For each conversation, fetch full details including Conversation Parts via GET /conversations/{id}. Store the raw JSON payloads locally or in a staging database.

# Production-grade Intercom conversation extraction
# Requires: pip install requests
# Uses Intercom API version 2.11+ with cursor-based pagination
 
import requests
import time
import json
import logging
from pathlib import Path
 
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
 
INTERCOM_TOKEN = "YOUR_INTERCOM_TOKEN"
API_VERSION = "2.11"  # Use 2.11+ for cursor-based pagination; 2.13 is latest stable
OUTPUT_DIR = Path("intercom_export")
OUTPUT_DIR.mkdir(exist_ok=True)
 
HEADERS = {
    "Authorization": f"Bearer {INTERCOM_TOKEN}",
    "Intercom-Version": API_VERSION,
    "Accept": "application/json"
}
 
# Token bucket: 1,666 requests per 10-second window
REQUEST_WINDOW = 10  # seconds
MAX_REQUESTS_PER_WINDOW = 1600  # leave margin below 1,666
request_timestamps = []
 
 
def throttle():
    """Enforce 10-second rolling window rate limit."""
    now = time.time()
    # Remove timestamps older than the window
    while request_timestamps and request_timestamps[0] < now - REQUEST_WINDOW:
        request_timestamps.pop(0)
    if len(request_timestamps) >= MAX_REQUESTS_PER_WINDOW:
        sleep_time = REQUEST_WINDOW - (now - request_timestamps[0]) + 0.1
        logger.info(f"Rate limit approaching, sleeping {sleep_time:.1f}s")
        time.sleep(sleep_time)
    request_timestamps.append(time.time())
 
 
def api_get(url, params=None, max_retries=5):
    """GET with rate limiting, retry on 429/5xx, exponential backoff."""
    for attempt in range(max_retries):
        throttle()
        resp = requests.get(url, headers=HEADERS, params=params)
        if resp.status_code == 200:
            return resp.json()
        elif resp.status_code == 429:
            retry_after = int(resp.headers.get("X-RateLimit-Reset", 10))
            logger.warning(f"429 rate limited. Retrying after {retry_after}s")
            time.sleep(retry_after)
        elif resp.status_code >= 500:
            wait = (2 ** attempt) + 0.5  # exponential backoff with jitter
            logger.warning(f"{resp.status_code} server error. Retry in {wait:.1f}s")
            time.sleep(wait)
        else:
            logger.error(f"Unexpected {resp.status_code}: {resp.text}")
            resp.raise_for_status()
    raise RuntimeError(f"Max retries exceeded for {url}")
 
 
def extract_all_conversations():
    """Extract all conversations with full conversation parts."""
    conversations = []
    params = {"per_page": 50}  # max 150 per page for list endpoint
    url = "https://api.intercom.io/conversations"
    page_count = 0
 
    while True:
        page_count += 1
        data = api_get(url, params=params)
        logger.info(f"Page {page_count}: {len(data.get('conversations', []))} conversations")
 
        for convo_summary in data.get("conversations", []):
            convo_id = convo_summary["id"]
 
            # N+1: Fetch full conversation with parts
            detail = api_get(f"https://api.intercom.io/conversations/{convo_id}")
 
            # Check for 500-part truncation
            parts = detail.get("conversation_parts", {})
            total_parts = parts.get("total_count", 0)
            returned_parts = len(parts.get("conversation_parts", []))
            if total_parts > 500:
                logger.warning(
                    f"Conversation {convo_id}: {total_parts} total parts, "
                    f"only {returned_parts} returned (API 500-part limit)"
                )
 
            conversations.append(detail)
 
            # Persist incrementally to avoid data loss on crash
            output_file = OUTPUT_DIR / f"conversation_{convo_id}.json"
            with open(output_file, "w") as f:
                json.dump(detail, f)
 
        # Cursor-based pagination
        pages = data.get("pages", {})
        next_cursor = pages.get("next", {}).get("starting_after") if isinstance(pages.get("next"), dict) else None
        if not next_cursor:
            break
        params = {"per_page": 50, "starting_after": next_cursor}
 
    logger.info(f"Extraction complete: {len(conversations)} conversations")
    return conversations
 
 
if __name__ == "__main__":
    conversations = extract_all_conversations()
    logger.info(f"Total conversations extracted: {len(conversations)}")

Step 5: Transform and map

  • Convert Intercom timestamps (seconds) → HubSpot timestamps (milliseconds): multiply by 1,000.
  • Map Intercom role (user/lead) → HubSpot Lifecycle Stage.
  • Convert Conversation Parts → engagement objects (type: NOTE for internal notes, EMAIL for customer-facing messages).
  • Download all inline images from Intercom's CDN (*.intercom-attachments*.com) and upload to HubSpot's File Manager API. Replace image URLs in conversation HTML. File import from URL is async — build your queue accordingly. Use GET to poll the import status until complete before referencing the file in note bodies.
  • Map tags → custom property values.
  • Split long conversation parts across multiple notes if they exceed 65,536 characters.

Step 6: Load tickets and engagements into HubSpot

Load in this exact sequence:

  1. TicketsPOST /crm/v3/objects/tickets/batch/create — set pipeline, stage, owner, and custom properties.
  2. Engagements (Notes/Emails) → POST /crm/v3/objects/notes/batch/create — one per Conversation Part, preserving hs_timestamp.
  3. Associations → Use HubSpot's Associations API v4 to link Engagements to Tickets, Tickets to Contacts, and Tickets to Companies.
  4. File attachments → Upload downloaded files to HubSpot File Manager, then attach to notes using hs_attachment_ids.

Use idempotent source keys and objectWriteTraceId values on batch writes so partial failures can be replayed safely. Retry 429 and 5xx responses with exponential backoff tied to rate limit headers. Throttle HubSpot Search separately — search indexing can lag behind object writes by 30 seconds to several minutes. (developers.intercom.com)

Danger

Order-of-operations failure mode: If you load tickets before contacts, HubSpot will create the tickets but association calls will fail silently or throw 404 errors (response: {"status": "error", "message": "Object not found"}). You'll end up with thousands of orphaned tickets that require manual cleanup or a second migration pass.

Step 7: Validate, delta sync, and cut over

  • Run record-count reconciliation between Intercom and HubSpot.
  • Sample 5% of records and verify field-level accuracy (timestamps, custom properties, tag values, assignees).
  • Confirm all Conversation Parts appear as engagements on the correct ticket in chronological order.
  • Verify inline images render correctly in the HubSpot ticket view.
  • Have 2–3 agents from the support team review real migrated tickets and simulate customer follow-ups.

After validation passes, run a final delta sync for any conversations created in Intercom during the historical load. Keep Intercom live until the delta is loaded and the team has signed off.

How Long Does an Intercom to HubSpot Service Hub Migration Take?

[For PMs] For most mid-market teams, plan for 3–8 weeks end to end. Small, clean workspaces can move faster. Workspaces with attachment archives, custom ticket types, or multiple inboxes land toward the high end.

Phase Duration What Must Be True to Exit
Scoping and field mapping 2–4 days Scope, objects, owners, and destination properties agreed
Custom property creation and pipeline setup 1–2 days Mapping document finalized
Script development and testing 5–7 days Engineering resource allocated, extraction/load pipeline built
Test migration (subset) 1–2 days Sample data loaded into HubSpot sandbox, mapping bugs identified
Validation and UAT 2–3 days CS team reviews sample tickets, field-level checks pass
Production migration 1–3 days Full historical data loaded (200K conversations ≈ 1–2 days of API transfer)
Post-migration validation 1–2 days Record counts, field checks, image verification
Delta sync and go-live 1–2 days Final delta run, routing switched, agents operating in HubSpot
Total 3–8 weeks Depends on volume, custom field complexity, and attachment load

The biggest schedule risk is not the data transfer — it's scope creep during field mapping. Every custom attribute, every tag, and every workflow rule needs a decision. Budget 30% more time for mapping than you think you need.

We recommend a "Big Bang" cutover preceded by multiple delta syncs, rather than an incremental phase-out, to avoid agents working across two systems simultaneously.

Rollback plan

HubSpot does not have a one-click "undo migration" feature. Your rollback strategy:

  1. Keep Intercom active (read-only) for at least 30 days after go-live.
  2. Export a backup of HubSpot before migration starts (use HubSpot's native CRM export or the API).
  3. If migration fails validation, batch-delete migrated records in HubSpot using the source_intercom_conversation_id field to identify migrated records, then re-run.

Risk register

Risk Likelihood Mitigation
Inline images break after Intercom deactivation High Download and re-host all CDN images during transformation
Tickets loaded without contact associations Medium Enforce load order: Companies → Contacts → Tickets → Engagements
HubSpot Workflows fire on migrated records High Disable all enrollment-based Workflows before migration
Rate limit throttling extends timeline Medium Implement exponential backoff with jitter; monitor X-RateLimit-Remaining headers
Duplicate contacts created Medium Deduplicate by email before loading; use batch upsert endpoint; preload lookup tables
Very long threads truncated Medium Flag conversations with >500 parts; plan archive or PDF fallback
GDPR non-compliance during data transfer Low-Medium Sign DPAs with all parties; confirm data center region; document legal basis
Timestamp corruption (seconds vs. milliseconds) Medium Unit test timestamp conversion; validate sample records show dates after 2010

What Do Customers and Agents Notice During Migration?

If executed correctly, customers should notice zero downtime. The migration runs against both APIs in the background — Intercom stays live for incoming conversations while historical data loads into HubSpot.

What changes for end users:

  • The chat widget changes from Intercom Messenger to HubSpot Live Chat or Chatflow. This is a visible change — communicate it to customers in advance.
  • Conversation history will be available in HubSpot as ticket engagements, but the visual format differs. Agents see a CRM activity timeline rather than Intercom's Messenger-style chat bubbles.
  • SLA timers reset. HubSpot SLAs are configured independently — historical SLA performance from Intercom does not carry over.

Why history preservation matters: When a returning customer contacts support, the agent needs to see prior conversations. In HubSpot, this history lives on the Contact record's activity timeline as associated engagements. Without a proper migration, agents lose all context — and customers repeat themselves.

Change management checklist:

  • Notify agents 2 weeks before go-live with training on HubSpot Help Desk workspace.
  • Send a customer-facing communication if the chat widget is changing.
  • Set up a #migration-issues Slack channel for agents to report data problems in the first 48 hours.
  • Assign a migration owner who has admin access to both platforms during the transition window.
  • Plan a "hypercare" period for the first two weeks post-cutover with daily standups to triage data issues.

What Data Cannot Be Migrated from Intercom to HubSpot Service Hub?

Be explicit with stakeholders about what won't transfer:

  • Intercom Workflows — HubSpot Workflows use a completely different trigger/action model. Rebuild manually. Expect 1–3 days of work for a typical 10–20 workflow setup.
  • Fin AI agent configuration — No export. Reconfigure Breeze Customer Agent from scratch.
  • Product Tours — No equivalent in HubSpot. Consider a third-party tool (e.g., Pendo, Appcues, Userpilot).
  • Series / Outbound Messages — Rebuild as HubSpot email sequences or Workflow-based emails.
  • Messenger Apps — Custom Messenger integrations don't transfer. Rebuild as HubSpot Chatflow actions.
  • Conversation ratings — Intercom's emoji-based rating system has no direct equivalent. Map to a custom property.
  • Behavioral events — Intercom Events (track API) do not map to HubSpot without Custom Behavioral Events (Enterprise only).
  • Inbox/team routing rules — HubSpot uses its own routing logic. Rebuild from scratch.

Edge cases that catch teams off guard

Inline images are the silent data-loss risk. Intercom hosts all uploaded images on its own CDN. If you migrate conversation HTML containing <img src="https://downloads.intercomcdn.com/..." /> URLs without downloading and re-uploading those images to HubSpot's File Manager, every image will return a 404 once your Intercom workspace is deactivated. This is permanent data loss. Download the files via API and re-upload them during transformation.

Warning

Inline image breakage is the #1 post-migration regret. When you cancel your Intercom contract, Intercom deletes the CDN links, resulting in broken images across all historical HubSpot tickets. The only fix at that point is a re-migration — if you still have access to the data.

Merged contacts. Intercom merged contacts disappear from list results except for the surviving target record. Build dedupe around canonical source IDs, not whatever happened to be exported last. (developers.intercom.com)

Very long threads. Intercom's API returns only the 500 most recent conversation parts. The total_count field in the response tells you how many parts exist. Flag these records early and decide whether to keep a legacy archive, use exported text/PDF, or accept truncation. (developers.intercom.com)

Source shutdown timing. Do not cancel Intercom too early. Intercom's Conversations API is only available to apps with a trial or active subscription. Keep Intercom active (at minimum read-only) for at least 30 days after go-live. (developers.intercom.com)

Attachment-heavy history. HubSpot file imports from URL are async, and HubSpot does not support multiple file uploads in one request. Attachment queues need their own throughput plan — budget approximately 1–2 seconds per file for the upload-and-poll cycle. (developers.hubspot.com)

HubSpot Enterprise requirement. HubSpot Service Hub Professional is sufficient for most migrations. Enterprise is only required if you need Custom Objects (to map Intercom Custom Objects) or Custom Behavioral Events (to map Intercom Events). The ticket, contact, company, and engagement APIs are available on Professional and above.

Validation and Testing Checklist

Never assume an HTTP 200 OK response means the data is correct. HubSpot will accept records with silently rejected property values, wrong timestamps, and missing associations.

Check Method Pass Criteria
Total ticket count Compare Intercom conversation count vs. HubSpot ticket count ±0 difference
Contact count Compare Intercom contact export vs. HubSpot contact count ±0 (excluding intentionally skipped records)
Company count Compare Intercom company count vs. HubSpot company count ±0
Field-level accuracy Sample 50 random tickets — verify custom properties, timestamps, assignees 100% match
Timestamp sanity check Verify all hs_timestamp and createdate values fall within expected date range (e.g., 2018–2026) 0 records with dates before 2000 (indicates seconds-not-milliseconds bug)
Conversation threading Open 20 tickets in HubSpot — confirm all Parts appear as ordered engagements Correct chronological order
Inline images Open 10 tickets with known images — confirm images render 100% render
Associations Verify tickets linked to correct Contact and Company Spot-check 30 records
Tag/property mapping Verify all Intercom tags appear as correct HubSpot property values 100% match
Truncation check Verify conversations flagged as >500 parts have appropriate fallback (archive, PDF, or documented truncation) All flagged conversations accounted for

Do not use one large HubSpot search query as your only validation method. Search is capped at 10,000 results per query and limited to 5 requests per second. Use returned record IDs, direct object reads, or exports for final reconciliation. HubSpot's Associations API can read up to 1,000 record IDs in a single request, which makes ticket-to-contact and ticket-to-company validation faster than manual sampling. (developers.hubspot.com)

UAT should involve 2–3 agents from the support team reviewing real tickets in HubSpot. Ask them to simulate a customer follow-up on a migrated ticket — this catches broken associations and missing context faster than automated checks. Select tickets across different statuses (open, closed, snoozed) and edge cases (merged contacts, long threads, attachment-heavy tickets). For a full post-load checklist, see Post-Migration QA.

Build In-House or Use a Managed Migration Service?

Build in-house when:

  • You have a dedicated engineer with experience in both the Intercom and HubSpot APIs.
  • Your dataset is relatively clean (<50K conversations, minimal custom objects).
  • You need full control over transformation logic.
  • You have 4–6 weeks of engineering bandwidth to allocate (80–250 hours).
  • You can afford a re-run if the first attempt has issues.

Use a managed service when:

  • Your engineering team is already at capacity on product work.
  • You have >100K conversations, complex custom attributes, or heavy attachment/inline image volumes.
  • You cannot afford a failed migration — re-migrations typically cost 2–3x the original effort in both time and money.
  • You need the migration completed with guaranteed data fidelity and zero downtime.
  • You have GDPR or compliance requirements that demand a documented chain of custody for customer data.

The hidden cost of DIY is not writing the first script — it's the debugging. Broken associations, missing conversation parts, corrupted timestamps, and broken inline images don't surface until agents start working in HubSpot. By then, you may have already deactivated Intercom and lost your extraction window.

Frequently Asked Questions

How long does an Intercom to HubSpot Service Hub migration take?
A typical migration takes 3–8 weeks end-to-end, including scoping, field mapping, script development, test migration, and production run. The actual data transfer for 100K conversations takes 1–2 days of API processing time. Calendar time is driven by mapping decisions and validation, not data volume.
Can I migrate Intercom conversations to HubSpot without losing data?
Yes, but only via the API — not CSV imports. Native CSV imports cannot carry conversation content or multi-object associations. API-based migration preserves conversation parts as engagement records with timestamps and author attribution. Inline images require explicit download and re-hosting to avoid permanent breakage when Intercom is deactivated.
What data cannot be migrated from Intercom to HubSpot Service Hub?
Intercom Workflows, Fin AI configuration, Product Tours, Series, Messenger Apps, and behavioral events cannot be migrated programmatically. These must be manually rebuilt using HubSpot Workflows, Chatflows, and Breeze Customer Agent. Conversation ratings also have no direct equivalent and should be stored as custom properties.
Will my HubSpot Workflows fire during migration?
Yes. Any active Workflow with enrollment triggers like 'Ticket is created' or 'Contact property is updated' will fire on every migrated record, potentially sending thousands of unwanted emails. Always disable enrollment-based Workflows before starting the migration and re-enable them after validation.
Do I need HubSpot Enterprise to migrate from Intercom?
No. HubSpot Service Hub Professional is sufficient for most migrations. Enterprise is only required if you need Custom Objects (to map Intercom Custom Objects) or Custom Behavioral Events (to map Intercom Events). The ticket, contact, company, and engagement APIs are available on Professional and above.

More from our Blog

Your Help Desk Data Migration’s Secret Saboteur: Automations, Macros, and Workflows
Help Desk

Your Help Desk Data Migration’s Secret Saboteur: Automations, Macros, and Workflows

Migrating automations, macros, and workflows is the secret saboteur of most help desk migration projects. Standard tools often fail because they can't translate the complex, custom logic that acts as your support team's "central nervous system". This guide provides a battle-tested, 3-phase framework to successfully audit, "translate," and test these intricate workflows. Learn to deconstruct your old system and rebuild it perfectly in the new one, avoiding the common pitfalls that cause 83% of data migrations to fail.

Raaj Raaj · · 8 min read