Skip to content

How to Export Data from Dixa: Methods, API Limits & Data Mapping

Step-by-step guide to exporting Dixa data via CSV and the Exports API. Covers authentication, rate limits, attachments, and a working Python script.

Raaj Raaj · · 17 min read
How to Export Data from Dixa: Methods, API Limits & Data Mapping
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

Dixa is a conversational customer service platform that unifies voice, email, chat, and social media into a single agent workspace. When teams need to migrate off Dixa to platforms like Zendesk, Freshdesk, or Intercom — or feed Dixa data into a warehouse for analytics — the first step is getting the data out.

Dixa gives you two ways to export: a built-in CSV export through the web UI, and a programmatic Exports API that streams conversation and message data as JSON. The CSV path works for quick, ad-hoc reporting. The API is what you need for migrations, warehouse loads, compliance archives, or anything recurring.

The catch: Dixa splits its data across multiple endpoints. Conversations, messages, contacts, agents, analytics, and files all live in different places. If you plan to migrate or build a warehouse feed, map the data model before you start writing scripts.

If you're exporting as part of a platform migration, these destination-specific guides cover the import side: Dixa to Zendesk or Dixa to Freshdesk.

What Data Can You Export from Dixa?

Before picking an export method, understand what entities exist and where each one lives.

Data Entity Best Export Path Key Details
Conversations UI CSV or GET /v1/conversation_export Core data object. Includes conversation ID (csid), channel type (phone, email, chat, social), queue, assigned agent, status, timestamps (created, updated, closed), tags, CSAT ratings, duration, and direction. Phone conversations include caller number and call duration. Chat conversations include chat rating and chat duration. Internal notes live here under conversation_wrapup_notes. The API is a superset of the CSV fields.
Messages GET /v1/message_export Individual messages within conversations: text content, sender (agent or customer), timestamp, direction, and message type. Response includes recording_url, voicemail_url, and attached_files. Does not include internal notes.
Contacts / End users GET /v1/endusers (main API) Customer profiles with contact information, interaction history, and metadata.
Agents GET /v1/agents (main API) Agent profiles: UUID, email, display name, role (AGENT, TEAM_LEAD, ADMIN), active status, team IDs, queue IDs.
Teams & Queues GET /v1/teams, GET /v1/queues (main API) Organizational structure: team membership, queue definitions, routing configurations.
Analytics GET /v1/analytics/metrics, POST /v1/analytics/records Pre-calculated KPIs: response time, resolution rate, CSAT scores. Use these instead of rebuilding metrics from raw exports. (Dixa Analytics API docs)
Attachments & call recordings Links in message_export, files from files.dixa.io The export payload gives you URLs, not binary files. You download each file separately. Private files require Basic auth.
Warning

Internal notes are not in the message export. This is the most common data model mistake during a Dixa export. Internal notes live in the conversation export under conversation_wrapup_notes. You must merge data from both endpoints to reconstruct a complete conversation timeline.

Method 1: CSV Export via the Dixa Web UI

The CSV export is the fastest path for a one-time snapshot of conversation-level data. No engineering required, but it has strict limitations that make it unsuitable for anything beyond ad-hoc reporting.

How to Run a Dixa CSV Export

  1. Log in to your Dixa account as an administrator.
  2. Navigate to Analytics > Export in the left navigation sidebar.
  3. Select the email address where you want to receive the CSV file.
  4. Select the time period. Each export is limited to a maximum of 3 months of data.
  5. Click Export My Data.
  6. The CSV file will be emailed to the specified address within a few minutes, depending on data volume.

What the CSV Includes

The CSV provides conversation-level data: conversation ID, timestamps, queue, assigned agent, chat rating, notes, tags, feedback, duration, and other conversation metadata. All fields available in the CSV are also available via the Exports API — the API is a strict superset. (Dixa Exports API Guide)

Limitations of the CSV Export

  • 3-month time window per export. For longer periods, you must run multiple manual exports.
  • Data is emailed, not downloaded. You're dependent on email delivery and spam filters. No direct download from the UI.
  • No filtering at export time. You get everything for the time period. Filter by channel, queue, or agent post-export.
  • No message-level data. Only conversation metadata. No transcript text.
  • No attachments or recordings. Files shared during conversations are not included.
  • Not suitable for recurring exports. If you're exporting regularly, the CSV method requires manual repetition and cannot be automated.

