Skip to content

Freshchat to Salesforce Service Cloud Migration: Technical Guide

A technical guide to migrating Freshchat conversations, messages, and contacts into Salesforce Service Cloud — covering data model mapping, API extraction, the conversation fidelity decision, and edge cases.

Nachi Nachi · · 20 min read
Freshchat to Salesforce Service Cloud Migration: 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: Freshchat to Salesforce Service Cloud migration requires API-based extraction, schema translation (conversations → Cases, messages → CaseComments or EmailMessages), and careful handling of rich chat formats that have no direct Salesforce equivalent. CSV-only methods work for small, shallow moves. For threaded history, files, ownership, and custom property fidelity, use an API/ETL pipeline or a managed migration service.

There is no native import path between Freshchat and Salesforce Service Cloud. This is a schema translation project — you are moving from a purpose-built messaging platform with conversations, messages, users, and channels into an enterprise CRM service layer where Case, Contact, Account, and EmailMessage objects follow strict relational hierarchies and governor limits. Every record must be extracted via the Freshchat REST API, transformed to fit Salesforce's schema, and loaded through Salesforce's Bulk API 2.0 or REST API. (developers.freshchat.com)

One important clarification: Freshchat's Account API is about Freshworks tenant configuration, not customer company records. Standalone Freshchat is not a full CRM. If your deployment is bundled with Freshsales Suite, CRM entities like companies, leads, or opportunities belong to a separate extraction workstream outside core Freshchat chat history.

This guide covers data model mapping, extraction strategies, API constraints on both sides, the conversation fidelity problem, edge cases, and validation — everything an engineering team needs to execute this migration without data loss. For a broader pre-flight checklist, see the Salesforce Service Cloud Migration Checklist. For sign-off testing, the Post-Migration QA Checklist is the companion document. For teams migrating from Freshdesk (the ticket-based product, not Freshchat), the Freshdesk to Salesforce Service Cloud guide covers that path.

Last updated: July 2025. Salesforce API examples use v67.0; adjust to your org's current API version.

Why Teams Migrate from Freshchat to Salesforce Service Cloud

Freshchat is Freshworks' messaging platform for support conversations across web chat, mobile, WhatsApp, Facebook Messenger, and email. Its public API exposes users, agents, groups, channels, conversations, messages, files, and raw reports. API access is available on the Pro and Enterprise plans only — Free and Growth plans do not provide REST API access, which makes programmatic extraction impossible on those tiers. (developers.freshchat.com)

Salesforce Service Cloud is an enterprise CRM-based service platform where the Case object is the central hub connecting Accounts, Contacts, Assets, Entitlements, and Knowledge. It provides case management, omni-channel routing, SLA enforcement, and deep analytics within the Salesforce ecosystem.

Teams typically make this move for one or more of these reasons:

  • Enterprise CRM consolidation: The organization standardizes on Salesforce across sales, marketing, and service. Running Freshchat alongside Salesforce creates a data silo that breaks 360-degree customer views.
  • Compliance and audit trails: Salesforce's EmailMessage records are read-only and immutable, creating compliance-grade audit trails that Freshchat's conversation model does not natively provide. Organizations with compliance requirements should also evaluate Salesforce Shield (Platform Encryption, Event Monitoring, Field Audit Trail) for encryption-at-rest and granular audit logging — these are add-on features not included in base Service Cloud licenses.
  • SLA enforcement: Salesforce Entitlements and Milestones offer built-in SLA tracking with automated breach notifications — capabilities that go beyond Freshchat's native SLA features. Note: Entitlements require Enterprise Edition or higher.
  • Scale and customization: Salesforce supports custom objects, complex Apex-driven automation, and Flow orchestration at enterprise scale.

Salesforce Edition Requirements

Not all Service Cloud features are available on every edition. This affects both migration target design and post-migration operations:

Feature Professional Enterprise Unlimited
Case Management
Bulk API 2.0
Omni-Channel Routing
Entitlements & Milestones
Einstein Bots Add-on
Salesforce Shield Add-on Add-on Add-on
Data Storage (base) 10 GB 10 GB Unlimited*
File Storage (base) 10 GB 10 GB Unlimited*

