---
title: "Richpanel to HubSpot Service Hub Migration: A Technical Guide"
slug: richpanel-to-hubspot-service-hub-migration-a-technical-guide
date: 2026-08-04
author: Rishabh
categories: [Hubspot Service Hub, Migration Guide, Help Desk]
excerpt: "Technical guide to migrating from Richpanel to HubSpot Service Hub. Covers API extraction, data model mapping, rate limits, and the Help Desk workspace gotcha."
tldr: "Richpanel to HubSpot Service Hub migration is bottlenecked by Richpanel's 100 calls/min API limit. Migrated tickets appear on the Activity Timeline, not the Help Desk conversation pane."
canonical: https://clonepartner.com/blog/richpanel-to-hubspot-service-hub-migration-a-technical-guide/
---

# Richpanel to HubSpot Service Hub Migration: A Technical Guide


# Richpanel to HubSpot Service Hub Migration: A Technical Guide

> [!NOTE]
> **TL;DR:** A Richpanel to HubSpot Service Hub migration shifts your helpdesk from an e-commerce, order-centric conversation platform to a CRM-integrated ticket-and-engagement architecture. Richpanel's core objects — Conversations, Messages, Customers, Orders, Tags, and Teams — must be mapped to HubSpot Tickets, Engagements (emails/notes), Contacts, Custom Properties, and Owner assignments. The primary extraction bottleneck is Richpanel's API rate limit of **100 calls per minute** (492 status code on breach); at a default page size of 50 conversations per list call, extracting 50K conversations requires ~101K API calls (~17 hours). On the HubSpot side, tickets created via API don't populate the Help Desk workspace conversation thread — migrated history lives on the ticket Activity Timeline only. Automation rules, self-service portal flows, and widget configurations do not transfer. Realistic timeline: **1–3 weeks** for under 50K conversations, 3–5 weeks for larger datasets with heavy attachments and complex order-linked metadata.

## The Architectural Shift: E-commerce Helpdesk to CRM

Migrating from Richpanel to HubSpot Service Hub is not a 1:1 data transfer. It is a fundamental shift in how your customer support data is structured.

<cite index="22-2,22-6">Richpanel operates as a live chat and help desk software purpose-built for e-commerce.</cite> Its architecture assumes an order — usually synced from Shopify, Magento, or WooCommerce — is the central context of a conversation. A customer messages you, and that message is intrinsically linked to their most recent transaction. Everything — chat, email, social — flows into a unified conversation timeline linked directly to order data.

HubSpot Service Hub is a CRM-first platform. Its architecture assumes that a Contact or Company is the central context. Tickets are standalone objects, and the actual back-and-forth messages (emails, notes, calls) are separate "Engagement" objects that must be explicitly associated with the Ticket and the Contact through HubSpot's Associations API.

To successfully migrate, you must extract flat conversation threads from Richpanel and reconstruct them across 3 to 5 separate HubSpot API endpoints. There is no native import path that handles this end-to-end.

## Why Teams Migrate from Richpanel to HubSpot Service Hub

Teams leave Richpanel for HubSpot Service Hub for a few specific reasons:

- **CRM consolidation:** Teams already running HubSpot for Marketing or Sales want support in the same platform. Similar to the drivers behind [Intercom to HubSpot Service Hub migrations](https://clonepartner.com/blog/blog/how-to-migrate-intercom-to-hubspot-service-hub-2026-guide/), running Richpanel alongside HubSpot CRM means maintaining a separate integration just to keep customer context in sync.
- **Reporting depth:** <cite index="13-14,13-15">Richpanel's standard reports cover ticket volume, agent productivity, and channel performance, and you can export data for analysis in external tools like Excel or Google Sheets.</cite> HubSpot's custom reporting engine with cross-object datasets and attribution reporting gives teams more analytical power without exporting to spreadsheets.
- **Scaling beyond e-commerce:** <cite index="13-21">Richpanel works well for specific types of ecommerce brands but has clear limitations as businesses grow.</cite> Teams expanding into B2B, SaaS, or multi-vertical support need ticket pipelines, SLA management, and workflow automation that span sales and service — which is HubSpot's core strength.
- **Pricing structure:** <cite index="15-36,15-37">Richpanel charges one AI agent for $500/mo and $100/seat for the helpdesk.</cite> For teams not heavily leveraging AI resolution, HubSpot Service Hub Professional at $100/seat/month with included automation, knowledge base, and SLA tools can be more cost-effective (though you should account for mandatory onboarding fees, as detailed in our [HubSpot Service Hub to Help Scout migration guide](https://clonepartner.com/blog/blog/hubspot-service-hub-to-help-scout-migration-technical-guide/)).

> [!TIP]
> If your team is primarily a Shopify-native DTC brand with fewer than 10 agents and you don't use HubSpot CRM for anything else, the migration cost may outweigh the benefit. Richpanel's deep Shopify integration and self-service portal are hard to replicate in HubSpot without significant custom work.

## Richpanel vs. HubSpot Service Hub: Data Model Mapping

Before writing any extraction logic, define how Richpanel's schema translates to HubSpot's standard objects. This mapping is the foundation of your migration script.

| Richpanel Object | HubSpot Object | Notes |
| :--- | :--- | :--- |
| **Conversation** | **Ticket** | One conversation = one ticket. Map status to `hs_pipeline_stage`. |
| **Message (Agent/Customer)** | **Engagement (Email/Note)** | Customer/agent messages → Email Engagements. Internal comments → Note Engagements. |
| **Customer** | **Contact** | Map email, phone, name, addresses. De-duplicate against existing HubSpot contacts. |
| **Customer Address** | **Contact properties** | Richpanel stores billing/shipping separately; HubSpot has a single default address. Use custom properties for shipping. |
| **Order** | **Custom Object or Deal** | Requires HubSpot Enterprise for Custom Objects. Or store key order data as custom Ticket properties. |
| **Subscription** | **Custom Object or custom properties** | Same Enterprise requirement for Custom Objects. |
| **Tags** | **Ticket property (Dropdown/Multi-select)** | HubSpot doesn't have a native tag object on tickets; pre-define dropdown or multi-select properties. |
| **Agent** | **Owner** | Map Richpanel agent emails to HubSpot user emails. |
| **Team** | **Team** | Create matching teams in HubSpot before import. |
| **CSAT rating** | **Custom ticket property** | Richpanel CSAT data needs a custom property in HubSpot. |
| **Attachments** | **File Manager + Engagements** | Files must be uploaded to HubSpot's File API, then linked to the corresponding Engagement via `hs_attachment_ids`. |
| **Automation rules** | **Workflows** | **No export.** Must be rebuilt manually in Service Hub Professional+. |
| **Self-service portal flows** | **Knowledge Base + Chatflows** | **No export.** Requires full manual recreation. |
| **Macros / Canned responses** | **Snippets / Templates** | **No export.** Must be manually copied over. |

### The Order Context Problem

<cite index="12-6,12-7,12-8">Richpanel syncs order data to provide agents with complete customer purchase history and enables self-service order tracking. It also manages recurring subscription data and links orders to conversations so agents have full context when helping customers.</cite>

In HubSpot, this order-to-ticket linkage doesn't exist natively. You have three options:

1. **Custom Objects (Enterprise only):** Create an "Order" Custom Object and associate it with Tickets and Contacts. This preserves the relational model but requires HubSpot Enterprise.
2. **HubSpot's native Shopify integration:** If you connect Shopify to HubSpot, it syncs orders as Deals. You then need a script to associate historical HubSpot Tickets with the corresponding Deals based on the order ID referenced in the Richpanel export.
3. **Flatten into Ticket properties:** Create custom properties on the Ticket object (e.g., `richpanel_order_id`, `richpanel_order_value`, `richpanel_tracking_url`) and stamp the most relevant order data directly onto the ticket. Simpler, but you lose the relational model.

Most teams under 50K tickets pick option 3 for speed and adopt the Custom Object approach later if needed.

## How to Extract Data from Richpanel

Richpanel does not offer a one-click "export everything" button that yields complete conversation histories with attachments. You have three extraction paths: the REST API, the Graph API, and a limited CSV export.

### REST API (Primary for Message Detail)

<cite index="2-1,2-2,2-3,2-4,2-5,2-6">The Richpanel API is structured with REST in mind, with predictable resource-oriented URLs, JSON-encoded request/response bodies, and standard HTTP verbs. It covers Conversations (create, update, retrieve support tickets, add messages, assign to agents/teams, manage tags, track status), Customer Profiles, and Teams & Agents.</cite>

Key endpoints for extraction:

- **List conversations:** `GET https://api.richpanel.com/v1/tickets`
- **Get single conversation:** `GET https://api.richpanel.com/v1/tickets/{id}`
- **Get conversations by customer:** `GET https://api.richpanel.com/v1/tickets/{type}/{id}` (by email or phone)

Authentication uses an API key passed via the `x-richpanel-key` header:

```bash
curl -X GET "https://api.richpanel.com/v1/tickets/{id}" \
  -H "accept: application/json" \
  -H "x-richpanel-key: YOUR_API_KEY"
```

### Graph API (Bulk Extraction)

<cite index="34-7">The Graph API endpoint is at `https://graph.richpanel.com/data-api` and supports fetching Conversations and Customers with `first`, `offset`, and filter parameters.</cite>

The Graph API is better for bulk extraction because it supports pagination with offset-based cursors. Use it to pull conversation lists and total counts, then hit the REST API for full message content per conversation.

**Page size and call count:** The Richpanel Graph API returns a default of **50 conversations per page**. This means a list of 50K conversations requires 1,000 list calls to paginate through, plus 50,000 detail calls to retrieve full message bodies — approximately 51,000 total API calls for conversation metadata alone. Add additional calls for customer profile lookups and attachment downloads.

### Rate Limits — The Extraction Bottleneck

<cite index="34-1,34-2">Richpanel's Graph API has a rate limit of 100 calls per minute. After crossing usage within a minute, the API responds with a 492 status code.</cite> <cite index="34-3,34-4,34-5">Rate limit headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`) are sent on all API responses, and `Retry-After` headers indicate wait time once exceeded.</cite>

Richpanel does not currently offer webhooks for real-time event streaming. This means all delta sync logic must be poll-based, using the `updatedAt` filter parameter on the Conversations endpoint to retrieve records modified since a given timestamp.

At 100 calls/minute and a page size of 50 conversations per list call, here's the extraction math:

| Dataset Size | List Calls (50/page) | Detail Calls | Total Calls | Time at Rate Limit |
| :--- | :--- | :--- | :--- | :--- |
| 10K conversations | 200 | 10,000 | ~10,200 | ~1.7 hours |
| 50K conversations | 1,000 | 50,000 | ~51,000 | ~8.5 hours |
| 100K conversations | 2,000 | 100,000 | ~102,000 | ~17 hours |

*Note: These figures assume one detail call per conversation for full message bodies. Attachment downloads and customer profile lookups add additional calls on top.*

Always implement exponential backoff in your extraction scripts. Hitting the API too aggressively will result in 492 errors that interrupt your pipeline.

> [!WARNING]
> **CSV export limitation:** <cite index="40-1,40-2">Richpanel allows exporting conversations to CSV via bulk actions.</cite> But this export is metadata-level — it includes conversation IDs, statuses, assignees, and timestamps, but typically does not include full message thread content. Always verify your CSV export contains complete message bodies before relying on it as your primary extraction method.

### Singer Tap

<cite index="9-1">Richpanel maintains an official Singer tap (`tap-richpanel`) for extracting data.</cite> This is useful if you're already running a Singer/Meltano pipeline, but you'll still need custom transformation logic to reshape the data for HubSpot's schema.

### Extracting Users and Messages

Begin by extracting your user base. Query the Richpanel Customers endpoint, handling pagination to pull down the entire list. Store this in a staging database or structured JSON format. You need the Richpanel `user_id` to map conversations accurately later.

Richpanel groups messages inside conversations. Iterate through the Conversations endpoint to get metadata (status, assignee, tags, creation date). For each conversation, request the associated messages. This is where the 100 calls/minute rate limit becomes the primary bottleneck — if you have 200,000 conversations, you're making at least 200,000 additional API calls just to retrieve the message payloads.

> [!WARNING]
> **The multi-store problem:** <cite index="12-11,12-12,12-13">Many Richpanel endpoints require an `appClientId` parameter — a unique identifier representing a specific store or brand. Find your app client IDs in Richpanel Settings → Connected Apps.</cite> If you run multiple stores in Richpanel, you need to extract each store's data separately and decide how to structure them in HubSpot (single portal with brand properties, or separate portals).

## How to Load Data into HubSpot Service Hub

Loading data into HubSpot is structurally complex. You cannot simply `POST` a ticket with an array of messages. HubSpot requires a multi-step relational build for every single conversation. The loading sequence matters because HubSpot requires objects to exist before you can associate them.

### Authentication: Private App Tokens (Required)

HubSpot deprecated legacy API key authentication in November 2022. All API requests now require a **Private App access token**, passed as a Bearer token in the Authorization header:

```bash
curl -H "Authorization: Bearer YOUR_PRIVATE_APP_TOKEN" \
     -H "Content-Type: application/json" \
     https://api.hubapi.com/crm/v3/objects/tickets
```

Create your Private App under HubSpot Settings → Integrations → Private Apps. Scope your token to at minimum: `crm.objects.tickets.write`, `crm.objects.contacts.write`, `crm.objects.notes.write`, `crm.objects.emails.write`, `crm.associations.write`, and `files.ui_hidden.write` for migration use.

**Testing environment:** HubSpot provides free developer sandbox portals at `app.hubspot.com` under developer accounts. Always test your migration script against a sandbox portal before running against production. Sandbox portals are isolated — changes do not affect your production HubSpot account.

### Step 1: Upsert Contacts

Use the HubSpot Contacts API (`/crm/v3/objects/contacts`) to create or update contacts. Use the customer's email address as the unique identifier. De-duplicate against existing HubSpot contacts — if your CRM already has contacts, importing Richpanel customers without de-duplication will create duplicates.

Capture the HubSpot `contact_id` returned by the API and map it against the Richpanel `user_id` in your staging database. You will need this for associations.

### Step 2: Create Custom Properties and Pipeline Stages

Before creating tickets, set up custom properties in HubSpot for any Richpanel-specific fields: order ID, CSAT score, channel source, Richpanel conversation ID (for cross-referencing and rollback). Create ticket pipelines and stages that map to your Richpanel conversation statuses (Open, Pending, Resolved, Closed).

**Why store `richpanel_conversation_id`:** If a batch of tickets fails mid-migration, you need a way to identify which Richpanel conversations were successfully loaded and which weren't. Storing `richpanel_conversation_id` as a custom property on every HubSpot ticket allows you to query HubSpot for existing records before re-running a failed batch — preventing duplicates without a full re-extraction from Richpanel.

**Field length limits:** HubSpot `text` properties have a maximum of 65,536 characters. `textarea` properties support up to 65,536 characters as well, but single-line text fields are capped at 255 characters. Long email bodies in Engagements that exceed field limits will be silently truncated. Store message bodies in `textarea` or `html_text` fields, not single-line text properties.

### Step 3: Create Tickets

Use the Tickets API (`/crm/v3/objects/tickets`) to create the ticket container.

You must map the Richpanel status (e.g., Open, Pending, Resolved) to HubSpot's specific `hs_pipeline_stage` internal IDs. Do not pass the string "Closed" — you must pass the exact alphanumeric ID of the pipeline stage in your specific HubSpot portal. Passing status strings instead of stage IDs will fail silently or error.

<cite index="51-1,51-2,51-4,51-5">To create new tickets, make a POST request to the tickets endpoint. Include ticket data in a properties object. You can also add an associations object to associate your new ticket with existing records (contacts, companies) or activities (meetings, notes).</cite>

<cite index="51-8">When creating a new ticket, include `subject` (the ticket's name), `hs_pipeline_stage` (the ticket's status), and `hs_pipeline` if you have multiple pipelines.</cite>

```json
// Example HubSpot Ticket Payload
{
  "properties": {
    "hs_pipeline": "0",
    "hs_pipeline_stage": "4",
    "subject": "Where is my order?",
    "content": "Original ticket created in Richpanel.",
    "createdate": "2023-10-15T14:30:00Z",
    "richpanel_order_id": "RP-12345",
    "richpanel_conversation_id": "conv_abc123"
  }
}
```

### Step 4: Handle Attachments via the File API

Before you can create the messages (Engagements), handle any file attachments (images, PDFs, receipts) included in the Richpanel messages:

1. Download the file from the Richpanel URL to your local server.
2. Upload the file to the HubSpot File Manager API (`/filemanager/api/v3/files/upload`).
3. Set the `folderPath` to something hidden (e.g., `migration_attachments`) so it doesn't clutter the marketing team's file manager.
4. Capture the returned HubSpot `file_id`.

### Step 5: Create Engagements (The Messages)

For every message in the Richpanel conversation, create an Engagement in HubSpot.

Use `/crm/v3/objects/emails` for customer/agent replies and `/crm/v3/objects/notes` for internal agent whispers. When creating the Engagement, pass the `file_id` from Step 4 into the `hs_attachment_ids` property. Set `hs_timestamp` to the original Richpanel message creation time to preserve chronological ordering.

**HTML vs. plain text fidelity:** Richpanel stores many message bodies as HTML (inline styling, image tags, anchor elements). HubSpot Email Engagements accept HTML in the `hs_email_html` property and plain text in `hs_email_text`. If you push raw HTML into `hs_email_text`, the angle brackets will display as literal characters in the HubSpot UI. Always map Richpanel HTML message bodies to `hs_email_html` and strip or convert a plain-text version for `hs_email_text`. For Note Engagements, the `hs_note_body` property accepts basic HTML, but complex inline styles may be stripped by HubSpot's sanitizer.

### Step 6: The Associations API

This is the most critical step. At this point, you have a Contact, a Ticket, and several Engagements floating independently in HubSpot. You must link them together using the Associations API (`/crm/v3/associations/{fromObjectType}/{toObjectType}/batch`).

The Associations API requires specific `associationTypeId` values — you cannot pass string labels. Key type IDs for migration:

| Association | `associationTypeId` |
| :--- | :--- |
| Contact → Ticket | `16` |
| Ticket → Contact | `15` |
| Email Engagement → Ticket | `210` |
| Ticket → Email Engagement | `211` |
| Email Engagement → Contact | `198` |
| Note Engagement → Ticket | `214` |
| Note Engagement → Contact | `202` |

For a single migrated ticket, you must execute:

- Associate Contact → Ticket (`typeId: 16`)
- Associate Email Engagement 1 → Ticket (`typeId: 210`)
- Associate Email Engagement 1 → Contact (`typeId: 198`)
- Associate Email Engagement 2 → Ticket (`typeId: 210`)
- Associate Email Engagement 2 → Contact (`typeId: 198`)

If you skip associating the Engagements to the Contact, the ticket thread will appear on the Ticket record, but it will not show up on the customer's centralized CRM timeline.

> [!WARNING]
> **Association type IDs can vary by portal configuration and HubSpot updates.** Validate the IDs above against your specific portal using `GET /crm/v4/associations/{fromObjectType}/{toObjectType}/labels` before running at scale. Using a wrong `associationTypeId` produces a 400 error rather than a silent failure, so these are detectable.

### Batch Endpoints and HubSpot Rate Limits

Use batch endpoints wherever possible. <cite index="30-10,30-11">A batch read of 100 contacts costs 1 API request, not 100. Refactor any loop that reads single records.</cite> The same principle applies to batch create — `POST /crm/v3/objects/tickets/batch/create` accepts up to 100 tickets per call.

<cite index="30-2,30-8">HubSpot Pro and Enterprise accounts have 100 requests per 10 seconds and 250,000 requests per day, with separate limits on Search API (4 requests per second).</cite>

<cite index="27-8,27-9,27-10,27-11,27-12">HubSpot uses a token bucket model for rate limiting — imagine a bucket holding 100 tokens (for a Starter account). Every request removes one token. The bucket refills at roughly 10 tokens per second. When the bucket is empty, you get a 429.</cite>

For a migration loading 50K tickets with associated engagements (~150K total API calls including associations), expect roughly 4–6 hours of loading time at a sustained rate with proper backoff logic.

### HubSpot CSV Import (Limited Alternative)

<cite index="59-1,59-2">HubSpot's import tool lets you add objects like contacts, companies, deals, and tickets by uploading a spreadsheet and mapping its columns to HubSpot properties to create new records, update existing ones, and define relationships.</cite>

This works for Contacts and basic Ticket records but **does not support importing Engagements (emails, notes)** or creating Conversation threads. If you need message history on tickets, you must use the API.

## The Help Desk Workspace Gotcha

This is the single most common surprise in HubSpot Service Hub migrations (a structural quirk we also highlight in our [Help Scout to HubSpot Service Hub migration guide](https://clonepartner.com/blog/blog/help-scout-to-hubspot-service-hub-migration-technical-guide/)).

<cite index="62-4,62-5,62-6">Creating a ticket via the API works — it creates a Contact record and a Ticket with relevant properties and associations. However, it doesn't display the Ticket Details in the Help Desk conversation thread. The ticket is added to the Help Desk queue, but the original message is ignored, and agents have no context.</cite>

<cite index="63-11">When you associate an Email engagement with a Ticket, you see no Conversations associated with that Ticket in the Help Desk view.</cite> <cite index="63-12">However, you should see that associated Email when viewing the Ticket's Activities timeline.</cite>

In practice, this means:

- **Migrated tickets appear in Help Desk** but with empty conversation threads
- **Message history is visible on the Ticket record's Activity Timeline** — agents can see it, but not from the Help Desk workspace's conversation pane
- **Only tickets created through HubSpot's native channels** (connected inbox, chat, forms) automatically generate conversation threads

> [!CAUTION]
> **This is a platform limitation, not a migration bug.** HubSpot distinguishes between "Engagement" objects (API-created emails and notes) and "Conversation" objects (native channel messages). There is currently no public API to create Conversation thread objects. Your migrated ticket history will live on the Activity Timeline, not in the conversation pane. Set agent expectations before cutover.

## Preserving Historical Timestamps

A common failure mode in helpdesk migrations is losing the original timestamp of a ticket. If you do not explicitly set the creation dates, HubSpot will stamp every migrated ticket and message with today's date.

To prevent this, pass the original Richpanel timestamp into the `createdate` property during the initial `POST` request. For engagements, set `hs_timestamp` to the original Richpanel message creation time. HubSpot accepts ISO 8601 formatted datetime strings (e.g., `2023-10-15T14:30:00Z`) or UNIX timestamps in milliseconds.

**Important:** If you update a ticket after initial creation — for example, to add a custom property you forgot — HubSpot may overwrite `hs_lastmodifieddate` but will not overwrite `createdate` if it was explicitly set on creation. Always set `createdate` in the original `POST` body, not in a subsequent `PATCH`. Verify this behavior in a sandbox portal before running at scale.

## Error Taxonomy: How Failures Manifest

Not all HubSpot API failures are equal. Understanding how different error types surface prevents hours of debugging:

| Error Type | HTTP Status | Cause | Detection |
| :--- | :--- | :--- | :--- |
| Invalid pipeline stage string | `400` | Passing `"Closed"` instead of stage ID | Returns error body with `invalidInput` |
| Wrong `associationTypeId` | `400` | Using string labels or incorrect numeric ID | Returns `INVALID_ASSOCIATION_TYPE` error |
| Rate limit exceeded | `429` | Exceeding 100 req/10 sec | `Retry-After` header present |
| Missing required property | `400` | No `subject` or `hs_pipeline_stage` | Returns `MISSING_REQUIRED_FIELD` |
| Duplicate contact on create | `409` | Contact with email already exists | Use upsert endpoint to prevent |
| HTML in plain-text field | Silent truncation or garbled display | `hs_email_html` content placed in `hs_email_text` | Visual inspection of migrated records |
| File attachment not linked | `200` (silent) | File uploaded but `hs_attachment_ids` not set | Engagement exists with no attachment |
| Field length exceeded | Silent truncation | Text > 65,536 chars in textarea field | Compare character counts pre/post |

The most dangerous failure modes are the silent ones — where HubSpot returns `200 OK` but the data is incomplete. HTML-in-plain-text, unlinked attachments, and truncated long-form message bodies all return 200 responses. These require sample auditing, not just status code checking.

## What Doesn't Transfer (and Must Be Rebuilt)

Data migrates; logic does not. <cite index="48-4">Automation rules, self-service portal flows, and widget configurations do not transfer — they require manual recreation on whatever platform you move to.</cite>

Here's the full list of Richpanel features with no migration path:

- **Self-service portal flows:** Richpanel's UI-driven self-service (order tracking, returns, cancellations) has no HubSpot equivalent. Build this with HubSpot's Knowledge Base, Chatflows, or a custom portal.
- **Automation rules:** Richpanel's auto-assignment, auto-tagging, and routing rules must be rebuilt as HubSpot Workflows (requires Service Hub Professional or Enterprise).
- **Macros and canned responses:** Must be manually copied and recreated as HubSpot Snippets or Templates.
- **CSAT survey configuration:** Rebuild in HubSpot's Customer Feedback tool.
- **SLA policies:** Recreate in HubSpot's SLA settings (available on Professional+).
- **Richpanel AI agent SOPs:** These don't transfer. If you're using HubSpot's Breeze AI, you'll need to retrain it on your knowledge base and response patterns.
- **Custom agent views:** Filtered views in Richpanel must be rebuilt as saved Ticket Views in HubSpot.
- **Shopify deep integration:** Richpanel's native order actions (refund, cancel, exchange from within the helpdesk) don't exist in HubSpot out of the box. You'll need the HubSpot-Shopify integration plus potentially custom workflows.

## The Delta Sync Strategy (Zero Downtime)

Migrating 100,000+ tickets takes time. Because API rate limits restrict how fast you can push data into HubSpot, a full migration might take several days to process. During this time, your support team must continue working in Richpanel.

Because Richpanel does not support webhooks for real-time event streaming, all delta capture must be poll-based. Use the `updatedAt` filter on the Richpanel Conversations endpoint with a timestamp corresponding to when your last extraction completed. This identifies new and modified conversations since your previous run.

To achieve zero downtime, execute a phased delta sync:

1. **The Historical Load:** Extract and load all tickets closed prior to your migration start date. This moves ~90% of your data without impacting active operations.
2. **The Active Load:** Move all "Open" and "Pending" tickets over a weekend. Instruct your agents to log out of Richpanel on Friday and log into HubSpot on Monday.
3. **The Delta Catch-up:** Run a final script that queries Richpanel for any tickets created or updated *during* the weekend cutover window (using `updatedAt` filter), and upsert those changes into HubSpot using `richpanel_conversation_id` to match against existing records.
4. **Channel redirect:** Redirect incoming channels (email, chat, social) to HubSpot. Disable Richpanel's active channels to prevent split-brain (tickets landing in both systems).
5. **Agent briefing:** Brief agents on where to find migrated history (Activity Timeline, not Help Desk conversation pane).

This phased approach ensures data integrity without forcing your support team to halt operations.

## Validating the Migration

Never assume the API calls succeeded just because you received a 200 OK response. You must validate the data payload.

- **Count check:** Total tickets in HubSpot should match total conversations in Richpanel.
- **Sample audit:** Pick 50–100 random tickets and programmatically verify that the number of messages in Richpanel matches the number of Engagements associated with that ticket in HubSpot.
- **Association check:** Every ticket should be associated with a Contact. Every Engagement should be associated with both its parent Ticket and the Contact. Use `GET /crm/v3/associations/ticket/contact/batch/read` to verify at scale.
- **Attachment check:** Manually inspect 10–20 attachment-heavy tickets in the HubSpot UI to ensure files open correctly and HTML formatting is preserved.
- **HTML fidelity check:** Open 10–20 tickets that contained HTML-formatted messages in Richpanel and confirm the formatting renders correctly in HubSpot — not as raw angle brackets.
- **Owner check:** Confirm agent/owner assignments are correct.
- **Timestamp check:** Verify that ticket creation dates and message timestamps reflect the original Richpanel dates, not the migration date.
- **Truncation check:** For any conversation with unusually long messages (customer emails, order notes), compare character counts between Richpanel source and HubSpot destination to detect silent truncation.

## Common Failure Modes

**1. Relying on CSV export for message content.**
Richpanel's CSV export is useful for metadata but unreliable for full message bodies. Always extract via API.

**2. Not accounting for the 100 calls/minute rate limit and page size.**
At 100 calls/min and 50 conversations per page, a 50K conversation extraction requires ~51K API calls (~8.5 hours for list + detail calls combined, before attachments). Plan for this. Don't schedule extraction during business hours if your team is still actively using Richpanel.

**3. Expecting migrated tickets to work like native Help Desk tickets.**
They won't. The Help Desk conversation pane will be empty. Train agents before cutover.

**4. Skipping de-duplication.**
If your HubSpot CRM already has contacts, importing Richpanel customers without de-duplication will create duplicates. Always match on email before creating new contact records.

**5. Forgetting the multi-store problem.**
If you run multiple stores in Richpanel, each with its own `appClientId`, you need to extract each store's data separately and decide how to structure them in HubSpot.

**6. Using wrong or hardcoded `associationTypeId` values.**
The Associations API returns a 400 error for invalid type IDs, but using a valid-but-wrong type ID (one that exists but maps to the wrong relationship) will silently create incorrect associations. Always validate type IDs for your specific portal using the labels endpoint before bulk runs.

**7. Pushing HTML message bodies into plain-text fields.**
This produces garbled output (raw HTML tags) in the HubSpot UI and returns 200 OK — it won't surface in your error logs. Always map Richpanel HTML bodies to `hs_email_html`, not `hs_email_text`.

**8. Using legacy API key authentication.**
HubSpot deprecated API keys in November 2022. Scripts using `hapikey=YOUR_KEY` query parameters will fail. Use Private App Bearer tokens.

## Timeline and Resource Planning

| Scenario | Dataset Size | Team | Timeline |
| :--- | :--- | :--- | :--- |
| Small DTC brand | <10K conversations, no orders migration | 1 engineer | 1–2 weeks |
| Mid-size e-commerce | 10K–50K conversations, order data | 1–2 engineers | 2–3 weeks |
| Multi-store operation | 50K+ conversations, multiple brands, attachments | 2+ engineers | 3–5 weeks |

Most of the time is spent on extraction (40%), transformation and edge-case handling (30%), and validation (20%). The actual HubSpot loading is fast — typically under a day for most datasets.

<cite index="48-5">Plan 1–3 weeks for a complete migration, with most of that time spent on workflow reconstruction rather than data transfer.</cite>

If you're also rebuilding self-service flows, automation rules, and knowledge base content, add another 1–2 weeks to the timeline.

## When to Self-Serve vs. When to Get Help

**Self-serve if:**

- Under 10K conversations
- No attachment-heavy tickets
- No order/subscription data to migrate
- You have an engineer comfortable with REST APIs and HubSpot's CRM API
- You accept that migrated history lives on the Activity Timeline only

**Get help if:**

- Over 50K conversations
- Multi-store Richpanel setup
- Complex order data that needs Custom Objects
- You need attachment migration with full fidelity
- You can't afford to lose any conversation history
- You don't have engineering bandwidth for a 2–3 week project

We've handled migrations from Richpanel and other e-commerce helpdesks at ClonePartner — [our Richpanel alternatives guide](https://clonepartner.com/blog/blog/top-richpanel-alternatives-2026-pricing-migration/) covers the broader landscape if you're still evaluating. For teams that have already decided on HubSpot, our [HubSpot Service Hub migration checklist](https://clonepartner.com/blog/blog/hubspot-service-hub-migration-checklist/) covers the destination-side setup in detail.

> Migrating from Richpanel to HubSpot Service Hub? Our engineering team has handled 1,500+ helpdesk migrations. We'll map your Richpanel data model, handle the API extraction at scale, and deliver validated data in HubSpot. Book a free 30-minute call to scope your migration.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can I migrate Richpanel conversation history to HubSpot Service Hub?

Yes, but with a limitation. Conversation messages are migrated as HubSpot Engagement objects (emails and notes) and appear on the ticket's Activity Timeline. They do not appear in the Help Desk workspace's conversation thread pane — that only shows messages from HubSpot's native channels.

### How long does a Richpanel to HubSpot migration take?

1–3 weeks for teams with under 50K conversations, 3–5 weeks for larger multi-store setups. Richpanel's API rate limit of 100 calls per minute is the main extraction bottleneck. Workflow and automation reconstruction adds 1–2 weeks on top.

### Can I migrate Shopify order history from Richpanel to HubSpot?

Yes, but it requires mapping. You can use HubSpot's native Shopify integration to sync orders as Deals, create Custom Objects (Enterprise only), or map specific order data to custom Ticket properties during the migration.

### Do Richpanel automation rules transfer to HubSpot?

No. Richpanel automation rules, self-service portal flows, macros, and widget configurations have no export mechanism. They must be manually rebuilt as HubSpot Workflows, Snippets, Knowledge Base articles, and Chatflows.

### How do I keep ticket timestamps accurate during the migration?

You must explicitly pass the original Richpanel timestamps into the createdate or hs_createdate properties when making POST requests to HubSpot's APIs. For engagements, use hs_timestamp. Otherwise, HubSpot defaults to the date of the API call.