If your goal is a Dixa export with full message history, skip the CSV and go straight to the API.

Method 2: Programmatic Export via the Dixa Exports API

The Dixa Exports API lives at https://exports.dixa.io/v1 and provides two endpoints for bulk data extraction. This is separate from the main Dixa API at https://dev.dixa.io/v1, which handles individual resource operations like agent lookups and contact queries. Using the wrong host returns a 404. (Dixa Exports API reference)

Authentication

Dixa uses a static, organization-scoped Bearer token. There is no per-user OAuth flow.

To generate an API token:

  1. Log in to Dixa as an administrator.
  2. Navigate to Settings > Integrations > API Tokens (under the Manage section in the sidebar).
  3. Click Add API token.
  4. Give the token a name and select "Dixa API" as the version (not the deprecated Integrations API).
  5. Click Save and copy the generated token.

(Dixa API token tutorial)

Dixa documents two authentication methods for the Exports API:

# Method 1: Bearer header
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://exports.dixa.io/v1/conversation_export?created_after=2025-01-01&created_before=2025-01-31"
 
# Method 2: Basic auth (username is "bearer", password is your token)
curl -u "bearer:YOUR_TOKEN" \
  "https://exports.dixa.io/v1/conversation_export?created_after=2025-01-01&created_before=2025-01-31"

Conversation Export Endpoint

Endpoint: GET https://exports.dixa.io/v1/conversation_export

This endpoint returns conversation-level data as a JSON array. You must provide either a time range or a list of conversation IDs.

Query parameters:

Parameter Required Format Description
created_after / created_before Yes* Unix ms or yyyy-mm-dd Filter by creation date
closed_after / closed_before Alternative Unix ms or yyyy-mm-dd Filter by close date
updated_after / updated_before Alternative Unix ms or yyyy-mm-dd Filter by update date
last_message_created_after / last_message_created_before Alternative Unix ms or yyyy-mm-dd Filter by last message date
csids Alternative* Comma-separated list Specific conversation IDs to export

*The request must include at least one _after/_before pair or a list of csids.

Constraints:

  • No query can span more than 31 days. For longer periods, iterate through consecutive windows.
  • No offset or record-count pagination. Time windows are the only control surface — you chunk the export by date range, not by page number.
  • The endpoint streams all matching records as a JSON array.

Internal notes (wrap-up notes) are included in this endpoint's response under conversation_wrapup_notes.

Sample Conversation Export Response

A truncated example of the JSON structure returned by conversation_export:

[
  {
    "csid": 123456,
    "channel": "email",
    "status": "closed",
    "direction": "inbound",
    "requester_email": "customer@example.com",
    "requester_name": "Jane Doe",
    "assigned_agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "queue_id": "q-abc-123",
    "tags": ["billing", "refund"],
    "created_at": 1704067200000,
    "updated_at": 1704153600000,
    "closed_at": 1704153600000,
    "csat_score": 5,
    "csat_offered": true,
    "conversation_wrapup_notes": "Customer requested refund for duplicate charge. Processed and confirmed.",
    "chat_rating": null,
    "chat_duration": null,
    "call_duration": null,
    "caller_number": null
  }
]

Key observations from the response structure:

  • csid is an integer, not a UUID. Use this as the join key when merging with message data.
  • assigned_agent_id is a UUID that maps to the id field in the agents endpoint.
  • tags is an array of strings, not tag IDs.
  • Timestamps are Unix milliseconds (not seconds). Divide by 1000 for standard epoch.
  • Channel-specific fields (chat_duration, call_duration, caller_number) return null for non-matching channel types.
  • conversation_wrapup_notes is a plain text string — this is where internal notes live.

Message Export Endpoint

Endpoint: GET https://exports.dixa.io/v1/message_export

This endpoint returns the actual conversation content. It uses created_after and created_before parameters with the same 31-day maximum window.

Warning