"Unlimited" still has per-org caps; contact Salesforce for specifics. Additional data storage is approximately $125/GB/month (list price) for Enterprise Edition — a significant cost factor when migrating millions of chat messages as individual records.

Data Model Mapping: Freshchat → Salesforce Service Cloud

This is the hardest part of the migration. Freshchat stores interactions as a continuous stream of messages within a conversation. Salesforce requires discrete, relational records. The schemas are fundamentally different.

Core Object Mapping

Freshchat Object Salesforce Service Cloud Object Notes
User (contact/visitor) Contact If no Account exists, create a default or derive from email domain.
Agent User (with Service Cloud license) Role and Profile assignment required. Deactivated agents need a mapping strategy.
Group Queue Freshchat groups map to Salesforce Queues for case routing.
Conversation Case Each conversation becomes a Case. Status mapping required.
Message CaseComment or EmailMessage or ConversationEntry The fidelity decision — see below.
Conversation Property Case Custom Field Freshchat supports up to 50 custom conversation properties. Each maps to a custom field on Case.
Channel Case Origin / custom field Web chat, WhatsApp, email map to Case Origin picklist values. A custom field is often cleaner.
CSAT Score Custom field on Case No native equivalent. Create a custom CSAT field.
Attachment ContentVersion / ContentDocumentLink Salesforce Files with ContentDocumentLink to the parent Case.

The Conversation Fidelity Decision

This is the single most consequential architectural choice in the migration. Freshchat conversations contain threaded, timestamped messages from users, agents, and bots with rich content (images, files, interactive elements). Salesforce offers three target models:

  1. CaseComment: Simplest. Plain text only, no HTML. Agent vs. customer distinction via the IsPublished flag. Loses rich formatting and inline attachments. Works for archival. CaseComments count as "related records" and consume less data storage than standalone objects.
  2. EmailMessage: Supports HTML body, From/To addresses, and attachments. Better fidelity. But EmailMessage is designed for email, not chat — you are shoehorning chat into an email model. If you want messages to appear in the Case email timeline, Email-to-Case must be enabled and ParentId must be set during loading; otherwise records can end up invisible to agents. (help.salesforce.com)
  3. ConversationEntry (enhanced Messaging): Part of Salesforce's newer Messaging object model (API v47.0+). Designed for chat-style interactions but primarily intended for Salesforce's own Messaging channels, not historical import from third-party systems.
Warning

Decision point: Most migrations land on CaseComment for historical archive (acceptable loss of rich formatting) or EmailMessage for higher fidelity (HTML body preserved, but semantically odd for chat data). There is no option that preserves Freshchat's exact interactive message format inside Salesforce. Salesforce retired Legacy Chat (LiveChatTranscript) on February 14, 2026 — do not target the deprecated LiveChatTranscript object.

Channel-Specific Transformation Rules

Freshchat supports multiple messaging channels, each with unique message formats that require specific handling:

Channel Message Format Transformation Approach
Web Chat message_parts with text, image, file types Flatten to plain text (CaseComment) or HTML (EmailMessage). Inline images → ContentVersion attachments.
WhatsApp Template messages with parameters, media messages, location pins, contact cards Template messages: extract parameter values and reconstruct as readable text (e.g., "Your order #{{1}} ships on {{2}}" → "Your order #12345 ships on Jan 5"). Media: download and attach as ContentVersion. Location: convert to "Lat: X, Lng: Y" text.
Facebook Messenger Rich cards, carousels, quick replies, persistent menus Cards: flatten to " [Card Title] [Subtitle] [URL]" format. Carousels: render each card sequentially. Quick replies: record as " [User selected: Option X]".
Apple Business Chat List pickers, time pickers, rich links Flatten interactive elements to descriptive text: " [Time picker: User selected 2:00 PM]". Preserve rich link URLs.
Email (via Freshchat) HTML body, standard attachments Most direct mapping to EmailMessage. Preserve HTML body. Map From/To headers.

Field-Level Mapping Reference

