Skip to content

Dixa to Crisp Migration: The Complete Technical Guide

A technical guide to migrating from Dixa to Crisp: API extraction, data mapping, custom attribute translation, rate limits, and step-by-step implementation.

Rishabh Rishabh · · 25 min read
Dixa to Crisp Migration: The Complete 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

What Is a Dixa to Crisp Migration?

A Dixa to Crisp migration is the process of extracting conversations, messages, end users (contacts), tags, custom attributes, internal notes, CSAT ratings, and attachments from Dixa and loading them into Crisp's conversation and people data model. The goal is to preserve complete customer interaction history so agents in Crisp can see every previous exchange without context gaps.

The real question teams ask is rarely "How do I export CSVs?" It's "How do I keep enough operational history that my agents can work in Crisp on day one without losing context?"

Why Teams Migrate from Dixa to Crisp

Teams switch from Dixa to Crisp for specific, measurable reasons:

  1. Cost structure. Dixa starts at €89/user/month (Growth plan) with a minimum of 7 seats, and API access is only available on Ultimate (€139) or Prime (€179). Crisp uses flat-rate workspace pricing — the Plus plan at $295/month includes unlimited conversations and up to 20 agents. Worked example: a 15-agent team on Dixa Ultimate pays 15 × €139 = €2,085/month (≈$2,290). The same team on Crisp Plus pays $295/month — an 87% reduction in platform cost.
  2. Simplicity. Crisp is a lighter platform: live chat, shared inbox, knowledge base, CRM contacts, campaigns, and an AI agent in a single workspace. Teams that don't need Dixa's native telephony, IVR, and advanced routing often find Crisp's simpler model faster to operate.
  3. Marketing automation. Crisp includes campaign features (email, chat, in-app) natively, with targeting based on segments, events, and custom data. Dixa has no native outbound marketing layer.
  4. Agency and multi-brand use cases. Crisp's per-workspace pricing makes it practical to run multiple brand workspaces without per-seat multiplication.

Core Architectural Differences

Three architectural gaps drive the complexity of this migration:

Dimension Dixa Crisp
Routing model Offer-based push routing via Flow Builder Inbox-based with Workflow automation
Contact model End Users with typed custom attributes (text, dropdown, integer) People profiles with freeform key-value custom data
Channel architecture Native phone, email, chat, social in one seat Chat, email, social via plugins; phone via third-party integrations (Ringover, Aircall)
Custom attributes Schema-defined per entity type (conversation, end user) Freeform data objects on sessions and contacts
Tags Tags (string labels on conversations) Segments (labels on conversations and contacts)
Knowledge base Dixa Knowledge (categories, articles) Crisp Helpdesk (categories, articles, markdown-based)
Conversation threading Linked conversations via link_type and linked_to fields (parent/child, merged) No native linked-conversation concept; threading is sequential within a single session

The biggest data model gap: Dixa exports conversation records with queue, assignee, ratings, wrap-up notes, linked-conversation metadata, and telephony fields. Crisp's model centers on people profiles, company info, custom data, conversations, messages, and inboxes. Dixa's linked-conversation relationships (parent/child, merged) have no structural equivalent in Crisp — you must flatten these into notes or custom data fields that preserve the audit trail without the relational link. If you have CRM-style objects like accounts, leads, or opportunities, those should stay in your CRM (HubSpot, Salesforce, etc.) and sync into Crisp as enrichment — not be forced into the helpdesk.

For related Dixa migration guides, see How to Export Data from Dixa: Methods, API Limits & Data Mapping and Dixa to Freshdesk Migration: The Complete Technical Guide.

Migration Approaches: Which Method Fits Your Team

1. Crisp's Official Import Tool (Custom Adapter)

How it works: Crisp maintains an open-source Node.js tool called crisp-import-conversations that reads exported conversation files (JSON, JSONL, CSV, XML) and writes them into Crisp via API. It ships with adapters for Zendesk, Gorgias, Help Scout, Tidio, GrooveHQ, and WHMCS — but no Dixa adapter exists. You'd export Dixa data via the Exports API, then write a custom adapter to transform Dixa's format into Crisp's expected schema.

When to use it: Mid-complexity migrations where you're comfortable writing a Node.js adapter (~200–400 lines). The tool handles API authentication, rate-limit retries, resume-on-failure, and conversation creation.

Pros: Built-in resume capability (skips previously imported conversations), handles message ordering and timestamp preservation, works on all Crisp plans.

Cons: No Dixa adapter — you build it yourself. Adapter documentation is sparse. Provided adapters "may break anytime" per the repo's own warning. The tool does not handle contact creation — people profiles must be imported separately.

Complexity: Medium

2. API-Based Custom Migration (Dixa API → Crisp API)