Internal notes are NOT in the message export. If you need internal notes, pull them from the conversation export under conversation_wrapup_notes and merge them with message data during transformation. This is not a bug — it's how Dixa's data model works.

Sample Message Export Response

[
  {
    "csid": 123456,
    "message_id": "msg-789xyz",
    "text": "Hi, I was charged twice for my subscription this month. Can you help?",
    "author_name": "Jane Doe",
    "author_email": "customer@example.com",
    "direction": "inbound",
    "created_at": 1704067200000,
    "from_phone_number": null,
    "to_phone_number": null,
    "duration": null,
    "recording_url": null,
    "voicemail_url": null,
    "attached_files": [
      {
        "name": "screenshot.png",
        "url": "/private/org-123/screenshot.png",
        "content_type": "image/png"
      }
    ]
  },
  {
    "csid": 123456,
    "message_id": "msg-790abc",
    "text": "I've processed your refund. You should see it within 3-5 business days.",
    "author_name": "Agent Smith",
    "author_email": "agent@company.com",
    "direction": "outbound",
    "created_at": 1704070800000,
    "from_phone_number": null,
    "to_phone_number": null,
    "duration": null,
    "recording_url": null,
    "voicemail_url": null,
    "attached_files": []
  }
]

Key observations:

  • csid matches the conversation export — this is the foreign key for joining conversations to messages.
  • attached_files contains relative paths that must be prepended with https://files.dixa.io for download.
  • For phone conversations, recording_url contains the call recording link and duration contains the call length in seconds.
  • There is no message_type field distinguishing agent replies from customer messages — use direction (inbound / outbound) instead.

A complete Dixa export uses both endpoints and joins them on csid. If you export only conversations, you miss transcript bodies. If you export only messages, you miss internal notes.

Rate Limits

Warning

The public rate-limit docs currently conflict. Dixa's general API standards page lists 10 requests per second per token with a daily quota of 864,000 requests. The specific Exports API guide lists much stricter limits: 10 requests per minute for the conversation export endpoint and 3 requests per minute for the message export endpoint. Code to the stricter endpoint-specific limits until Dixa clarifies the discrepancy.

Key details:

  • Rate limits are enforced per token, not per organization.
  • Exceeding the limit returns HTTP 429 with a response body like: {"error": "Rate limit exceeded", "message": "Too many requests"}. Dixa does not provide a Retry-After header.
  • You must implement exponential backoff in your client.
  • If you run multiple export jobs in parallel against the same token, they share the rate limit budget.

Practical throughput math: At 3 requests per minute for messages with a 31-day window per request, exporting one year of message data requires at minimum 12 requests (12 monthly windows) taking ~4 minutes of API time. For conversation exports at 10 requests per minute, the same 12 requests take ~1.2 minutes. The bottleneck is the message endpoint.

Incremental Export Pattern

For recurring exports (e.g., daily warehouse syncs), use a watermark-based incremental pattern with updated_after instead of created_after:

  1. Store the timestamp of your last successful export (the "watermark") — e.g., in a file, database row, or dlt state.
  2. On each run, query with updated_after={watermark} and updated_before={now}.
  3. After successful processing, update the watermark to {now}.

Using updated_after (not created_after) ensures you capture conversations that were reopened, re-tagged, or received new messages since your last export. This is critical for keeping warehouse data consistent with Dixa's current state.

# Incremental export example using a watermark file
from datetime import datetime
from pathlib import Path
 
WATERMARK_FILE = Path("last_export_timestamp.txt")
 
def get_watermark() -> str:
    if WATERMARK_FILE.exists():
        return WATERMARK_FILE.read_text().strip()
    return "2025-01-01"  # initial backfill date
 
def set_watermark(timestamp: str):
    WATERMARK_FILE.write_text(timestamp)
 
def incremental_export():
    watermark = get_watermark()
    now = datetime.utcnow().strftime("%Y-%m-%d")
    
    # Export using updated_after to catch modified conversations
    for window_start, window_end in iter_windows(
        datetime.strptime(watermark, "%Y-%m-%d"),
        datetime.strptime(now, "%Y-%m-%d"),
    ):
        conversations = get_export(
            "conversation_export",
            {"updated_after": window_start.strftime("%Y-%m-%d"),
             "updated_before": window_end.strftime("%Y-%m-%d")},
        )
        # Process conversations...
    
    set_watermark(now)