Freshchat Field Salesforce Field Type Notes
conversation_id Case.Freshchat_Conversation_ID__c Text (External ID) Store for deduplication and relationship linking
status (new/assigned/resolved) Case.Status Picklist Map to Salesforce picklist values deliberately
created_time Case.CreatedDate DateTime Requires "Set Audit Fields" permission via Bulk API
assigned_agent_id Case.OwnerId Lookup(User) Map agent UUID → Salesforce User ID
assigned_group_id Case.OwnerId (Queue) Lookup(Queue) If no agent assigned, route to Queue
channel_id Case.Origin Picklist Map channel name to Origin value
user.email Contact.Email Email Primary match key
user.id Contact.Freshchat_User_ID__c Text (External ID) Stable external key for relational mapping
user.reference_id / org_contact_id External ID or join key Text Useful for adjacent CRM joins
message.message_parts CaseComment.CommentBody TextArea (Long) Flatten rich message parts to plain text; strip HTML if needed
message.created_time CaseComment.CreatedDate DateTime Requires "Set Audit Fields" permission
message.actor_type CaseComment.IsPublished Boolean user → true (public), agent → false, or vice versa based on your convention

Salesforce supports both standard and custom objects, plus custom relationship fields. The real limitation is operational: every extra custom object adds permissions, page layouts, automation, and reporting overhead. Plan the target schema accordingly. (help.salesforce.com)

Migration Approaches Compared

Approach Best For Scalability Key Risk Complexity
CSV Export → Data Loader Small datasets, archival only Small No message-level fidelity; manual relationship mapping Low
API-to-API (custom scripts) Full-fidelity migration at any scale Enterprise Rate limits on both sides; engineering investment High
Custom ETL pipeline Enterprise programs needing repeatable loads Enterprise Highest build cost; most QA surface High
Middleware (Zapier, Make) Ongoing sync of new conversations Light sync only No historical backfill; per-task pricing Medium
Managed migration service Complex data, tight timelines, low eng bandwidth Any Vendor selection Low (for you)

CSV Export → Data Loader: Use Freshchat's Extract API (/v2/reports/raw/) to pull conversation metadata as CSV, then import into Salesforce via Data Loader. Salesforce's Data Import Wizard supports CSV only, up to 50,000 records, and a limited set of objects. (help.salesforce.com) This loses individual message threading and attachment fidelity. Suitable only for archival where full conversation replay is not needed.

API-to-API (custom scripts): Full control, full fidelity. Engineers write scripts (Python, Node.js) to extract data from Freshchat REST APIs, transform JSON payloads, and load into Salesforce using Bulk API 2.0. Requires engineering time, rate-limit management on both sides, and retry logic. The right choice when you have a dev team and need message-level accuracy.

Custom ETL pipeline: Land Freshchat JSON in a staging store (PostgreSQL, S3), normalize IDs, map statuses and custom fields, then upsert ordered batches into Salesforce using external IDs. Maximum control and observability, but the highest build cost. This approach works well when the pipeline needs to become an ongoing sync foundation. For real-time delta sync during coexistence, consider Salesforce Change Data Capture or Platform Events to propagate changes bidirectionally.

Middleware (Zapier, Make): Low-code platforms can capture new Freshchat conversations and push them to Salesforce in real time. They do not support historical bulk migration — task quotas, API timeout restrictions, and per-task pricing make backfills impractical. Use only for ongoing sync after a one-time migration.

Managed migration service: When engineering bandwidth is scarce or the dataset is large (50K+ conversations with attachments), a managed service reduces the risk of broken relationships, missed records, and extended timelines.

Practical recommendation: Use CSV only for pilots and shallow history. Use API/ETL for real migrations. Use middleware for post-cutover sync. Use a managed service when the business cannot tolerate relationship loss or engineering distraction.

Pre-Migration Planning

