Salesforce Service Cloud to Gorgias Migration: Technical Guide
A technical guide to migrating Salesforce Service Cloud data to Gorgias — covering object mapping, API constraints, extraction methods, edge cases, 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
Migrating from Salesforce Service Cloud to Gorgias is not a lateral helpdesk swap — it is a deliberate architectural downshift from a full enterprise CRM-and-service platform to a focused, e-commerce-native helpdesk. Cases become Tickets. Contacts become Customers. And most of the Salesforce data model — Accounts, Opportunities, Leads, custom objects, multi-level relationship hierarchies — has no equivalent in Gorgias.
There is no native migration path between these two platforms. Gorgias's built-in import only supports Zendesk natively. (docs.gorgias.com) For everything else, including Salesforce, the route is API extraction → data transformation → Gorgias API loading (or a managed migration service). This guide covers the exact object mapping, every viable migration method, API constraints on both sides, edge cases, and the failure modes that catch teams mid-migration.
All API-specific claims reflect documentation as of mid-2026 — verify rate limits and field constraints against current platform docs before implementation.
For a broader pre-migration checklist, see the Salesforce Service Cloud Migration Checklist. For post-migration validation, see our Help Desk Data Migration QA Checklist.
Why Companies Move from Salesforce Service Cloud to Gorgias
The migration is almost always driven by platform-fit economics, not dissatisfaction with Salesforce's capabilities. The most common triggers:
- Cost reduction. Salesforce Service Cloud per-user licensing ranges from $25/user/month (Starter) to $500/user/month (Einstein 1 Service), with most mid-market teams on Professional ($80/user/month) or Enterprise ($165/user/month) editions. (salesforce.com) Beyond licensing, implementation costs for a small single-channel team typically run $8K–$25K, scaling to $25K–$80K for mid-market teams of 11–50 agents and $150K+ for enterprise programs — covering configuration, customization, and integrator fees. Gorgias starts at $10/month with ticket-based pricing and unlimited agent seats on all paid plans. One public case study — Dr. Bronner's — reports about $100,000 per year in savings after switching from Salesforce. (gorgias.com)
- Shopify-native integration. Gorgias was built around Shopify from day one — order data, refund actions, and customer lifetime value surface inside every ticket without custom API work. (docs.gorgias.com) Salesforce has no first-party Shopify integration; connecting Shopify requires a third-party app.
- Operational simplicity. Teams that don't need Salesforce's full CRM (Accounts, Opportunities, pipeline management) find they're paying for complexity they never use. Gorgias strips that to a ticket inbox with e-commerce context.
- Admin overhead. Most Salesforce orgs need a certified Salesforce admin on payroll. Gorgias does not.
- Agency and DTC brand use cases. E-commerce agencies managing multiple Shopify brands prefer Gorgias's per-store model over Salesforce's org-level architecture.
This migration discards CRM functionality. If your team uses Accounts, Opportunities, Leads, or custom objects for anything beyond support, you need a separate CRM or those records stay in Salesforce. Gorgias is a helpdesk, not a CRM replacement.
Core Data Model Differences
Understanding the architectural gap is the single most important pre-migration step. These are fundamentally different systems:
| Concept | Salesforce Service Cloud | Gorgias |
|---|---|---|
| Architecture | Full CRM + service platform | E-commerce helpdesk |
| Core record | Case (with Account/Contact hierarchy) | Ticket (flat, linked to Customer) |
| Customer model | Account → Contact (many-to-many via ContactRole) | Customer (flat, identified by email/phone) |
| Custom objects | Unlimited, fully relational | None — custom ticket fields only (Dropdown, Number, Text, Yes/No) |
| Custom field limits | Effectively unlimited | 25 active ticket fields, 4 active customer fields (docs.gorgias.com) |
| Sales pipeline | Leads, Opportunities, Quotes | Not supported |
| Knowledge base | Salesforce Knowledge (articles, categories, data categories) | Help Center (articles, categories) |
| Automation | Flows, Process Builder, Assignment Rules, Escalation Rules | Rules, Macros, AI Agent |
| File storage | ContentDocument / ContentVersion (up to 2GB per file) | Ticket attachments |
| API model | REST, SOAP, Bulk API 2.0, Streaming, Metadata | REST API only |
| Ticket/record storage limits | Governed by data storage allocation (varies by edition) | No published per-account ticket cap; Gorgias retains all tickets indefinitely on paid plans |
Gorgias's data model is intentionally flat. There are no parent-child object relationships, no junction objects, no record types. Every ticket belongs to one customer. Every customer is identified by email or phone channel. That simplicity is the point — and the constraint.
Data Model and Object Mapping
This is the section that determines whether your migration succeeds or fails silently.
Object-Level Mapping
| Salesforce Object | Gorgias Equivalent | Notes |
|---|---|---|
| Case | Ticket | 1:1 mapping. Case comments → Ticket messages. |
| Contact | Customer | Email becomes the primary identifier. Phone as secondary channel. |
| Account | ❌ No equivalent | Flatten key Account fields into Customer tags or meta JSON. |
| Lead | ❌ No equivalent | Convert to Customer if support-relevant; otherwise exclude. |
| Opportunity | ❌ No equivalent | Exclude or archive externally. |
| Task / Event (Activities) | Internal note on Ticket | Lossy — Activities lose their standalone identity. |
| EmailMessage | Ticket Message | Map TextBody/HtmlBody → body_text/body_html. |
| CaseComment | Ticket Message (internal note) | Set channel: "internal-note" and from_agent: true. |
| Attachment / ContentVersion | Ticket Attachment | Download binary from Salesforce, re-upload to Gorgias. |
| Knowledge Article (KnowledgeArticleVersion) | Help Center Article | See dedicated Knowledge Base Migration section below. |
| Custom Objects | ❌ No equivalent | Store critical data in ticket meta field (JSON key-value), customer metafields, or tags. |
Field-Level Mapping (Cases → Tickets)
| Salesforce Case Field | Gorgias Ticket Field | Transformation |
|---|---|---|
CaseNumber |
external_id |
Direct mapping — useful for cross-referencing |
Subject |
First message subject |
Truncate to 998 characters (developers.gorgias.com) |
Description |
First message body_text / body_html |
Direct |
Status (New, Working, Escalated, Closed) |
status (open, closed) |
Gorgias has only open/closed — map intermediate states to tags |
Priority (High, Medium, Low) |
Tag or custom ticket field | No native priority field in Gorgias |
Origin (Web, Phone, Email) |
channel (email, chat, phone, api) |
Map to closest channel |
CreatedDate |
created_datetime |
ISO 8601 format |
ClosedDate |
closed_datetime |
ISO 8601 format |
ContactId → Contact.Email |
customer.email |
Lookup Contact email during extraction |
OwnerId → User.Email |
assignee_user.email |
Map to Gorgias agent; create agents first |
| Custom picklist fields | Custom ticket fields (Dropdown) | Pre-create in Gorgias. Dropdown imports max at 2,000 values and 5 nested levels. (docs.gorgias.com) |
| Custom text fields | Custom ticket fields (Text) | Field types: Dropdown, Number, Text, Yes/No only |
Gorgias's external_id field on tickets is explicitly not used by Gorgias internally — it exists for exactly this purpose. (developers.gorgias.com) Store the Salesforce CaseNumber or Case.Id here for post-migration cross-referencing and audit trails.
The meta Field Explained
Gorgias exposes a meta field on both Customer and Ticket objects. This is a freeform JSON key-value store — it accepts arbitrary key-value pairs and has no schema enforcement. It is not searchable through the Gorgias UI but is accessible via API. Use it to preserve Salesforce data that has no native Gorgias equivalent (Account IDs, custom object references, original status values) without consuming your limited custom field slots.
Handling Relationships and Hierarchies
Salesforce's relational hierarchy is the hardest part of this migration. The Account → Contact → Case chain has no structural equivalent in Gorgias.
Strategy: Flatten the hierarchy. During extraction, denormalize Account-level data (company name, industry, account tier) into the Customer record's meta field or as tags on associated tickets. You lose queryability but preserve context.
# Denormalize Account data into Gorgias customer meta
gorgias_customer = {
"email": contact["Email"],
"name": f"{contact['FirstName']} {contact['LastName']}",
"meta": {
"salesforce_contact_id": contact["Id"],
"salesforce_account_id": contact["AccountId"],
"company_name": account["Name"],
"account_tier": account.get("Tier__c", ""),
"industry": account.get("Industry", "")
}
}Where agents still need live Account or Opportunity context in Gorgias, use a custom HTTP integration widget rather than stuffing JSON into text fields.
Handling Custom Objects
Gorgias does not support custom objects the way Salesforce does. If you have a custom Salesforce object (e.g., RMA_Request__c or Warranty_Claim__c), you have three options:
- Map the custom object data into Gorgias Customer Metafields or ticket
meta(JSON key-value). - Render the custom object data as an internal note (a private message) on the relevant Gorgias Ticket.
- Keep the data in an external system and surface it in Gorgias through a custom HTTP widget.
For small, flat custom objects, option 1 or 2 works. For relational custom objects, option 3 is the only approach that preserves usability.
Migration Approaches
There are five viable methods. Each has a clear use case — and clear limits.
1. CSV-Based Export/Import
How it works: Export Cases, Contacts, and related data from Salesforce using Data Loader or Data Export Service as CSV files. Transform the CSVs to match Gorgias's expected format. Import via Gorgias's API or use a third-party tool's CSV import mode. Salesforce's Data Export Service is not optimized for speed, making this a poor path for iterative test migrations. (help.salesforce.com)
When to use it: Small datasets (under 5,000 records), one-time migrations, teams with no developer resources.
Pros: No code required. Familiar format. Good for auditing data before import.
Cons: No attachment handling. No relationship preservation without manual work. Gorgias customers cannot be imported manually by CSV. (docs.gorgias.com) Reports cap at 100,000 rows. Manual and error-prone at scale.
Scalability: Small business only.
Risk of data loss: High.
2. API-Based Migration (Salesforce APIs → Gorgias API)
How it works: Extract data using Salesforce REST or Bulk API 2.0 with SOQL queries. Transform in a script (Python, Node.js). Load into Gorgias via POST /api/customers/, POST /api/tickets, and POST /api/tickets/{id}/messages.
When to use it: Mid-size to large datasets. Teams with at least one developer who can write and maintain extraction/load scripts.
Pros: Full control over mapping, transformation, and error handling. Handles attachments. Preserves timestamps via sent_datetime. Supports incremental runs.
Cons: Requires development effort (typically 2–6 weeks). Must handle rate limits on both sides. Gorgias API caps at 40 requests per 20 seconds on API key auth, or 80 requests per 20 seconds on OAuth2. (developers.gorgias.com) Salesforce Enterprise Edition starts at 100,000 daily REST API requests.
Scalability: Enterprise-grade with proper batching.
Risk of data loss: Low (if built with proper error handling and retry logic).
3. Third-Party Migration Tools
How it works: Tools like Help Desk Migration connect to both platforms via API and handle mapping through a wizard UI. Gorgias's own documentation recommends help-desk-migration.com for non-Zendesk sources. (docs.gorgias.com)
When to use it: Teams that want a managed process without custom code. Medium-complexity migrations.
Pros: Pre-built connectors. Visual field mapping. Demo migration capability. Delta migration support.
Cons: Less control over edge-case transformations. Tickets with more than 30–40 comments may not fully migrate without customization. May not handle Salesforce custom objects or complex hierarchies. Pricing depends on record volume.
Scalability: Medium — depends on tool limits.
Risk of data loss: Medium.
4. Custom ETL Pipeline
How it works: Build a dedicated Extract-Transform-Load pipeline using tools like Apache Airflow, dbt, or custom scripts. Extract via Salesforce Bulk API 2.0 into a staging database. Transform with SQL or Python. Load via Gorgias API with retry logic and logging.
When to use it: Large enterprises with complex data (100K+ Cases), custom objects, and strict audit requirements.
Pros: Maximum control. Full audit trail. Reusable for future migrations. Handles complex transformations (flattening hierarchies, merging duplicates, custom field mapping).
Cons: Highest engineering investment. 2–6 weeks of development depending on complexity. Requires ongoing maintenance during migration window.
Scalability: Enterprise.
Risk of data loss: Low.
5. Middleware/Integration Platforms (Zapier, Make)
How it works: Use Zapier or Make to create workflows that read Salesforce records and write them to Gorgias. Zapier's official Gorgias app exposes actions like Create Ticket and Create Ticket Message. (zapier.com)
When to use it: Ongoing sync scenarios or very small one-time migrations (under 1,000 records).
Pros: No-code. Fast setup. Good for ongoing sync after a historical migration.
Cons: Not designed for bulk historical migration. Zapier processes records one at a time. Make has execution time limits. Both hit Gorgias rate limits quickly. No batch operation support. Expensive at volume (per-task pricing).
Scalability: Small business only.
Risk of data loss: High (for historical data).
Migration Approach Comparison
| Approach | Complexity | Best For | Scalability | Attachment Support | Cost |
|---|---|---|---|---|---|
| CSV Export/Import | Low | <5K records, no dev team | Small | ❌ | Low |
| API-Based | High | 5K–500K+ records, dev team available | Enterprise | ✅ | Medium |
| Third-Party Tools | Low–Med | Mid-size, no custom objects | Medium | Partial | Per-record |
| Custom ETL | High | 100K+, complex data, audit needs | Enterprise | ✅ | High (eng time) |
| Middleware (Zapier/Make) | Low | <1K records or ongoing sync | Small | ❌ | Per-task |
Scenario Recommendations
- Small e-commerce brand (<5K tickets, no custom objects): CSV export or third-party tool.
- Mid-market DTC brand (5K–50K tickets, standard fields): Third-party tool or API-based with a single developer.
- Enterprise (50K+ Cases, custom objects, attachments, audit requirements): Custom ETL or managed migration service.
- Ongoing sync needed: Middleware for low volume, custom integration for anything above.
Migration Architecture
A resilient migration architecture follows an Extract-Transform-Load pattern with an intermediate datastore.
Data Flow
Salesforce Service Cloud
├── Bulk API 2.0 (Cases, Contacts, Accounts)
├── REST API (EmailMessages, CaseComments)
└── REST API (ContentVersion / Attachments)
↓
Staging Layer (DB or file system)
↓
Transform (flatten, map, clean)
↓
Gorgias REST API
├── POST /api/customers/
├── POST /api/tickets
└── POST /api/tickets/{id}/messages
Salesforce API Constraints (Extraction Side)
- REST API daily limit: Enterprise Edition starts at 100,000 requests/24 hours + 1,000 per user license.
- Bulk API 2.0: Up to 150 million records per 24-hour period. 15,000 batch submissions per day (shared between Bulk API 1.0 and 2.0). Each batch up to 10,000 records, 150MB per job.
- Query cursors: REST queries return up to 2,000 records per response with a cursor for pagination. Cursors remain available for up to two days.
- Concurrency: Maximum 25 long-running requests (20+ seconds) in production.
- File extraction: ContentVersion binary content cannot be exported as CSV — requires individual REST API calls per file.
Use Bulk API 2.0 for Cases, Contacts, and Accounts. Use REST API for EmailMessages and ContentVersion (file downloads). Monitor usage via GET /services/data/vXX.0/limits.
Gorgias API Constraints (Load Side)
- Rate limits: 40 requests per 20 seconds on API key auth. 80 requests per 20 seconds on OAuth2. Enterprise plans may have custom limits — contact Gorgias support to request a temporary rate limit increase for migration windows. (developers.gorgias.com)
- 429 handling: Response includes
Retry-Afterheader. Implement exponential backoff with jitter. - Ticket creation: A ticket cannot be created without at least one message. The
messagesarray is required onPOST /api/tickets. - Historical import: Set
sent_datetimeon messages to prevent Gorgias from actually sending them. This is how you import historical messages without triggering outbound emails. (developers.gorgias.com) - No bulk API. Every ticket, customer, and message is a single REST call. At 40 req/20s, importing 100,000 tickets with 3 messages each ≈ 400,000 API calls ≈ ~55 hours of continuous import at max throughput. At 80 req/20s (OAuth2), that drops to ~28 hours.
Gorgias has no bulk import API. Every record is a single REST call. For large migrations (50K+ tickets), budget days of import time and implement checkpoint/resume logic in your scripts. A rollback of 50K tickets via DELETE /api/tickets/{id} at the same rate limits would take approximately 7 hours — plan accordingly.
Step-by-Step Migration Process
Step 1: Pre-Migration Audit
- Export a record count from Salesforce: Cases, Contacts, Accounts, EmailMessages, Attachments, custom objects.
- Identify unused or redundant data: spam Cases, test records, deprecated picklist values.
- Define migration scope: time range, active vs. archived agents, which custom fields matter.
- Document all custom objects and determine disposition (exclude, flatten to tags, store in
meta). - Decide on a cutoff date. Many teams choose to leave cases older than 3 years behind to reduce API load and clutter in Gorgias.
- Verify Gorgias custom field limits against your Salesforce field inventory: 25 active ticket fields and 4 active customer fields. If your Salesforce org has more mapped fields than this, prioritize which to carry as structured fields vs. tags vs.
metaJSON.
For deeper preparation, see our Salesforce Service Cloud Migration Checklist.
Step 2: Set Up the Gorgias Target Environment
- Create all agent accounts in Gorgias. Agents must exist before ticket import so that assignment mapping resolves correctly.
- Create custom ticket fields matching your Salesforce field mapping. Gorgias supports Dropdown, Number, Text, and Yes/No types.
- Create tags for any Salesforce picklist values that don't map to Gorgias fields (Priority, Case Reason, Escalation level).
- Disable all automation Rules in Gorgias before import. Active rules will trigger on imported tickets and can send auto-replies, change statuses, or assign tickets incorrectly.
- Note on sandbox environments: Gorgias does not offer a dedicated sandbox or staging environment. The standard workaround is to create a separate Gorgias test store (a second account, often on a free trial or basic plan) to validate your migration before running it against your production store. Delete the test store or its data after validation.
Step 3: Extract from Salesforce
Use Bulk API 2.0 for high-volume objects:
# Salesforce Bulk API 2.0 — Create a query job for Cases
import requests
sf_instance = "https://your-instance.my.salesforce.com"
headers = {"Authorization": f"Bearer {access_token}", "Content-Type": "application/json"}
# Create query job
job = requests.post(f"{sf_instance}/services/data/v60.0/jobs/query", headers=headers, json={
"operation": "query",
"query": "SELECT Id, CaseNumber, Subject, Description, Status, Priority, Origin, "
"CreatedDate, ClosedDate, ContactId, Contact.Email, Contact.FirstName, "
"Contact.LastName, OwnerId, Owner.Email, AccountId, Account.Name "
"FROM Case WHERE CreatedDate >= 2022-01-01T00:00:00Z"
})
job_id = job.json()["id"]
# Poll for completion, then retrieve results
results = requests.get(
f"{sf_instance}/services/data/v60.0/jobs/query/{job_id}/results",
headers={**headers, "Accept": "text/csv"}
)For EmailMessages and attachments, use REST API with pagination:
# Fetch EmailMessages for a specific Case
emails = requests.get(
f"{sf_instance}/services/data/v60.0/query/",
headers=headers,
params={"q": f"SELECT Id, Subject, TextBody, HtmlBody, FromAddress, ToAddress, "
f"MessageDate, Incoming FROM EmailMessage WHERE ParentId = '{case_id}'"}
)For Tasks and Events older than a year, use queryAll() or Data Loader Export All. Standard queries and reports miss archived activities entirely. (help.salesforce.com)
Step 4: Transform Data
The transformation layer is where most migrations fail. Key operations:
- Flatten Account hierarchy: Denormalize Account fields into Customer
meta. - Map Status values: Salesforce's multi-state status (New, Working, Escalated, Closed) → Gorgias's binary (open, closed). Preserve the original status as a tag (e.g.,
sf-status-escalated). - Map Priority: No native priority field in Gorgias. Create a custom Dropdown field or use tags.
- Deduplicate Contacts: Salesforce allows multiple Contacts with the same email across different Accounts. Gorgias identifies customers by email. Merge or choose a primary during transformation.
- Build message threads: Each Case needs its EmailMessages and CaseComments queried, sorted chronologically by
CreatedDate/MessageDate, and assembled into the Ticket'smessagesarray. - Sanitize HTML: Salesforce
EmailMessagebodies often contain complex HTML or proprietary CSS classes. Run a sanitization pass (e.g., usingbleachin Python orDOMPurifyin Node.js) to strip malformed HTML before loading. - Truncate subjects: Gorgias ticket subjects max out at 998 characters.
- Standardize timestamps to ISO 8601 format (
YYYY-MM-DDTHH:MM:SSZ). - Base64-encode attachments or download binaries from
ContentVersion.VersionDataand host them temporarily for upload.
Step 5: Load into Gorgias
Load order matters. Follow this sequence:
- Customers (
POST /api/customers/) — Create all customers first so ticket references resolve. - Tickets with messages (
POST /api/tickets) — Include the first message in the ticket creation payload. Setsent_datetimeon all messages to prevent outbound sends. - Additional messages (
POST /api/tickets/{id}/messages) — Add remaining messages to each ticket. - Tags and custom fields — Update tickets with tags and custom field values via
PUT /api/tickets/{id}.
import requests
import time
import random
import json
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("gorgias_import")
GORGIAS_BASE = "https://your-store.gorgias.com"
gorgias_email = "admin@yourdomain.com"
gorgias_api_key = "your_api_key"
CHECKPOINT_FILE = "migration_checkpoint.json"
def load_checkpoint():
try:
with open(CHECKPOINT_FILE) as f:
return json.load(f)
except FileNotFoundError:
return {"last_imported_index": -1}
def save_checkpoint(index):
with open(CHECKPOINT_FILE, "w") as f:
json.dump({"last_imported_index": index}, f)
def post_with_retry(url, payload, max_retries=5):
"""POST with exponential backoff and jitter on 429s."""
for attempt in range(max_retries):
response = requests.post(
url,
auth=(gorgias_email, gorgias_api_key),
json=payload
)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
jitter = random.uniform(0, retry_after * 0.5)
wait = retry_after + jitter
logger.warning(f"429 received. Retrying in {wait:.1f}s (attempt {attempt+1})")
time.sleep(wait)
continue
elif response.status_code >= 500:
time.sleep(2 ** attempt + random.uniform(0, 1))
continue
return response
raise Exception(f"Max retries exceeded for {url}")
# Create a historical ticket with message (not sent)
gorgias_ticket = {
"customer": {"email": "[email protected]"},
"channel": "email",
"status": "closed",
"external_id": "SF-00045821", # Salesforce CaseNumber
"via": "api",
"from_agent": False,
"tags": [{"name": "migrated-from-salesforce"}, {"name": "priority-high"}],
"created_datetime": "2024-03-15T09:22:00Z",
"messages": [{
"sender": {"email": "[email protected]"},
"channel": "email",
"from_agent": False,
"via": "api",
"subject": "Order #12345 damaged on arrival",
"body_text": "Hi, my order arrived damaged...",
"body_html": "<p>Hi, my order arrived damaged...</p>",
"sent_datetime": "2024-03-15T09:22:00Z", # Prevents actual sending
"source": {
"from": {"address": "[email protected]"},
"to": [{"address": "[email protected]"}]
}
}]
}
response = post_with_retry(f"{GORGIAS_BASE}/api/tickets", gorgias_ticket)
logger.info(f"Ticket created: {response.status_code} - {response.json().get('id', 'unknown')}")Tag every imported ticket with migrated-from-salesforce so they're identifiable for auditing and rollback.
Step 6: Validate
- Compare record counts: source Cases vs. imported Tickets.
- Sample 5–10% of tickets and verify field values, message ordering, and customer association.
- Check that closed tickets imported as closed (not re-opened by Rules you forgot to disable).
- Verify attachments are accessible.
- Confirm agent assignment mapping is correct.
Build a validation dashboard:
| Metric | Salesforce (Source) | Gorgias (Target) | Match? |
|---|---|---|---|
| Total Cases / Tickets | COUNT query | API list with pagination | |
| Total Contacts / Customers | COUNT query | API list | |
| Total EmailMessages / Messages | COUNT query | Sum messages per ticket | |
| Closed Cases / Closed Tickets | Filtered count | Filtered count | |
| Attachments | ContentDocumentLink count | Spot-check sample |
Sample 50–100 tickets across different time periods, statuses, and agents. For each, verify that the subject matches, message count and ordering match, customer email matches the source Contact, custom field values transferred correctly, tags include expected values, and external_id contains the Salesforce CaseNumber.
Step 7: Post-Migration Cutover
- Rebuild automations. Salesforce Flows, Assignment Rules, and Escalation Rules have no migration path. Recreate equivalent logic using Gorgias Rules and Macros.
- Connect e-commerce. Connect Shopify, email channels, chat widgets, and any other integrations to Gorgias. The Shopify integration should be operational before go-live — this is the whole point of the move.
- Update email routing. Redirect inbound support email to Gorgias. This typically involves one of: (a) updating email forwarding rules at your email provider to forward
support@yourdomain.comto the Gorgias forwarding address shown in Settings → Integrations → Email, (b) updating MX records if Gorgias is the primary mail handler for a support-specific subdomain, or (c) configuring an alias redirect in Google Workspace or Microsoft 365. Update SPF records to include Gorgias's sending IPs (provided in their email setup docs) and configure DKIM signing via Gorgias's DNS verification flow to maintain deliverability. - Run a final delta sync to catch any tickets created in Salesforce during the migration window.
- Train agents. Gorgias's UI and workflow differ significantly from Salesforce's console. Budget 1–2 days of hands-on training.
- Monitor for two weeks. Watch for tickets stuck in wrong status, customers missing data, automation misfires, and attachment access issues.
- Export Salesforce reports. Before decommissioning, export all historical CSAT, SLA, and agent performance data from Salesforce. This data does not exist in Gorgias.
Knowledge Base Migration: Salesforce Knowledge → Gorgias Help Center
Gorgias Help Center natively imports from Zendesk, HelpDocs, Re:amaze, and Intercom — but not from Salesforce Knowledge. Migrating Knowledge Articles requires a structured manual process:
Field Mapping
| Salesforce Knowledge Field | Gorgias Help Center Field | Notes |
|---|---|---|
Title |
Article title | Direct |
Summary |
Article description/excerpt | May need truncation |
Body (Rich Text) |
Article body (HTML) | Sanitize HTML; re-host images |
DataCategoryGroupName → DataCategoryName |
Category | Gorgias supports single-level categories; flatten multi-level Salesforce data categories |
PublishStatus |
Published/Draft | Map Online → Published, Draft → Draft, Archived → exclude or Draft |
Language |
Language (if multilingual Help Center) | Gorgias supports multilingual Help Center on higher plans |
| Article attachments | ❌ Not directly supported | Re-host files and link within article body |
Migration Steps
- Extract articles via SOQL:
SELECT Id, Title, Summary, KnowledgeArticleId FROM KnowledgeArticleVersion WHERE PublishStatus = 'Online' AND Language = 'en_US'. Note: the body field name depends on your article type's custom Rich Text field (e.g.,FAQ__kav.Answer__c). - Download and re-host images. Salesforce Knowledge article bodies contain
<img>tags referencing Salesforce-authenticated URLs (e.g.,/servlet/rtaImage?...). Download each image, upload to a public CDN or your own hosting, and rewrite thesrcattributes. - Map categories. Salesforce Data Categories support multi-level hierarchies with multiple category groups. Gorgias Help Center categories are single-level. Flatten by concatenating parent-child names (e.g., "Products > Returns" → "Products - Returns") or choose the most specific level.
- Create articles via Gorgias API (
POST /api/help-center/articles) or manually via the Help Center editor. The API accepts HTML body content. - Verify rendering. Check that HTML renders correctly in the Gorgias Help Center theme, especially tables, code blocks, and embedded media.
For large Knowledge Bases (200+ articles), script the extraction and API creation. For smaller ones, manual copy-paste with image re-upload is often faster than building tooling.
Edge Cases and Failure Modes
These are the issues that derail migrations after the "happy path" works.
Duplicate Customers
Salesforce allows multiple Contacts with the same email across different Accounts. Gorgias identifies customers solely by email. If you import two Contacts with john@company.com, the second POST /api/customers/ will update the first. All tickets from both Contacts merge under one Customer. Gorgias returns 409 Conflict for duplicate identifiers. (developers.gorgias.com) Decide pre-migration whether to merge or rename.
Missing Email Addresses
Salesforce Contacts don't require an email. Gorgias Customers do. Contacts without email addresses cannot be imported as Customers. Options: skip them, assign a placeholder email (loses searchability), or use phone channel if a phone number exists.
Inactive Users
If a ticket was handled by a Salesforce agent who left the company years ago, creating them as a paid seat in Gorgias wastes a license. Map all inactive Salesforce users to a single "Legacy Agent" user in Gorgias to preserve assignment history without consuming paid seats.
Multi-Level Relationships
Salesforce's Account → Contact → Case → EmailMessage → Attachment chain is five levels deep. Gorgias is two: Customer → Ticket → Message. Everything else must be flattened, tagged, or discarded. There is no workaround for this structural difference.
Inline Images
Salesforce EmailMessage HTML bodies often contain inline images as <img> tags referencing Salesforce content URLs (e.g., https://your-instance.my.salesforce.com/servlet/rtaImage?...). These URLs require authentication to access. After migration, they break. Download all inline images during extraction and either re-host them on a public CDN or convert to attachments.
ContentVersion Extraction
File attachments in Salesforce are stored across multiple objects: ContentDocument, ContentVersion, ContentDocumentLink, and legacy Attachment. You must query ContentDocumentLink to find the attachment tied to a Case, download the binary from the VersionData field via individual REST API calls (CSV export doesn't include file binaries), and upload it to Gorgias within the corresponding message payload. Gorgias returns 413 Payload Too Large when uploads exceed limits. (developers.gorgias.com)
Tickets with 30+ Messages
Third-party migration tools may truncate tickets with more than 30–40 comments. If using API-based migration, this is not a limitation — but watch for rate limits when loading message-heavy tickets. A single Case with 50 messages requires 51 API calls (1 ticket + 50 messages), consuming ~25 seconds of your rate limit budget.
Archived Activities
Salesforce archives older closed Tasks and Events and excludes them from standard queries and reports. If you need these records and don't query them explicitly with queryAll() or Data Loader Export All, they silently vanish from the migration. (help.salesforce.com)
Field History and Audit Trail
Salesforce retains field history tracking data for 18 months by default. If you need case field change history beyond that window, you must have Field Audit Trail enabled (available on Shield or Enterprise+ editions), which extends retention to 10 years. This historical data has no Gorgias equivalent — export it to a separate data warehouse before decommissioning Salesforce. (help.salesforce.com)
Custom-Field Overwrites
Gorgias's PUT /tickets/{ticket_id}/custom-fields endpoint has replace-all semantics — omitted fields are deleted. (developers.gorgias.com) Use individual-field updates or fetch-merge-update logic to avoid wiping existing values.
Merged Customer Redirects
Gorgias returns 301 on customer endpoints when a referenced customer was merged into another record. (developers.gorgias.com) Handle these redirects in your scripts to avoid broken ticket-to-customer associations.
Automation Triggers
If you forget to disable Gorgias Rules before import, auto-tagging, auto-assignment, and auto-reply rules will fire on every imported ticket. This can send thousands of emails to customers about cases from years ago. Disable all Rules. Verify they're disabled. Then verify again.
Limitations and Constraints
Be explicit with stakeholders about what is lost in this migration:
- No custom objects in Gorgias. Salesforce custom objects (e.g., Warranty Claims, Return Authorizations) have no structural equivalent. Data must be flattened into ticket
meta(JSON) or tags, or kept in a separate system. - Simplified status model. Gorgias tickets are open or closed. Salesforce's multi-state case lifecycle (New → Working → Escalated → Closed → Reopened) compresses to binary. Use tags to preserve granularity.
- No Account/Company hierarchy. B2B support teams that rely on Account-level views, entitlements, or SLAs lose that structure entirely.
- No SLA enforcement. Salesforce's entitlement management, milestone tracking, and SLA clock have no Gorgias equivalent.
- No pipeline management. Salesforce has Opportunities and pipeline stages. Gorgias is strictly a helpdesk. If you use Salesforce for sales, keep Salesforce CRM active or migrate sales data to another platform.
- Reporting reset. Historical first-response time, resolution time, and CSAT metrics do not carry over. Export Salesforce reporting data before decommissioning.
- Knowledge base import constraints. Gorgias Help Center natively imports from Zendesk, HelpDocs, Re:amaze, and Intercom. Salesforce Knowledge requires manual extraction, image re-hosting, and API loading or manual recreation.
- Custom field caps. Only 25 active ticket fields and 4 active customer fields. Dropdown imports max out at 2,000 values and 5 nested levels.
- API rate limits. Gorgias imposes strict rate limits based on your authentication method (40 req/20s on API key, 80 req/20s on OAuth2). A large historical migration can take days to load if not batched and throttled correctly. Enterprise plans may negotiate custom limits.
If leadership expects Gorgias to replace both Service Cloud and the CRM side of Salesforce, reset scope. That is a platform rationalization program, not a helpdesk migration.
Validation and Testing
Do not assume a 200 OK response means the data is correct.
UAT Process
- Run a test migration with a representative data subset (e.g., 500 Cases from different periods, statuses, and agents) into a separate Gorgias test store.
- Have 2–3 senior support agents review migrated tickets in the Gorgias UI.
- Verify agent assignment, tag accuracy, and message readability.
- Confirm that no outbound emails were sent during import (check agent email inboxes and Gorgias's email logs).
- Run the full migration only after UAT sign-off.
Rollback Planning
Gorgias does not have a native "undo import" function. If the migration goes wrong:
- Delete imported tickets via API (
DELETE /api/tickets/{id}) — but this is one-by-one and rate-limited. Deleting 50,000 tickets at 40 req/20s takes approximately 7 hours. - Tag all imported tickets with
migrated-from-salesforceso they're identifiable for bulk cleanup. - Keep Salesforce active until validation is complete. Do not decommission the Salesforce org until at least two weeks of parallel operation.
Estimated Migration Timelines
These are rough ranges based on migration size and approach. Actual timelines vary with data complexity, custom objects, and team availability.
| Migration Size | Approach | Development/Setup | Import Runtime | Validation + Cutover | Total Elapsed |
|---|---|---|---|---|---|
| <5K tickets | CSV + third-party tool | 1–3 days | Hours | 1–2 days | 1 week |
| 5K–30K tickets | API-based (1 developer) | 1–3 weeks | 6–18 hours | 3–5 days | 2–4 weeks |
| 30K–100K tickets | API-based or custom ETL | 2–4 weeks | 1–3 days | 1 week | 4–6 weeks |
| 100K+ tickets | Custom ETL or managed service | 3–6 weeks | 3–7 days | 1–2 weeks | 6–10 weeks |
Import runtime assumes Gorgias API key rate limits (40 req/20s). OAuth2 auth roughly halves import time. These estimates include tickets, messages, and customers but not knowledge base migration, which is additive.
When to Use a Managed Migration Service
Build in-house when you have a developer available for 2–4 weeks, your data is straightforward (standard fields, no custom objects, under 50K records), and your team has done API integrations before.
Use a managed service when:
- You have custom objects or complex hierarchies that need careful flattening.
- Your Salesforce org has 100K+ Cases with attachments.
- Your engineering team is committed to product work and can't absorb a multi-week migration project.
- You need guaranteed timelines — the hidden cost of DIY is scope creep when edge cases surface.
- You've already attempted a migration and hit data integrity issues.
The engineering cost of a "simple" migration typically compounds: the first 80% of records take 20% of the effort. The remaining 20% — duplicates, missing emails, broken attachments, custom field edge cases — take 80% of the time.
The hidden cost in DIY is not just code. It is audit time, mapping workshops, test reruns, attachment triage, agent UAT, rollback planning, and the operational load of running two systems in parallel. Salesforce also automatically archives older Tasks and Events, and field history has retention limits unless Field Audit Trail is enabled. (help.salesforce.com) Gorgias will send messages if imported records are posted without sent_datetime. Those are the details that blow up a weekend cutover.
Best Practices
- Back up everything first. Run a full Salesforce Data Export before starting. Store it independently.
- Run test migrations in a separate Gorgias store. Since Gorgias does not provide sandbox environments, create a dedicated test store and validate there before importing into production.
- Start with a 500-record subset. Choose representative data spanning different time periods, statuses, channels, and agents. Validate thoroughly before scaling up.
- Tag imported records. Apply a
migrated-from-salesforcetag to every imported ticket. This makes filtering, auditing, and rollback possible. - Disable Gorgias automations. Turn off all Rules before import. Re-enable only after validation.
- Preserve Salesforce IDs. Store
CaseNumberinexternal_idandCase.Idin ticketmeta. You'll need these for troubleshooting and re-runs. - Use a delta strategy. Migrate 99% of historical data over a week. On cutover day, freeze Salesforce, run a final delta migration to catch tickets created during that week, and route email forwarding to Gorgias.
- Sanitize early. Clean up Salesforce data before extracting it. Delete spam cases and merge duplicate accounts. Do not migrate garbage into a fresh system.
- Automate retry logic. Gorgias 429s are expected during bulk import. Build retry with exponential backoff and jitter into your scripts from the start.
- Implement checkpoint/resume. Persist the last successfully imported record index to a file or database. On restart, resume from the checkpoint rather than re-importing from the beginning.
- Communicate the reporting reset. Stakeholders need to know that historical support metrics (FRT, resolution time, CSAT) will not exist in Gorgias reporting. Export these from Salesforce before cutover.
- Keep unsupported CRM objects external instead of forcing them into text fields. Use HTTP widgets for live context that agents actually need.
The Trade-Off
This migration trades Salesforce's CRM depth for Gorgias's e-commerce speed. That trade-off is the right call for Shopify-native brands that don't use Salesforce's pipeline, Account hierarchy, or enterprise automation features. It is the wrong call for B2B teams that need Account-level SLAs, complex entitlements, or custom object workflows.
The technical execution is API-to-API with no shortcuts. No native import path exists. Gorgias's flat data model means every relational structure in Salesforce must be deliberately flattened or discarded. The migration is achievable — a 30K-ticket migration with standard fields typically completes in 2–4 weeks including development, import, and validation — but requires careful planning for edge cases that consume the majority of effort.
For related migration guides, see Kustomer to Gorgias Migration, How to Export Data from Salesforce Service Cloud, and Mastering Gorgias (2026).
Frequently Asked Questions
- Can you migrate directly from Salesforce Service Cloud to Gorgias?
- No. Gorgias's native import only supports Zendesk. For Salesforce Service Cloud, you need an API-based migration (Salesforce Bulk API extraction → data transformation → Gorgias REST API loading), a third-party migration tool like Help Desk Migration, or a managed migration service.
- What Salesforce data can be migrated to Gorgias?
- Cases map to Tickets, Contacts map to Customers, EmailMessages map to Ticket Messages, CaseComments map to internal notes, and Attachments can be re-uploaded. Accounts, Leads, Opportunities, and custom objects have no Gorgias equivalent — they must be flattened into tags or metadata, or excluded.
- What are the Gorgias API rate limits for data migration?
- Gorgias caps API requests at 40 per 20 seconds on API key authentication and 80 per 20 seconds on OAuth2. Enterprise plans may have custom limits. There is no bulk import API — each ticket, customer, and message requires a separate REST call.
- How long does a Salesforce Service Cloud to Gorgias migration take?
- It depends on volume. At Gorgias's max API throughput (40 req/20s), importing 100,000 tickets with 3 messages each takes roughly 55 hours of continuous import. Including extraction, transformation, testing, and validation, budget 1–3 weeks for a mid-size migration.
- How do I handle inactive Salesforce users in Gorgias?
- Map all historical tickets owned by inactive Salesforce users to a single 'Legacy Agent' profile in Gorgias. This preserves assignment history without paying for unnecessary Gorgias licenses.