How it works: Write a custom ETL script that calls the Dixa Exports API (https://exports.dixa.io/v1/) to extract conversations and messages, calls the Dixa API (https://dev.dixa.io/v1/) to extract end users, tags, and custom attributes, transforms the data, and loads it into Crisp using the REST API. The Crisp Python SDK (python-crisp-api) or Node.js SDK can be used for the load phase. (docs.dixa.io)

When to use it: Full-control migrations where you need contacts, custom data, segments, and conversation history in a single coordinated pipeline.

Pros: Total control over data transformation and error handling. Can handle contacts and conversations in one pipeline. Preserves original timestamps on Crisp messages.

Cons: Dixa Exports API queries are limited to 31-day windows — extracting years of history requires iterating month by month. Significant development effort (120–200 engineer-hours for a full implementation). Both platforms impose rate limits that require careful handling.

Complexity: High

3. CSV Export + Crisp Contact Import (Partial Migration)

How it works: Export contacts from Dixa as CSV via the admin UI or Exports API, and import them into Crisp via the built-in CSV import. Crisp's import supports email, full name, company, segments, created dates, and custom data mapping. (help.crisp.chat)

When to use it: When you only need to carry over contact data and are willing to leave historical conversations behind.

Pros: No code required. Fastest path to get contacts into Crisp. Crisp CSV import supports custom data mapping.

Cons: No conversation history, message, attachment, or note migration. Dixa CSV exports don't include message bodies. Large CSVs may need splitting below 10 MB.

Complexity: Low

4. Middleware Platforms (Zapier, Make, n8n)

How it works: Build automation flows that pull data from Dixa and push it to Crisp using pre-built connectors or HTTP request modules.

When to use it: Ongoing, real-time sync between systems after the historical migration is complete. Not practical for bulk historical migration.

Pros: No-code setup for simple triggers. Good for post-cutover contact sync.

Cons: Not designed for bulk historical data. Task limits and API rate limits make large migrations cost-prohibitive. Dixa's Zapier integration has limited trigger/action coverage. Hard to make idempotent at scale.

Complexity: Low (for sync), impractical (for bulk migration)

5. Managed Migration Service

How it works: A migration partner handles the full pipeline — extraction, transformation, loading, validation — using custom scripts tuned to both platforms' APIs and edge cases.

When to use it: When you have 50K+ conversations, complex custom attributes, limited engineering bandwidth, or zero tolerance for data loss.

Pros: Handles API quota negotiation, rate limiting, error recovery, and edge cases (orphaned messages, missing end users, attachment re-hosting). Includes validation and rollback planning.

Cons: External cost. Requires sharing API credentials with the partner.

Complexity: Low (for your team)

Approach Comparison

Approach Conversations Contacts Custom Data Complexity Best For
Crisp Import Tool (custom adapter) ❌ (separate step) Partial Medium 10K–200K conversations, dev team available
Custom API Pipeline High 200K+ conversations, full data fidelity
CSV Export + Import Partial Low Contact-only migration
Middleware (Zapier/Make) ❌ (bulk) ✅ (trickle) Low Post-migration ongoing sync
Managed Service Low (your side) 50K+, limited eng bandwidth

Recommendations by Scenario

  • Small team, <10K conversations, dev available: Crisp import tool with a custom Dixa adapter. Budget 40–80 hours.
  • Mid-market, 10K–200K conversations: Custom API pipeline or managed service. The Dixa Exports API's 31-day window constraint and Crisp's daily quotas add real complexity.
  • Enterprise, 200K+ conversations: Managed migration service. The API coordination alone justifies the cost.
  • Contacts only, no history needed: CSV export from Dixa → CSV import into Crisp. Done in an afternoon.
  • Ongoing sync post-cutover: Zapier or Make for new conversation notifications or contact sync.

The summary: CSV for contacts only, API or ETL for history, middleware for ongoing sync, managed service when you cannot afford mistakes. Neither platform exposes a one-click Dixa-to-Crisp translator.

When to Use a Managed Migration Service

Building a migration script in-house always looks like a two-day project on a Jira board. In reality, the hidden costs stack up:

  • API quota coordination. Crisp's plugin token quotas start at approximately 5,000 requests/day. Large imports require requesting increases through the Marketplace, which adds days of back-and-forth.
  • 31-day export windows. Dixa's Exports API limits queries to 31-day ranges. Three years of history means 36+ sequential API calls just for extraction, each needing error handling.
  • Attachment re-hosting. Dixa attachment URLs may expire or require authentication. Each file needs to be downloaded and re-uploaded to Crisp.
  • Custom attribute translation. Dixa uses typed, schema-defined attributes (dropdown, integer, text) mapped by UUID. Crisp uses freeform key-value pairs. The translation logic is not trivial.
  • Internal notes extraction. Dixa's message_export endpoint does not include internal notes. You must separately extract conversation_wrapup_notes from the conversation export or the Dixa API conversation endpoint. Miss this and you lose all internal notes. (docs.dixa.io)
  • Email suppression. Crisp recommends contacting support before importing to block outbound emails from imported conversations. Miss this step and your customers get unexpected messages.
  • Webhook migration. If your Dixa instance pushes events to downstream systems (CRMs, analytics, warehouses) via webhooks, those configurations must be recreated in Crisp's webhook system or via plugin integrations. This is often overlooked until post-cutover when downstream systems stop receiving events.

When you build in-house, your engineers absorb all of this operational risk. If historical tickets map to the wrong Crisp profiles, your support team loses context and customers get a worse experience.

Pre-Migration Planning

Do not touch an API key until you've audited your Dixa environment.

Data Audit Checklist

Data Object Where to Find It Migration Priority
Conversations Exports API (/v1/conversation_export) High — core history
Messages Exports API (/v1/message_export) High — conversation content
End Users Dixa API (/v1/endusers) High — maps to Crisp people
Internal / Wrap-up Notes Dixa API (conversation conversation_wrapup_notes) Medium — not in message export
Tags Dixa API (/v1/tags) High — maps to Crisp segments
Custom Attributes Dixa API (/v1/custom-attributes) Medium–High
CSAT Ratings Dixa API (/v1/conversations/{id}/ratings) Medium
Agents Dixa API (/v1/agents) Low (recreate manually in Crisp)
Queues Dixa API (/v1/queues) Low (rebuild as Crisp Inboxes)
Flows No API export N/A (rebuild manually)
Knowledge Base Dixa Knowledge (no bulk API export) Medium
Attachments / Recordings Dixa Files API Medium–High
Webhook configurations Dixa admin UI (Settings → Webhooks) Medium — rebuild in Crisp

Identify What Not to Migrate

  • Closed spam conversations — filter these out during extraction
  • Bot-only conversations with no human reply — often noise
  • Test/sandbox data — exclude agent test conversations
  • Conversations older than 3 years — truncating old data speeds up the migration and reduces API load; decide based on actual agent usage patterns
  • Deactivated agent records — Dixa's GET /v1/agents only returns active agents by default; pass ?active=false to include deactivated ones

GDPR and Data Residency Considerations

Both Dixa and Crisp serve EU customers and store data in the EU (Dixa in AWS EU regions, Crisp in EU data centers). During migration, data transits through your extraction infrastructure. Ensure:

  • Your extraction scripts run on infrastructure within an approved data processing region
  • Temporary data stores (JSON files, databases) used during transformation are encrypted at rest
  • Your Data Processing Agreement (DPA) with Crisp is executed before importing personal data
  • If using a managed migration service, verify the partner has an appropriate DPA and processes data within compliant jurisdictions
  • After migration, confirm with Dixa their data retention and deletion policies for your account — Dixa's standard terms may retain data for a period after account closure, which affects your GDPR obligations

Dixa Data Retention After Account Closure

Before canceling your Dixa subscription, confirm in writing how long Dixa retains your data post-cancellation. Dixa's standard terms typically allow data retrieval for 30 days after contract end, after which data may be permanently deleted. This creates a hard deadline for your extraction phase.

Migration Strategy

Three options:

  • Big bang: Extract everything, import in one batch, cut over. Works well for <50K conversations. Run a full historical migration, pause Dixa, run a delta migration for the final few hours of tickets, and route DNS/email forwarding to Crisp.
  • Phased: Migrate in time-based batches (last 6 months first, then older history). Reduces risk and allows validation between phases. Crisp's own migration guides recommend stabilizing the new workflow before loading historical conversations. (help.crisp.chat)
  • Incremental: Migrate historical data, then run a delta sync for conversations created during the migration window. Requires timestamp tracking.

Timeline Estimates by Phase

Phase Scope: <10K convos Scope: 50K–150K convos Scope: 200K+ convos
Data audit & planning 1–2 days 2–3 days 3–5 days
Extraction from Dixa 2–4 hours 1–3 days 3–7 days
Transformation 1–2 days 3–5 days 5–10 days
Loading into Crisp 4–8 hours 2–5 days 5–14 days
Validation & UAT 1–2 days 2–3 days 3–5 days
Cutover & monitoring 1 day 1–2 days 2–3 days
Total 1–2 weeks 2–4 weeks 3–6 weeks

Extraction time is dominated by the Dixa Exports API rate limits (10 conversation export requests/minute, 3 message export requests/minute) and the 31-day windowing constraint. Loading time depends on Crisp plugin token daily quotas and any quota increases obtained from the Marketplace.

Data Model & Object Mapping

This is where most Dixa-to-Crisp migrations succeed or fail. The platforms share a conversation-centric model, but the details diverge.

Object-Level Mapping

Dixa Object Crisp Equivalent Notes
End User People Profile Match on email. Crisp enforces email uniqueness.
Conversation Conversation (Session) 1:1 mapping. Both use conversation as the core unit.
Message Message Crisp supports types: text, note, file, audio, event.
Internal Note / Wrap-up Note Message (type: note) Dixa stores wrap-up notes separately; Crisp treats all notes as message type. Both types must be extracted and imported.
Tag Segment Dixa tags are conversation-level; Crisp segments apply to both conversations and contacts.
Custom Attribute (End User) People Custom Data Dixa: typed schema (UUID-keyed). Crisp: freeform key-value JSON in data object.
Custom Attribute (Conversation) Session Data Same transformation challenge.
Queue Inbox Rebuild manually; routing logic differs fundamentally.
Flow Workflow No migration path; rebuild in Crisp's visual builder.
CSAT Rating No direct structural equivalent Store as conversation metadata/custom data or re-implement using Crisp's native satisfaction survey. Historical ratings import as data.csat_score and data.csat_comment.
Agent Operator Recreate manually in Crisp.
Linked Conversations No equivalent Dixa's link_type (merged, parent/child) has no Crisp analog. Preserve as data.dixa_linked_to and data.dixa_link_type custom fields on the session.
Webhooks Plugin webhooks or Crisp hooks Must be rebuilt; endpoint configurations and event types differ.

Field-Level Mapping: End Users → People Profiles

Dixa Field Crisp Field Transform
email email Direct
displayName person.nickname Direct
phoneNumber phone Normalize to E.164
additionalEmails Not natively supported Store in custom data as data.additional_emails (JSON array)
additionalPhoneNumbers Not natively supported Store in custom data as data.additional_phones (JSON array)
customAttributes (typed, UUID-keyed) data (freeform key-value) Flatten UUID keys to human-readable names using attribute definitions endpoint
externalId data.dixa_external_id Store as custom data

Field-Level Mapping: Conversations → Sessions

Dixa Field Crisp Field Transform
csid (conversation ID) session_id Generate new UUID or use mapping table
channel (email, chat, phone) origin Map to Crisp origin URN
requesterId People profile link Resolve via email match
assignedAgentId Conversation participant (operator) Map agent email
state (open, closed) Conversation state Direct
tags [] segments [] Lowercase, underscore-delimited
createdAt created_at Timestamp in milliseconds
customAttributes meta.data Flatten UUID keys to readable names
conversation_wrapup_notes Message (type: note) Flatten to note message; preserve author and timestamp
recording_url / voicemail_url Custom data or archived link note Fetch, store, re-link; often better archived than replayed
link_type / linked_to Custom data (data.dixa_linked_to, data.dixa_link_type) Preserve for audit trail; no relational equivalent in Crisp

Handling Custom Attributes

Dixa's custom attributes use a schema with explicit types. Each attribute has a UUID, a displayName, an entityType (conversation or end_user), and an inputDefinition that specifies the data format (text, dropdown, integer, etc.).

Crisp's custom data is freeform — any JSON key-value pair. This means:

  • Dropdown values in Dixa become plain strings in Crisp (validation is lost)
  • Integer fields become JSON numbers (or strings if you're not careful — always cast explicitly)
  • UUID attribute keys must be resolved to human-readable names using the custom attributes definition endpoint
  • Multi-select fields often need to become arrays or delimited strings
  • Boolean fields should remain JSON booleans, not strings like "true"
Warning

Edge case: Dixa's GET /v1/endusers list endpoint does not include custom attributes in the response. You must call GET /v1/endusers/{userId} individually for each user to retrieve their custom attributes. For 100K end users at 10 req/sec, that's ~2.8 hours of extraction time just for custom data.

Migration Architecture

Data Flow: Extract → Transform → Load

┌──────────┐     ┌──────────────┐     ┌──────────┐
│  DIXA    │────▶│  TRANSFORM   │────▶│  CRISP   │
│          │     │              │     │          │
│ Exports  │     │ • Flatten    │     │ People   │
│ API      │     │   attributes │     │ API      │
│ (convos) │     │ • Map tags   │     │          │
│          │     │   → segments │     │ Messages │
│ Dixa API │     │ • Normalize  │     │ API      │
│ (users,  │     │   phone #s   │     │          │
│  attrs,  │     │ • Re-host    │     │ Import   │
│  notes)  │     │   files      │     │ Tool     │
└──────────┘     └──────────────┘     └──────────┘

Dixa Extraction Details

Exports API (https://exports.dixa.io/v1/):

  • Uses bearer token authentication
  • Returns streamed JSON arrays
  • Queries are time-bounded: created_after and created_before parameters, max 31-day span
  • Two endpoints: conversation_export (conversation metadata, wrap-up notes) and message_export (message bodies)
  • Rate limit: 10 conversation export requests/minute and 3 message export requests/minute (docs.dixa.io)

Dixa API (https://dev.dixa.io/v1/):

  • End users: GET /v1/endusers (paginated, pageKey + pageLimit)
  • Individual user with custom attrs: GET /v1/endusers/{userId}
  • Tags: GET /v1/tags
  • Custom attribute definitions: GET /v1/custom-attributes
  • Ratings: GET /v1/conversations/{conversationId}/ratings
  • Rate limit: 10 req/sec per token, burst of 4, daily ceiling of 864,000
  • No Retry-After header on 429 responses — implement exponential backoff
Danger

Data loss risk: Dixa's Exports API message endpoint does not return internal notes. If you only use the Exports API for messages, you'll lose all internal notes. You must also extract conversation_wrapup_notes from the conversation export endpoint or use the Dixa API v1 conversation endpoint.

Crisp Loading Details

Crisp REST API (https://api.crisp.chat/v1/):

  • Plugin tokens are recommended for migrations — they're exempt from per-route rate limits and only subject to daily quotas. Website tokens are limited to ~10,000 requests/day; plugin tokens start at ~5,000/day but can be increased by requesting through the Marketplace. (docs.crisp.chat)
  • POST /v1/website/{website_id}/conversation/{session_id}/message — supports a timestamp field for ingesting older messages with original timestamps
  • People profiles: POST to create, PATCH to update via /v1/website/{website_id}/people/profile
  • Custom data: PUT /v1/website/{website_id}/people/data/{people_id}
  • Segments: set via conversation meta update or people profile update
  • Authentication: Basic Auth with identifier and key generated from a custom plugin in the Crisp Marketplace. Do not use personal user tokens for migrations.
  • 429 handling: Crisp returns HTTP 429 with a JSON body including "reason": "too_many_requests". Unlike some APIs, Crisp does not include a Retry-After header on 429 responses. Implement exponential backoff starting at 2 seconds, doubling per retry, capping at 120 seconds.
  • Python SDK: The crisp-api package (version 5.x as of mid-2025) wraps the REST API. Pin the version in your requirements.txt to avoid breaking changes during migration.
Tip

Critical step: Before starting any conversation import, contact Crisp support and ask them to block outbound emails for your workspace. Imported conversations can trigger email notifications to customers if this isn't disabled. Re-enable after the import is complete and validated.

Authentication & Rate Limits Summary

Platform Auth Method Rate Limit Daily Limit
Dixa Exports API Bearer token 10 conv req/min, 3 msg req/min Shared with Dixa API
Dixa API v1 Bearer token 10 req/sec per token, burst of 4 864,000 req/token
Crisp REST API (plugin) Identifier + Key (Basic Auth) Not subject to per-route limits Daily quota (~5,000 default; requestable increase)
Crisp REST API (website) Identifier + Key (Basic Auth) API Global + API Route limits ~10,000 req/day

Step-by-Step Migration Process

Step 1: Extract Conversations and Messages from Dixa

import requests
import time
from datetime import datetime, timedelta
 
DIXA_TOKEN = "your_dixa_api_token"
EXPORTS_URL = "https://exports.dixa.io/v1"
 
def extract_conversations(start_date, end_date):
    """Extract conversations in 31-day chunks."""
    all_conversations = []
    current = start_date
    
    while current < end_date:
        chunk_end = min(current + timedelta(days=31), end_date)
        
        response = requests.get(
            f"{EXPORTS_URL}/conversation_export",
            params={
                "created_after": current.strftime("%Y-%m-%d"),
                "created_before": chunk_end.strftime("%Y-%m-%d")
            },
            headers={"Authorization": f"Bearer {DIXA_TOKEN}"}
        )
        
        if response.status_code == 200:
            all_conversations.extend(response.json())
        elif response.status_code == 429:
            # No Retry-After header — exponential backoff
            time.sleep(60)
            continue
        else:
            raise Exception(f"Extraction failed: {response.status_code}{response.text}")
        
        current = chunk_end
    
    return all_conversations

Step 2: Extract End Users and Custom Attribute Definitions

def extract_end_users():
    """Paginate through all end users."""
    users = []
    page_key = None
    
    while True:
        params = {"pageLimit": 100}
        if page_key:
            params["pageKey"] = page_key
        
        response = requests.get(
            "https://dev.dixa.io/v1/endusers",
            params=params,
            headers={"Authorization": f"Bearer {DIXA_TOKEN}"}
        )
        
        if response.status_code == 429:
            time.sleep(1)
            continue
        
        data = response.json()
        users.extend(data.get("data", []))
        
        page_key = data.get("nextPageKey")
        if not page_key:
            break
    
    return users
 
def get_user_custom_attributes(user_id):
    """Fetch individual user to get custom attributes (not included in list endpoint)."""
    response = requests.get(
        f"https://dev.dixa.io/v1/endusers/{user_id}",
        headers={"Authorization": f"Bearer {DIXA_TOKEN}"}
    )
    if response.status_code == 200:
        return response.json().get("data", {}).get("customAttributes", {})
    return {}
 
def get_attribute_definitions():
    """Fetch custom attribute definitions to map UUIDs to names."""
    response = requests.get(
        "https://dev.dixa.io/v1/custom-attributes",
        headers={"Authorization": f"Bearer {DIXA_TOKEN}"}
    )
    definitions = response.json().get("data", [])
    return {attr["id"]: attr["displayName"] for attr in definitions}

Step 3: Transform Data to Crisp Format

import re
 
def normalize_phone_e164(phone, default_country_code="+1"):
    """Normalize phone number to E.164 format."""
    if not phone:
        return ""
    digits = re.sub(r'[^\d+]', '', phone)
    if not digits.startswith('+'):
        digits = default_country_code + digits
    return digits
 
def transform_end_user(dixa_user, attr_map):
    """Transform a Dixa end user into Crisp people profile format."""
    custom_data = {}
    for attr_id, value in dixa_user.get("customAttributes", {}).items():
        readable_name = attr_map.get(attr_id, attr_id)
        key = readable_name.lower().replace(" ", "_")
        # Preserve type: integers stay integers, booleans stay booleans
        custom_data[key] = value
    
    if dixa_user.get("externalId"):
        custom_data["dixa_external_id"] = dixa_user["externalId"]
    
    if dixa_user.get("additionalEmails"):
        custom_data["additional_emails"] = dixa_user["additionalEmails"]
    
    if dixa_user.get("additionalPhoneNumbers"):
        custom_data["additional_phones"] = dixa_user["additionalPhoneNumbers"]
    
    return {
        "email": dixa_user.get("email"),
        "person": {"nickname": dixa_user.get("displayName", "")},
        "phone": normalize_phone_e164(dixa_user.get("phoneNumber", "")),
        "data": custom_data
    }
 
def transform_tag_to_segment(tag):
    """Dixa tags map to Crisp segments. Lowercase, underscore-delimited."""
    return tag.lower().replace(" ", "_")

Step 4: Load Contacts into Crisp

Create people profiles first — you'll need the Dixa user ID → Crisp people ID mapping to link conversations later.

from crisp_api import Crisp
 
client = Crisp()
client.set_tier("plugin")
client.authenticate(CRISP_IDENTIFIER, CRISP_KEY)
 
WEBSITE_ID = "your_website_id"
 
# Persistent ID map — use a database or JSON file in production
id_map = {}  # {dixa_user_id: crisp_people_id}
 
def import_people_profile(dixa_user_id, profile_data):
    """Create or update a Crisp people profile."""
    try:
        result = client.website.add_new_people_profile(
            WEBSITE_ID,
            {"email": profile_data["email"],
             "person": profile_data["person"]}
        )
        people_id = result["people_id"]
        
        if profile_data.get("data"):
            client.website.save_people_data(
                WEBSITE_ID, people_id,
                {"data": profile_data["data"]}
            )
        
        id_map[dixa_user_id] = people_id
        return people_id
    except Exception as e:
        log_error("people_import", profile_data.get("email"), str(e))
        return None

Step 5: Create Conversations and Replay Messages

In Crisp, you first create a conversation session, then append messages to it. A key implementation detail: a newly created conversation is not visible in the Crisp Inbox until a message is sent with a user sender. Your loader should emit the first customer-authored message before internal notes or agent replies.

import uuid
 
def import_conversation(dixa_conversation, messages, agent_email_map):
    """Replay messages into a Crisp conversation with original timestamps."""
    session_id = str(uuid.uuid4())
    
    # Create the conversation session
    client.website.create_a_new_conversation(
        WEBSITE_ID,
        {"session_id": session_id}
    )
    
    # Sort messages chronologically
    messages.sort(key=lambda m: m["original_timestamp_ms"])
    
    # Ensure first message is from a user (required for Inbox visibility)
    user_messages = [m for m in messages if m["from"] == "user"]
    if user_messages:
        first_user = user_messages[0]
        messages.remove(first_user)
        messages.insert(0, first_user)
    
    for message in messages:
        try:
            client.website.send_message_in_conversation(
                WEBSITE_ID,
                session_id,
                {
                    "type": message["type"],  # "text", "note", "file"
                    "content": message["content"],
                    "from": message["from"],  # "user" or "operator"
                    "origin": "chat",
                    "timestamp": message["original_timestamp_ms"],
                    "stealth": True  # Don't notify the other party
                }
            )
        except Exception as e:
            log_error("message_import", session_id, str(e))
    
    # Store mapping for delta sync and rollback
    id_map[dixa_conversation["csid"]] = session_id
    return session_id

In production, add idempotency keys, source-to-target ID maps (persisted to disk or database), exponential backoff on 429, and a dead-letter queue for failed attachments. Do not let the script guess on duplicates; choose a deterministic match order such as email → external_id → phone → manual review.

Step 6: Validate and Cut Over

After import, run validation checks:

  • Record count comparison: Total conversations in Dixa export vs. total sessions in Crisp. Accept <1% variance for filtered spam/bot conversations.
  • Message count per conversation: Spot-check 50–100 conversations for message count parity.
  • Contact count: Dixa end users vs. Crisp people profiles. Account for deduplication reducing the Crisp count.
  • Segment coverage: Verify that all Dixa tags appear as Crisp segments by querying GET /v1/website/{website_id}/conversations/list and checking segment values.
  • Custom data integrity: Sample 20 contacts and compare custom data values side-by-side between your Dixa export JSON and Crisp's people data endpoint.
  • Timestamp integrity: Verify that message timestamps in Crisp match the original Dixa timestamps, not the import time.

Only switch mail forwarding, chat widgets, or phone routing after counts and agent UAT pass.

For a deeper validation framework, see Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration.

Edge Cases & Challenges

Duplicate Contacts

Dixa allows multiple end users with the same email (rare but possible through API creation or different channels). Crisp uses email as the de facto unique identifier for people profiles. Your transform layer must deduplicate by email before importing, merging custom data from duplicates. Strategy: sort duplicates by updatedAt descending, take the most recent profile as the primary, and merge custom attributes from older profiles (newer values win on conflict).

Missing End Users

Some Dixa conversations may reference a requesterId that no longer exists (deleted or anonymized users). Your script must handle orphaned conversations gracefully — create a placeholder contact with email unknown+{requesterId}@placeholder.local or import the conversation with available metadata only. Log all orphaned conversations for manual review.

Phone-Only Conversations

Dixa's native telephony creates conversations with voice recordings and call metadata. Crisp has no native phone channel — phone integrations come through third-party plugins (Ringover, Aircall). Phone conversation history can be imported as text-type messages with a note indicating the original channel, but actual call recordings need to be stored externally (S3, GCS) and linked via URLs in custom data. Example note format: [Phone call — Duration: 4m 23s — Recording: https://your-cdn.com/recordings/{id}.mp3].

Attachments and Inline Images

Dixa attachment URLs are authenticated and may expire. Each attachment must be:

  1. Downloaded from Dixa during extraction (store locally or in cloud storage)
  2. Re-uploaded to Crisp's bucket endpoint (POST /v1/website/{website_id}/bucket/url) or hosted on your own CDN
  3. Referenced in the Crisp message as a file type message

Images pasted directly into Dixa emails are often base64 encoded or hosted on Dixa's CDN. These will break in Crisp unless parsed, downloaded, and re-uploaded. Budget ~0.5 seconds per attachment for download+upload at scale — for 50K conversations averaging 1.5 attachments each, that's approximately 10 hours of attachment processing.

Internal Notes vs. Wrap-Up Notes

Dixa has two distinct note types: in-conversation internal notes (visible to agents during the conversation) and wrap-up notes (added at conversation close, stored in conversation_wrapup_notes). Crisp treats all notes as messages of type note. Both must be extracted and imported — but they come from different API endpoints. The wrap-up notes are in the conversation export; inline notes may require separate API calls. Prefix wrap-up notes with [Wrap-up Note] to preserve the distinction in Crisp.

Imported History Visibility

Imported conversation history in Crisp is visible to operators in the Inbox, but customers do not automatically see past imported exchanges in the chatbox widget. If your requirement is customer-visible transcript replay inside the widget, test this in a sandbox before promising parity to stakeholders.

Multi-Language Knowledge Base

If your Dixa Knowledge base is multi-language, Crisp's helpdesk import requires a separate URL per language variant. Your Dixa KB must be publicly accessible during import.

Crisp Limitations vs. Dixa

Be honest with your stakeholders about what does not migrate cleanly:

Capability Dixa Crisp Impact
Native phone/IVR ✅ Built-in ❌ Third-party plugins Phone history loses native context
Typed custom attributes ✅ Schema-defined ❌ Freeform key-value Dropdown validation lost
Routing flows ✅ Visual Flow Builder ✅ Workflows (different paradigm) Must be rebuilt manually
CSAT ratings ✅ Native with API ✅ Native surveys Re-implement triggers; historical ratings migrate as custom data
SLA policies Partial Must be rebuilt
Linked conversations ✅ Parent/child, merged ❌ No equivalent Flatten to custom data
Tables in KB articles Supported Not supported KB articles with tables need reformatting
Video in KB articles Supported Not imported Videos must be re-added manually
Contact profile limits No published limit Plan-dependent caps May need higher Crisp plan for large databases
API access by plan Ultimate/Prime only (€139–€179/user/mo) All plans Crisp is more accessible for API-based work
Webhook integrations Configurable via admin UI Plugin-based webhooks Must be rebuilt; different event taxonomy

Validation & Testing

Validation Strategy

  1. Pre-migration snapshot: Export full record counts from Dixa — total conversations, messages, end users, tags. Store these counts in a validation spreadsheet.
  2. Test migration: Run the full pipeline against a subset (e.g., last 30 days of data) into a Crisp test workspace. Always validate in a sandbox before touching production.
  3. Record count comparison: Dixa totals vs. Crisp totals. Accept <1% variance for edge cases (anonymized users, spam).
  4. Field-level sampling: Pick 50 random conversations. Verify message count, content, timestamps, and participant mapping.
  5. Custom data spot-check: Compare 20 contacts side-by-side between Dixa and Crisp.
  6. Segment verification: Export the full list of Dixa tags and verify each appears as a Crisp segment.

UAT Process

Have 2–3 agents open Crisp and search for recent customers they handled in Dixa:

  • Verify conversation history is complete and readable
  • Confirm segments (tags) are visible and filterable
  • Check that contact custom data appears correctly in the people profile sidebar
  • Verify they can route the next reply correctly
  • Confirm that wrap-up notes are distinguishable from inline notes

Rollback Planning

Crisp does not offer a one-click "undo import." Your rollback plan:

  1. Use a test workspace for the first full import. Only import into production after validation passes.
  2. Track imported session IDs in your persistent ID map so you can programmatically delete them via DELETE /v1/website/{website_id}/conversation/{session_id} if needed. For 100K conversations at Crisp's daily API quota, a full rollback would take approximately 2–5 days depending on your quota tier.
  3. Back up everything from Dixa before starting. Keep the raw JSON exports in versioned cloud storage (S3, GCS) with at least 90-day retention.

Post-Migration Tasks

Rebuild Workflows and Automations

Dixa's Flow Builder logic — routing, IVR trees, auto-replies, escalation rules — has no migration path. Rebuild in Crisp's Workflow builder. The paradigm differs but covers similar use cases: auto-tagging, routing to inboxes, bot responses.

Rebuild Webhook Integrations

If your Dixa instance sent webhook events to downstream systems (CRMs, analytics pipelines, data warehouses, Slack channels), catalog every active webhook and its downstream consumer. Recreate equivalent event subscriptions in Crisp's plugin webhook system. Test each webhook end-to-end before cutover.

Rebuild Knowledge Base

Crisp can auto-import KB content from a public URL via the helpdesk import feature. Your Dixa KB must be publicly accessible during import. Tables are not supported and videos are not imported. Manual formatting review is required after import.

Agent Onboarding

  • Crisp uses Inboxes (not queues) for conversation routing
  • Segments replace tags — same concept, different terminology
  • Custom data appears in the conversation sidebar, not in dedicated fields
  • Keyboard shortcuts and conversation actions differ from Dixa
  • Internal notes are added as message type note in the conversation, not via a separate notes panel

Post-Cutover Monitoring

In the first 2 weeks:

  • Watch for duplicate contacts created by the live chat widget (new visitors who already exist as imported contacts)
  • Monitor Crisp's people profile count against plan limits
  • Check that Workflow automations fire correctly on new conversations
  • Watch error logs for the first 48 hours for webhook failures or routing misconfigurations
  • Verify downstream systems that depended on Dixa webhooks are receiving events from Crisp

Best Practices

  • Back up before migration. Export all Dixa data to JSON files and store in versioned cloud storage (S3, GCS) with 90-day retention before touching anything in Crisp.
  • Run a sandbox migration first. Move 1,000 tickets to a Crisp test workspace to test your mapping logic before touching production. Validate after each phase, not just at the end.
  • Block outbound emails. Contact Crisp support to disable email notifications before importing conversations. Re-enable after the import is complete and validated.
  • Use plugin tokens. Plugin tokens in Crisp are exempt from per-route rate limits — significantly better for bulk operations. Request quota increases through the Marketplace before starting. Allow 3–5 business days for quota increase requests.
  • Track the ID map. Maintain a persistent mapping of Dixa conversation IDs → Crisp session IDs and Dixa user IDs → Crisp people IDs. Store in a database or versioned JSON file. You'll need this for debugging, delta syncs, rollback, and post-migration auditing.
  • Handle rate limits gracefully. Neither Dixa nor Crisp returns a Retry-After header on 429 responses. Implement exponential backoff starting at 1–2 seconds, doubling per retry, capping at 60–120 seconds.
  • Log everything. If a single message fails to push, your script should log the Dixa ID, error message, and timestamp so you can retry it later without restarting the entire batch. Use a dead-letter queue for failed attachments.
  • Execute DPA with Crisp. Ensure your Data Processing Agreement is signed before importing any personal data, especially if migrating EU customer records.
  • Do not delete Dixa immediately. Keep it in a read-only state for at least 30 days post-migration as a fail-safe. Confirm Dixa's post-cancellation data retention timeline in writing.

For general migration best practices, see Best Practices for a Successful Help Desk Data Migration.

Making the Decision

A Dixa-to-Crisp migration is moderate complexity. The data layer is manageable — both platforms are conversation-centric, and Crisp's API supports timestamp-preserving message ingestion. The hard parts are the workflow rebuild (Dixa Flows → Crisp Workflows), the custom attribute translation (typed schema → freeform key-value), the extraction constraints (31-day API windows, separate endpoints for notes), and the webhook/integration rebuild.

If your team has JavaScript or Python experience and your dataset is under 50K conversations, a DIY migration using Crisp's import tool with a custom adapter is realistic. Budget 2–3 weeks.

If you're dealing with 100K+ conversations, complex custom attributes, phone history, or a tight cutover window, the engineering overhead and risk of data loss typically justify a managed migration.

Frequently Asked Questions

Can I migrate conversation history from Dixa to Crisp?
Yes, but not through CSV import alone. Crisp's native import is contact-focused. Historical conversations require Crisp's crisp-import-conversations tool with a custom Dixa adapter, or direct API calls using the message endpoint with original timestamps. Internal notes require a separate extraction step from Dixa's conversation export — the message export endpoint does not include them.
Does Crisp have a built-in Dixa migration tool?
Not directly. Crisp's open-source crisp-import-conversations tool supports adapters for Zendesk, Gorgias, Help Scout, Tidio, GrooveHQ, and WHMCS — but no Dixa adapter is included. You need to write a custom adapter or use the raw Crisp API for data loading.
What Dixa plan do I need for API-based migration?
Dixa's API access is only available on the Ultimate and Prime plans (€139–€179/user/month). The Growth plan does not include API access. Without API access, you're limited to CSV exports, which don't include message bodies or threaded conversation history.
How long does a Dixa to Crisp migration take?
For 50,000–150,000 conversations, expect 2–4 weeks including data extraction, transformation script development, test migration, validation, and cutover. Smaller datasets (under 10K conversations) can be completed in 1–2 weeks. The 31-day API export window constraint and Crisp's daily plugin quotas are the main time factors.
Will my customers receive emails during the Crisp migration?
They can if you don't take precautions. Contact Crisp support before importing and ask them to block outbound emails for your workspace. Imported conversations can trigger email notifications to customers if this isn't disabled. Re-enable notifications after the import is complete and validated.

More from our Blog

Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration
Help Desk

Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration

Ensure your help desk migration is a success with this comprehensive 20-point post-migration QA checklist. This expert guide details the 20 essential tests needed to validate your data integrity, system functionality, user-friendliness, and performance . Learn exactly how to check everything from ticket data, attachments, and knowledge base articles to critical workflows, automations, and integrations before you go live. This process is your final line of defense against lost tickets, broken workflows, and unhappy customers.

Raaj Raaj · · 8 min read