Before extracting a single record, complete this checklist:

  • Verify Freshchat plan tier: Confirm your Freshchat plan (Pro or Enterprise) provides REST API access. Free and Growth plans do not.
  • Audit Freshchat data volume: Total conversations, messages, users, attachments. Use the paginated API endpoints to get accurate counts.
  • Identify unused data: Filter out bot-only conversations, spam, test users, abandoned sessions, and retired channels. These inflate volume without adding value and increase Salesforce storage costs.
  • Define migration scope: All history vs. last N months? Active conversations only or resolved too?
  • Estimate Salesforce storage impact: Calculate expected record counts. Each CaseComment is ~2 KB minimum. 100,000 conversations × 15 messages average = 1.5M CaseComments ≈ 3 GB of data storage. Compare against your org's storage limits and budget for overages at ~$125/GB/month (Enterprise Edition list price).
  • Map agents: Build a Freshchat Agent UUID → Salesforce User ID lookup table. Decide what happens to conversations owned by deactivated agents — map them to a generic "Legacy Agent" user to preserve data without consuming a license.
  • Map conversation properties: List all custom properties in Freshchat (up to 50) and create corresponding custom fields on the Case object in Salesforce.
  • Choose conversation fidelity model: CaseComment vs. EmailMessage vs. flat transcript. This decision cannot easily be reversed.
  • Configure Salesforce org first: Custom fields, picklist values, Queues, page layouts, and automation (Flows, assignment rules) must be configured before data loading begins.
  • Plan for attachments: Estimate total attachment size. Salesforce Files (ContentVersion) has a default per-file limit of 2 GB and storage cost implications.
  • Choose cutover model: Big bang (single weekend), phased (by region or department), or incremental (coexistence with delta sync).
  • Set up a sandbox: Always run the migration against a Salesforce Full Copy Sandbox first. Never load directly to production on the first attempt.
  • Create rollback keys: Add a custom External ID field on every target object holding the legacy Freshchat ID. This enables upsert-based re-runs and bulk rollback if needed.
  • Define data retention policy: Determine what happens to Freshchat data after migration. Note your Freshworks contract termination timeline — data may be deleted 30–90 days after contract end depending on your agreement. Export and archive a complete backup before cancellation.

Migration Architecture: Extract → Transform → Load

┌─────────────────┐     ┌──────────────────────┐     ┌───────────────────────┐
│   FRESHCHAT      │     │   STAGING LAYER       │     │   SALESFORCE SC        │
│                  │     │                       │     │                        │
│  /v2/conversations├────►│  PostgreSQL / S3      │     │                        │
│  /v2/messages    │     │                       │     │                        │
│  /v2/users       │ ETL │  ┌─────────────────┐  │     │                        │
│  /v2/agents      ├────►│  │ Transform Layer │  │     │  1. Accounts           │
│  /v2/groups      │     │  │                 │  │LOAD │  2. Contacts           │
│  /v2/channels    │     │  │ - Flatten msgs  │  ├────►│  3. Cases              │
│  /v2/reports/raw │     │  │ - Map statuses  │  │     │  4. CaseComments       │
│                  │     │  │ - Resolve IDs   │  │     │  5. ContentVersions    │
│  Attachments ────├────►│  │ - Clean fields  │  │     │  6. ContentDocLinks    │
│  (download binary)     │  └─────────────────┘  │     │                        │
└─────────────────┘     └──────────────────────┘     └───────────────────────┘
                               │
                               ▼
                     ┌──────────────────┐
                     │  Validation Logs  │
                     │  - Row counts     │
                     │  - Error records  │
                     │  - Audit trail    │
                     └──────────────────┘

Step 1: Extract from Freshchat

Freshchat's REST API is the primary extraction path. Key endpoints:

  • GET /v2/conversations — list all conversations (paginated)
  • GET /v2/conversations/{id}/messages — messages per conversation (max 50 per page)
  • GET /v2/users — requires at least one filter parameter; cannot list all users without filtering
  • GET /v2/users/{user_id}/conversations — user-scoped conversation discovery
  • GET /v2/agents — list all agents
  • GET /v2/groups — list all groups
  • GET /v2/channels — list all channels
  • POST /v2/reports/raw/ — Extract API for bulk CSV report generation
Info