Warning

Incremental exports with updated_after may return conversations you've already exported if they were modified. Your loading logic must handle upserts (insert-or-update), not just inserts, to avoid duplicate records in your warehouse.

Sample Python Export Script

This script chunks the export into 31-day windows, exports both conversations and messages, handles 401/404/429/5xx responses with exponential backoff, and writes JSONL files. JSONL is a safer landing format than a single JSON array because Dixa export records include nested structures and file arrays — writing line by line avoids loading the entire response into memory.

The script throttles conservatively to match the stricter endpoint-specific rate limits from the Exports API guide.

import json
import os
import time
from datetime import datetime, timedelta
from pathlib import Path
 
import requests
 
EXPORT_BASE = "https://exports.dixa.io/v1"
TOKEN = os.environ["DIXA_API_TOKEN"]
OUTPUT_DIR = Path("dixa_export")
OUTPUT_DIR.mkdir(exist_ok=True)
 
# Throttle intervals based on stricter endpoint-specific limits
MIN_INTERVAL = {
    "conversation_export": 6.5,   # ~10 req/min
    "message_export": 20.5,       # ~3 req/min
}
LAST_CALL_AT = {
    "conversation_export": 0.0,
    "message_export": 0.0,
}
 
session = requests.Session()
session.headers.update({
    "Authorization": f"Bearer {TOKEN}",
    "Accept": "application/json",
})
 
 
def iter_windows(start_date, end_date, max_days=31):
    current = start_date
    while current < end_date:
        window_end = min(current + timedelta(days=max_days), end_date)
        yield current, window_end
        current = window_end
 
 
def throttle(endpoint: str):
    wait = MIN_INTERVAL[endpoint] - (time.time() - LAST_CALL_AT[endpoint])
    if wait > 0:
        time.sleep(wait)
 
 
def get_export(endpoint: str, params: dict, max_retries: int = 8):
    url = f"{EXPORT_BASE}/{endpoint}"
    backoff = 2
 
    for _ in range(max_retries):
        throttle(endpoint)
        response = session.get(url, params=params, timeout=300)
        LAST_CALL_AT[endpoint] = time.time()
 
        if response.status_code == 200:
            return response.json()
        if response.status_code == 401:
            raise RuntimeError("401 Unauthorized. Check DIXA_API_TOKEN.")
        if response.status_code == 404:
            raise RuntimeError(f"404 Not Found: {url}. Check base URL.")
        if response.status_code == 429 or 500 <= response.status_code < 600:
            time.sleep(backoff)
            backoff = min(backoff * 2, 60)
            continue
        raise RuntimeError(f"{response.status_code}: {response.text}")
 
    raise RuntimeError(f"Failed after {max_retries} attempts: {url}")
 
 
def write_jsonl(path: Path, rows: list):
    with path.open("a", encoding="utf-8") as f:
        for row in rows:
            f.write(json.dumps(row, ensure_ascii=False) + "\n")
 
 
def export_range(start_str: str, end_str: str):
    start_date = datetime.strptime(start_str, "%Y-%m-%d")
    end_date = datetime.strptime(end_str, "%Y-%m-%d")
 
    for window_start, window_end in iter_windows(start_date, end_date):
        after = window_start.strftime("%Y-%m-%d")
        before = window_end.strftime("%Y-%m-%d")
 
        conversations = get_export(
            "conversation_export",
            {"created_after": after, "created_before": before},
        )
        messages = get_export(
            "message_export",
            {"created_after": after, "created_before": before},
        )
 
        stamp = f"{after}_to_{before}"
        write_jsonl(OUTPUT_DIR / f"conversations_{stamp}.jsonl", conversations)
        write_jsonl(OUTPUT_DIR / f"messages_{stamp}.jsonl", messages)
        print(f"{stamp}: {len(conversations)} conversations, {len(messages)} messages")
 
 
if __name__ == "__main__":
    export_range("2025-01-01", "2025-12-31")

Open-Source Connector Alternatives