Rate limit reality: Freshchat's per-tier rate limits are not comprehensively documented in the public API reference — some limits appear in account-level documentation and vary by plan tier. The API returns HTTP 429 when limits are exceeded, with X-RateLimit-Limit and X-RateLimit-Remaining headers. The dashboard API limit is documented at 100 requests/minute. In practice, expect extraction throughput of approximately 1,500–2,500 conversations/hour (including message sub-requests) at sustainable rate-limit headroom. Implement exponential backoff on every extraction script. (developers.freshchat.com)

A key Freshchat-specific constraint: conversation discovery can be user-scoped through GET /v2/users/{user_id}/conversations. If your user inventory is incomplete, conversation extraction via this path will be incomplete too. Cross-reference with GET /v2/conversations to verify coverage. (developers.freshchat.com)

The Extract API at /v2/reports/raw/ can generate CSV reports for events like Conversation-Created, Message-Sent, Resolution-Time, and CSAT scores. This is useful for bulk metadata extraction but does not include full message content — you still need the messages endpoint for that.

API endpoint note: Freshchat's newer .api.freshchat.com endpoints behave differently from the legacy {subdomain}.freshchat.com endpoints. Test your extraction scripts against the exact URL format your account uses before running a full extraction.

Extraction code (Python):

import requests
import time
import logging
 
# Configure for your account — adjust BASE_URL to match your endpoint format
# Legacy: https://{subdomain}.freshchat.com/v2
# Newer:  https://{subdomain}.api.freshchat.com/v2
BASE_URL = "https://{subdomain}.freshchat.com/v2"
HEADERS = {"Authorization": "Bearer {api_token}", "Accept": "application/json"}
 
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
 
def extract_conversations():
    page = 1
    all_conversations = []
    while True:
        resp = requests.get(
            f"{BASE_URL}/conversations",
            headers=HEADERS,
            params={"page": page, "items_per_page": 50}
        )
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 60))
            logger.warning(f"Rate limited. Sleeping {retry_after}s")
            time.sleep(retry_after)
            continue
        resp.raise_for_status()
        data = resp.json()
        convos = data.get("conversations", [])
        if not convos:
            break
        all_conversations.extend(convos)
        logger.info(f"Extracted {len(all_conversations)} conversations (page {page})")
        page += 1
    return all_conversations
 
def extract_messages(conversation_id):
    page = 1
    messages = []
    while True:
        resp = requests.get(
            f"{BASE_URL}/conversations/{conversation_id}/messages",
            headers=HEADERS,
            params={"page": page, "items_per_page": 50}
        )
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 60))
            time.sleep(retry_after)
            continue
        resp.raise_for_status()
        data = resp.json()
        msgs = data.get("messages", [])
        if not msgs:
            break
        messages.extend(msgs)
        page += 1
    return messages
 
def download_attachment(url, local_path):
    """Download attachment immediately — URLs may expire."""
    resp = requests.get(url, headers=HEADERS, stream=True)
    resp.raise_for_status()
    with open(local_path, 'wb') as f:
        for chunk in resp.iter_content(chunk_size=8192):
            f.write(chunk)
    return local_path

Step 2: Transform

Transformation handles these operations:

  • Flatten message parts: Freshchat messages use message_parts arrays with types like text, image, file. Flatten to plain text for CaseComment or HTML for EmailMessage. See the channel-specific transformation rules table above for WhatsApp, Messenger, and other channel formats.
  • Map statuses: Freshchat's new / assigned / resolved → Salesforce's Status picklist values.
  • Resolve agent IDs: Build a lookup table mapping Freshchat agent UUIDs to Salesforce User IDs.
  • Resolve user IDs: Match Freshchat users to Salesforce Contacts by email. Create new Contacts for unmatched users.
  • Map conversation properties: Each custom conversation property becomes a custom field value on the Case.
  • Handle timestamps: Convert Freshchat's ISO 8601 timestamps to Salesforce's expected DateTime format. Preserve UTC. Note: Salesforce displays CreatedDate in the viewing user's time zone in reports and UI — during validation, compare timestamps in UTC to avoid false mismatches caused by time zone rendering.
  • Clean custom fields: A text field in Freshchat cannot be mapped to a Picklist in Salesforce unless the data perfectly matches the Picklist values. Run a standardization pass before insertion.

Step 3: Load into Salesforce Service Cloud

Load order matters. Salesforce enforces referential integrity:

  1. Accounts (create default accounts or derive from contact email domains)
  2. Contacts (linked to Accounts via AccountId)
  3. Users (agent mapping — typically pre-existing in Salesforce)
  4. Cases (linked to Contact via ContactId, to Queue/User via OwnerId)
  5. CaseComments / EmailMessages (linked to Case via ParentId)
  6. Attachments / ContentVersions (linked via ContentDocumentLink)

Use Bulk API 2.0 for Cases and Contacts. Salesforce's Bulk API 2.0 can process up to 100 million records per 24-hour period. It auto-chunks into internal batches of 10,000 records. Each job supports up to 150 MB of CSV data and handles one object at a time. You can run up to 25 concurrent jobs. In practice, expect ingest throughput of approximately 30,000–60,000 Cases/hour with 15–20 fields per record, depending on org complexity and active automation. (developer.salesforce.com)

For CaseComments, use the standard REST API — Bulk API does not support CaseComment inserts in all configurations. Batch your REST calls using the Composite API (up to 25 subrequests per call) to reduce API call consumption.

# Salesforce Bulk API 2.0 — Create ingest job for Cases
# Note: Replace v67.0 with your org's current API version
import requests
 
SF_BASE = "https://{instance}.salesforce.com"
SF_HEADERS = {"Authorization": "Bearer {access_token}", "Content-Type": "application/json"}
 
# Step 1: Create job
job = requests.post(
    f"{SF_BASE}/services/data/v67.0/jobs/ingest",
    headers=SF_HEADERS,
    json={
        "object": "Case",
        "operation": "upsert",
        "externalIdFieldName": "Freshchat_Conversation_ID__c",
        "contentType": "CSV",
        "lineEnding": "LF"
    }
).json()
 
# Step 2: Upload CSV data
csv_headers = {"Authorization": "Bearer {access_token}", "Content-Type": "text/csv"}
requests.put(
    f"{SF_BASE}/services/data/v67.0/jobs/ingest/{job['id']}/batches",
    headers=csv_headers,
    data=open("cases.csv", "rb")
)
 
# Step 3: Close job to begin processing
requests.patch(
    f"{SF_BASE}/services/data/v67.0/jobs/ingest/{job['id']}",
    headers=SF_HEADERS,
    json={"state": "UploadComplete"}
)
Tip

JWT Bearer flow is the recommended authentication method for backend migration scripts against Salesforce. It is certificate-based, requires no refresh tokens, and no human login. Configure a Connected App with the api and refresh_token OAuth scopes.

Error handling essentials:

  • Read Freshchat X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers and slow down before hard failures.
  • Watch Salesforce API usage headers and stop cleanly on REQUEST_LIMIT_EXCEEDED.
  • Keep row-level success and failure logs for every object load.
  • Make every load idempotent through external IDs — never match on email or subject text alone. (developers.freshchat.com)

Edge Cases and Failure Modes

Attachment Expiration

Freshchat attachment URLs often require authentication or expire after a set time (commonly 24–72 hours). Your script must download the binary file immediately during extraction and store it locally or in S3 before pushing to Salesforce as a ContentVersion. Never defer attachment downloads to the load phase.

Duplicate Contacts

Freshchat users may not have unique emails — anonymous web visitors get auto-generated IDs. Decide before migration: create placeholder Contacts for anonymous visitors (e.g., with a naming convention like anonymous-{freshchat_user_id}@placeholder.local), or skip them entirely. Deduplicate users in your staging database before pushing to Salesforce.

Bot Messages

Freshchat conversations may include Freddy AI bot interactions. These messages have an actor_type of system or bot. Decide whether to import bot messages as CaseComments for historical reference or discard them. Interactive elements like buttons, carousels, and quick replies have no Salesforce equivalent and must be flattened to descriptive text (e.g., [Bot presented options: A, B, C] [User selected: B]).

Multi-Message Conversations (200+ Messages)