If you prefer off-the-shelf tooling over custom scripts:

  • dlt (data load tool): An open-source Python library that automates schema inference, data normalization, and incremental loading. dlt has a Dixa source that handles windowed queries, schema evolution, and state management for incremental syncs. Install with pip install dlt. The dlt Dixa source stores its watermark (last export timestamp) automatically, so you don't need to manage incremental state manually.

  • Airbyte: Publishes an open-source Dixa connector (airbyte-source-dixa) that supports the conversation export stream with Full Refresh and Incremental sync modes. The batch_size parameter controls the time window size in days (default: 31). Increasing it decreases request count but increases response size and memory usage per request.

Both tools work well for continuous sync to a data warehouse. Neither handles the field mapping and transformation needed for a helpdesk-to-helpdesk migration.

Exporting Agents, Contacts, Teams, and Queues via the Main Dixa API

The Exports API only covers conversations and messages. For the rest of the data model, use the main API at https://dev.dixa.io/v1.

Agents

Endpoint: GET /v1/agents

Returns agent profiles including UUID, email, display name, role, active status, team IDs, and queue IDs. The endpoint uses cursor-based pagination: the response includes a cursor field, and you pass it as ?cursor={value} on subsequent requests. When the response contains no cursor field (or it's null), you've reached the last page. Use ?limit=100 to control page size (max 100).

# First page
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://dev.dixa.io/v1/agents?limit=100"
 
# Response includes: { "data": [...], "cursor": "eyJpZCI6..." }
 
# Next page
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://dev.dixa.io/v1/agents?limit=100&cursor=eyJpZCI6..."
Warning

Gotcha: missing deactivated agents. By default, this endpoint returns only active agents. To include deactivated agents, pass ?active=false. Omitting this parameter silently excludes agents who have left the company, which breaks historical conversation assignment mapping during migrations.

# Include deactivated agents
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://dev.dixa.io/v1/agents?active=false&limit=100"

Contacts / End Users

Endpoint: GET /v1/endusers

Used to export customer profiles and contact data. The endpoint supports cursor-based pagination (same pattern as agents: cursor and limit parameters) and filters for email, phone, and external ID. If you need custom attributes, fetch individual end-user records via GET /v1/endusers/{id} in a second pass — the list endpoint may not include custom attributes in the response.

Teams and Queues

Endpoints: GET /v1/teams and GET /v1/queues

These endpoints provide the organizational structure needed for routing, reporting dimensions, and ownership mapping in your target system. Queue IDs can also be found in the Dixa UI at Settings > Queues. Both endpoints return all records without pagination (team and queue counts are typically small enough for a single response).

Analytics Endpoints

If your goal is reporting rather than migration, Dixa exposes analytics-specific endpoints: GET /v1/analytics/metrics and POST /v1/analytics/records. These return pre-calculated KPIs, which is usually better than trying to reconstruct every metric from raw conversation exports. (Dixa Analytics API)

Webhooks for Real-Time Sync

For continuous data sync rather than batch exports, Dixa supports outbound webhooks. The documented event catalog includes: conversation created, open, pending, closed, abandoned, assigned, note added, message added, tag added/removed, rated, transferred to agent, transferred to queue, and queue entered.

Configure webhooks at Settings > Integrations > Webhooks in the Dixa UI, or programmatically via the Create Webhook API. You can apply filters by channel, queue, and event origin.

Webhook payloads are POST requests with a JSON body containing the event type, timestamp, and the full conversation or message object matching the same schema as the export endpoints. Verify webhook authenticity by checking the X-Dixa-Signature header against your webhook secret.

How to Export Dixa Attachments and Call Recordings

This is the step teams forget — and only realize they've lost access to historical files after decommissioning their Dixa account.

Attachments, call recordings, and voicemails are not included as binary files in the CSV or API export payloads. The message export response includes metadata — attached_files, recording_url, and voicemail_url fields contain links, not file contents.

Exporting attachments is a two-step process:

  1. Export message metadata via the message export endpoint to collect all file URLs.
  2. Download each file before you decommission Dixa.

Dixa documents three URL shapes for files (Dixa Files API guide):

  • Full URL: https://files.dixa.io/private/...
  • Relative private path: /private/...
  • Relative public path: /public/...

For relative paths, prepend https://files.dixa.io. Public files do not require authentication. Private files require a Basic auth header where the username is bearer and the password is your API token.

# Download a private file
curl -u "bearer:YOUR_TOKEN" \
  "https://files.dixa.io/private/your-file-id" \
  -o downloaded_file

Maintain a mapping file (CSV or JSON) that links each downloaded file path to its original Dixa csid and message_id. This mapping is essential for re-attaching files to the correct conversation and message in your target platform:

{
  "csid": 123456,
  "message_id": "msg-789xyz",
  "original_url": "/private/org-123/screenshot.png",
  "local_path": "dixa_export/attachments/123456/msg-789xyz/screenshot.png",
  "content_type": "image/png"
}
Danger

Download attachments before you cut over. File URLs may expire after your Dixa account is deactivated. Build attachment downloading into your export script from the start. The file download endpoints share the main API rate limit (10 req/s per the general API standards page), so large attachment libraries (10,000+ files) may take several hours to download completely.

GDPR and Data Handling Considerations

Dixa stores customer PII including email addresses, phone numbers, names, and conversation content. When exporting this data, consider:

  • Data residency: Dixa processes data in the EU (AWS eu-west-1). Exported data stored in your own infrastructure must comply with the same GDPR requirements as the source.
  • Right to erasure: If a customer exercises their right to deletion under GDPR Article 17, you must be able to locate and delete their data across both Dixa and any exported copies. Track end-user IDs in your export metadata to enable this.
  • Data minimization: Export only the data entities you need. If you're migrating conversations but don't need analytics records, skip the analytics endpoints.
  • Access controls: Exported JSONL files and downloaded attachments contain the same sensitive data as the live platform. Apply equivalent access controls (encryption at rest, role-based access) to your export storage.
  • Retention: Define a retention policy for exported data. If you're using exports for a one-time migration, delete the intermediate export files after successful import and validation.

Troubleshooting Common Dixa Export Errors

Error Cause Fix
404 Not Found Wrong base URL. Using app.dixa.io instead of exports.dixa.io (Exports API) or dev.dixa.io (main API). Use the correct hostname for the API you're calling. Exports API → exports.dixa.io. Main API → dev.dixa.io.
401 Unauthorized API token is invalid, expired, or not formatted correctly. Regenerate at Settings > Integrations > API Tokens. Ensure you selected "Dixa API" as the version, not "Integrations API."
429 Too Many Requests Rate limit exceeded. Response body: {"error": "Rate limit exceeded"}. Implement exponential backoff. Code to the stricter per-endpoint limits (10/min for conversations, 3/min for messages). Multiple jobs on the same token share the budget.
Query spans > 31 days The API rejects time windows longer than one month. Break your export into consecutive 31-day chunks. The Python script above does this automatically.
Missing internal notes You're looking in the message export. Internal notes are not there. Pull internal notes from the conversation export under conversation_wrapup_notes.
Missing deactivated agents The agents endpoint returns only active agents by default. Pass ?active=false to include deactivated agents.
Empty results Timestamp format mismatch, created_after is after created_before, or missing required parameter pair. The API accepts both Unix milliseconds (e.g., 1704067200000) and yyyy-mm-dd (e.g., 2025-01-01). Verify date order and ensure you have at least one _after/_before pair or csids.
Large payload timeouts Month-sized pulls on high-volume accounts can produce very large JSON responses (100MB+ for accounts with 50K+ monthly conversations). Use narrower time windows (7 days instead of 31) for high-volume periods. Write to JSONL. Set a timeout of 300+ seconds on your HTTP client.
Duplicate records in incremental export Using updated_after returns previously exported conversations that were modified. Implement upsert logic in your loading step — match on csid and update existing records rather than inserting duplicates.

Importing Dixa Data into a New Platform

Exporting is the straightforward part. The harder work is transforming Dixa's data model to fit your target platform while preserving conversation threading, timestamps, agent assignments, tags, CSAT scores, and file links.

  • Zendesk: The Ticket Import API (POST /api/v2/imports/tickets) supports bulk ticket creation with preserved timestamps (created_at override) and inline attachment handling. Map Dixa conversations → Zendesk tickets, Dixa contacts → Zendesk users, Dixa conversation_wrapup_notes → Zendesk internal comments. See our Dixa to Zendesk migration guide for field mapping details.
  • Freshdesk: Freshdesk supports CSV import for contacts and companies in the UI, but ticket migration requires the API (POST /api/v2/tickets) — there is no ticket CSV import. Freshdesk does not support timestamp override on standard ticket creation, so historical ticket ordering requires the Freshdesk data import feature or custom workarounds. See our Dixa to Freshdesk guide.
  • Intercom: Use the Intercom API to map Dixa contacts to Intercom contacts (POST /contacts) and conversations to Intercom conversations. Full conversation loading typically requires creating admin-initiated conversations and appending messages as conversation parts with correct timestamps.
  • Data warehouses: Load JSONL exports directly into BigQuery, Snowflake, Databricks, or Redshift. Tools like dlt and Airbyte have Dixa connectors for continuous sync. Recommended schema: separate tables for conversations, messages, contacts, agents, teams, and queues, joined on csid (conversations ↔ messages), assigned_agent_id (conversations ↔ agents), and queue_id (conversations ↔ queues).

Post-Migration Validation Checklist

After importing, run these checks to catch data loss or mapping errors:

  1. Total conversation count: Compare Dixa export row count to imported ticket/conversation count.
  2. Message count per conversation: Sample 50+ conversations and verify message counts match.
  3. Timestamp integrity: Verify created_at and closed_at values survived the import (not overwritten with import time).
  4. Agent assignment: Confirm assigned agents map correctly, especially for deactivated agents.
  5. Attachment presence: Spot-check 20+ conversations with attachments — verify files are accessible in the target platform.
  6. Internal notes: Verify conversation_wrapup_notes appear as internal/private comments in the target.
  7. Tags and CSAT scores: Confirm tag arrays and CSAT ratings transferred correctly.
  8. Channel type: Verify phone, email, chat, and social conversations are categorized correctly.

For a comprehensive post-migration QA methodology, see Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration.

When to Call in Help

Getting data out of Dixa is straightforward if you follow the patterns above. The complexity hits during transformation and loading: merging internal notes with messages, preserving conversation threading across channels, downloading and re-attaching files at scale, and mapping Dixa's data model to your target platform's schema.

If you've read through this guide and decided you'd rather not build and maintain export scripts, talk to us. We handle Dixa extractions including the full export, transformation, attachment migration, and post-migration QA.

If you're moving the other direction, our Zendesk to Dixa migration guide is a useful companion read.

Frequently Asked Questions

How do I export conversations from Dixa?
You have two options: use the built-in CSV export at Analytics > Export in the Dixa UI (limited to 3 months per export, conversation-level metadata only), or use the Exports API at exports.dixa.io/v1/conversation_export with a Bearer token. The API returns JSON, supports time-range or conversation-ID queries, and is capped at 31-day windows per request.
What is the Dixa Exports API rate limit?
Dixa's public docs conflict: the general API standards page says 10 requests per second per token, while the Exports API guide lists 10 requests per minute for conversations and 3 requests per minute for messages. Code to the stricter endpoint-specific limits. Exceeding the limit returns HTTP 429 with no Retry-After header, so you need exponential backoff.
Does the Dixa message export include internal notes?
No. The message_export endpoint does not include internal notes. Internal notes are only available in the conversation_export endpoint under the conversation_wrapup_notes field. You need to merge data from both endpoints for a complete picture.
How do I export attachments from Dixa?
Attachments are not included in the CSV or API export payloads. The API response includes attachment metadata (URL, file type, file name). You must iterate through these and download each file individually. Private files on files.dixa.io require Basic auth with your API token. Download before decommissioning your Dixa account.
Can I export deactivated agents from Dixa?
By default, the GET /v1/agents endpoint returns only active agents. To include deactivated agents, pass ?active=false as a query parameter. Omitting this silently excludes former agents, which breaks historical conversation assignment mapping during migrations.

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