Freshchat's Download Transcript app caps at 200 messages per conversation. For conversations exceeding this, you must use the API's paginated messages endpoint to get the full history.

Missing or Deactivated Agents

If a Freshchat agent has left the company, they will not have an active Salesforce User license. Map their historical cases to a generic "Legacy Agent" user in Salesforce to preserve the data without consuming a license.

User-Scoped Extraction Gaps

If your user inventory is incomplete, conversation discovery via GET /v2/users/{user_id}/conversations will miss orphaned conversations. Cross-reference with GET /v2/conversations to verify full coverage.

Salesforce Governor Limits

If you load Cases with Apex triggers or Flows active, governor limits (DML operations, SOQL queries per transaction) can cause batch failures. Disable all automation during bulk loads — Validation Rules, Triggers, Process Builders, and Flows — then re-enable and backfill after the load completes.

Storage Costs

Salesforce data storage is expensive (~$125/GB/month at Enterprise Edition list price). Storing millions of individual chat messages as separate records will rapidly consume your storage limits. For reference: 1 million CaseComments with average 500-character bodies ≈ 1.5–2 GB of data storage. Compiling chat threads into a single rich-text Case description or using CaseComments (which count differently against storage) is often a necessary compromise. Always calculate projected storage consumption before committing to a fidelity model.

Time Zone Display Discrepancies

Salesforce stores all DateTime fields in UTC but displays them in the viewing user's configured time zone. During validation, always compare timestamps in UTC. Agents may report that "timestamps are wrong" when they are actually seeing correct UTC values rendered in their local time zone — this is expected behavior, not a migration defect.

Validation and Testing

Do not assume a successful API 200 OK response means the data is correct.

Record count validation:

  • Total conversations in Freshchat = Total Cases in Salesforce
  • Total messages per conversation = Total CaseComments per Case
  • Total unique users = Total Contacts created or matched

Field-level validation:

  • Spot-check 5–10% of migrated Cases for correct status, owner, timestamps, and custom field values
  • Verify attachment links resolve correctly
  • Confirm Contact → Case relationships are intact

Sampling strategy:

  • Pull a random sample of 50 conversations from Freshchat
  • Locate each corresponding Case in Salesforce
  • Compare message count, timestamps, and content
  • Include at least 5 conversations per channel type (web chat, WhatsApp, etc.) in the sample

UAT process:

  • Have 2–3 support agents review their own historical conversations in Salesforce
  • Confirm the data is usable for their workflow — search, reporting, customer context

Rollback plan:

  • Tag all migrated records with a custom field (Migration_Source__c = 'Freshchat') or use External IDs
  • If rollback is needed, bulk-delete records matching that tag

Validate incrementally — check after Contacts, check after Cases, check after Comments. Do not wait until the end to discover a broken relationship mapping.

Post-Migration Tasks

Once the data is in Salesforce, the operational work begins.

  • Rebuild automation: Freshchat's assignment rules, IntelliAssign, and bot flows do not transfer. Rebuild using Salesforce Omni-Channel Routing (Enterprise Edition+), Flows, and Einstein Bots.
  • Configure Omni-Channel: Set up presence statuses, routing configurations, and skill-based routing to replace Freshchat's group-based assignment.
  • Agent training: Salesforce Service Cloud's console is a different paradigm from Freshchat's inbox. Budget time for agent onboarding on the Service Console, macros, and Knowledge.
  • Monitor for gaps: Run daily reports for the first two weeks comparing Freshchat conversation counts against Salesforce Case counts to catch missed records.
  • Monitor delta sync: If you kept Freshchat live during the transition, monitor the sync error logs daily for failed updates until full cutover. Consider Salesforce Change Data Capture to detect modifications on the Salesforce side that need back-propagation during coexistence.
  • Archive source data: Export a complete backup of Freshchat data to your own storage (S3, GCS, etc.) before canceling your Freshworks contract. Freshworks typically deletes tenant data 30–90 days after contract termination.

Best Practices

  • Back up everything before starting. Export Freshchat data to a local database or S3 bucket as your source of truth.
  • Always use External IDs. Create custom fields like Freshchat_Conversation_ID__c marked as External IDs on every target object. This enables upsert operations, prevents duplicates on re-runs, and gives you a rollback key.
  • Run test migrations in a sandbox. Use a Salesforce Full Copy Sandbox. Never load directly to production on the first attempt.
  • Disable Salesforce automation during import. Turn off Validation Rules, Triggers, Process Builders, and Flows before loading data. Leaving them on will slow the Bulk API and cause unexpected record failures.
  • Log everything. Every API call, every transformation decision, every skipped record. You will need the audit trail when debugging edge cases in week two.
  • Validate incrementally — object by object, not all at once at the end.
  • Automate transforms instead of doing them in spreadsheets. Manual CSV manipulation is the fastest path to broken data.
  • Version your migration scripts and tag them with the Salesforce API version they target. API behavior can change between versions.

When to Use a Managed Migration Service

Build in-house when:

  • You have a dedicated dev team with Salesforce API experience
  • Data volume is under 10K conversations
  • You can tolerate a 2–4 week engineering investment

Consider a managed service when:

  • Data volume exceeds 50K conversations with attachments
  • Engineering time is better spent on product work
  • You need zero-downtime migration with parallel operation during cutover
  • The Freshchat account uses complex conversation properties, multi-channel data (WhatsApp, Messenger), or heavy bot history that requires careful transformation

What This Migration Actually Costs

The Freshchat → Salesforce Service Cloud migration is a 4–8 week project for most teams, depending on data volume, conversation fidelity requirements, and Salesforce org readiness. The data migration itself is the smaller workstream. The larger effort is Salesforce configuration — custom fields, page layouts, Flows, Omni-Channel, Knowledge base setup, and agent training.

Rough effort estimates by scale:

Scale Conversations Estimated Engineering Effort Elapsed Time
Small < 5,000 40–80 hours 1–2 weeks
Medium 5,000–50,000 120–240 hours 3–5 weeks
Large 50,000–500,000 300–500 hours 5–8 weeks
Enterprise 500,000+ 500+ hours 8–12 weeks

These estimates include extraction, transformation, loading, and two full validation cycles. They do not include Salesforce org configuration, agent training, or automation rebuild.

Budget 2–5x the effort of a chat-to-chat migration. The payoff is a unified service platform that ties into your CRM, enforces SLAs, and scales with the organization — but only if the migration is executed with fidelity.

For most Freshchat to Salesforce Service Cloud projects, the winning pattern is: design the Salesforce schema first, migrate with API/ETL second, validate by relationship and timeline fidelity third, and use middleware only for short-term coexistence after the main load.

Frequently Asked Questions

Can I export Freshchat conversations directly into Salesforce Service Cloud?
No. There is no native import path between Freshchat and Salesforce. You must extract data via Freshchat's REST API (conversations, messages, users endpoints), transform it to match Salesforce's Case/Contact/CaseComment schema, and load it through Salesforce's Bulk API 2.0 or REST API.
How do Freshchat conversations map to Salesforce objects?
Each Freshchat conversation becomes a Salesforce Case. Individual messages become CaseComments (plain text) or EmailMessages (HTML-supported). Freshchat users map to Contacts, agents to Users, groups to Queues, and conversation properties to custom Case fields.
Can we migrate Freshchat attachments to Salesforce?
Yes, but Freshchat attachment URLs often expire or require authentication. Your script must download the binary files immediately during extraction and upload them to Salesforce as ContentVersion records linked to the corresponding Case via ContentDocumentLink.
How long does a Freshchat to Salesforce Service Cloud migration take?
Typically 4–8 weeks end to end. The data migration itself may take days, but Salesforce org configuration — custom fields, Flows, Omni-Channel routing, page layouts, and agent training — is the larger workstream and must be completed before data loading begins.
What happens to Freshchat bot messages during migration?
Freshchat bot messages (Freddy AI) have an actor_type of 'system' or 'bot.' You can import them as CaseComments for historical reference or exclude them entirely. Interactive elements like buttons and carousels have no Salesforce equivalent and must be flattened to text.

More from our Blog