---
title: "Thena to Crisp Migration: A Technical Guide"
slug: thena-to-crisp-migration-a-technical-guide
date: 2026-08-24
author: Abdul
categories: [Migration Guide, Help Desk]
excerpt: "Technical guide to migrating from Thena to Crisp: API extraction, data model mapping, crisp-import-conversations tool, rate limits, and edge cases."
tldr: "Thena to Crisp migration requires API extraction of requests, transformation from ticket-based to conversation-centric data, and loading via Crisp's import tool — no native connector exists."
canonical: https://clonepartner.com/blog/thena-to-crisp-migration-a-technical-guide/
---

# Thena to Crisp Migration: A Technical Guide


# Thena to Crisp Migration: A Technical Guide

Migrating from Thena to Crisp is an API-to-API project. There is no native connector, no shared export format, and no built-in import adapter between the two platforms. The practical path is to extract tickets ("requests") and their messages from Thena's Platform API, transform the data to match Crisp's conversation-centric schema, and load it using the open-source `crisp-import-conversations` tool or the Crisp REST API directly.

This guide covers the full technical architecture: data model differences, API constraints on both sides, extraction and loading patterns, field mapping, edge cases, and validation. It is written for engineering leads and CX ops teams planning a move from Thena's Slack-native, AI-first ticketing platform to Crisp's chat-centric shared inbox.

*All API references target Thena Platform API v1 and Crisp REST API V1. Both platforms update their APIs — verify endpoints against current documentation before writing migration code. API behavior in this guide was verified against published documentation and observed API responses; field names should be confirmed against live API output before coding.*

## Why Teams Move from Thena to Crisp

**Thena** is an AI customer support platform built for B2B teams. It runs natively inside Slack, MS Teams, email, and web chat, with AI agents that auto-detect requests, triage tickets, and manage account-level workflows. Thena is purpose-built for B2B support with deep Slack integration — not for consumer-facing live chat.

**Crisp** is a chat-first customer messaging platform with a shared inbox, CRM, knowledge base, chatbot builder, and ticketing. It is designed for teams where operators handle conversations directly through a web widget, with automation layered on top.

The most common triggers for this migration:

- **Pricing model shift:** Thena charges per user — Starter at $29/user/month, Standard at $79/user/month, Enterprise at $119/user/month (all billed annually). Crisp charges per workspace: Free ($0, 2 seats), Mini ($45/mo, 4 seats), Essentials ($95/mo, 10 seats), Plus ($295/mo, 20 seats). For a 10-person team, Thena Standard costs $790/month vs. Crisp Essentials at $95/month — an 88% reduction.
- **Channel focus change:** Teams that outgrow Slack-first support and need a customer-facing web chat widget with built-in CRM and knowledge base find Crisp's chat-centric model a better fit.
- **Simplification:** B2B teams using Thena's AI-heavy, account-centric model may find they need a simpler shared inbox with less operational overhead for smaller-scale support.
- **Widget-first experience:** Crisp's embeddable chat widget with co-browsing, video calls, and screen sharing is purpose-built for real-time customer interaction — capabilities that are not Thena's core focus.

> [!NOTE]
> Thena's API access requires the Standard plan ($79/user/month) or higher. If you are on the Starter plan, you will need to either upgrade before migration or rely on the dashboard CSV/JSON export, which has significant limitations for full data extraction.

## Data Model Differences: Thena Requests vs. Crisp Conversations

This is not a 1:1 migration. As we've detailed in our [Thena to Kustomer migration](https://clonepartner.com/blog/blog/thena-to-kustomer-migration-a-technical-guide/) guide, moving off Thena requires a fundamental data-model transformation. The two platforms organize support data very differently. Thena treats Slack as the database of record for interactions and organizes everything under company-level Accounts. Crisp uses its own proprietary database built around individual web sessions.

**Thena is request-centric (ticket-based).** The primary entity is a **Request** — a ticket that can originate from Slack, email, web chat, or MS Teams. Requests belong to **Accounts** (company-level groupings that link multiple contacts, contracts, and SLA policies to a single B2B customer), are assigned to **Teams**, carry **Tags**, have **Custom Fields** via Forms, support **SLA policies**, and contain **Comments** (messages, notes).

**Crisp is conversation-centric.** The primary entity is a **Conversation** identified by a `session_id`, tied to a **Website** (workspace). Conversations carry messages in a flat stream (text, notes, files, events), belong to **Segments** (free-form tags), and link to **People** profiles. Crisp has no native account-level grouping — contacts are individual.

| Thena Concept | Crisp Equivalent | Notes |
|---|---|---|
| Request (Ticket) | Conversation | 1:1 mapping, but structure differs |
| Account | No direct equivalent | Flatten to People profiles or use Segments |
| Comment (message) | Message (text/note) | Map based on comment type |
| Internal Note | Message (type: `note`) | Requires Mini plan or higher |
| Tag | Segment | Free-form strings in both |
| Custom Field | Conversation metadata / People data | Limited mapping options |
| Agent/Assignee | Operator | Map by email address |
| SLA Policy | No equivalent | Crisp has no built-in SLA engine |
| Status (open/pending/resolved/closed) | State (pending/unresolved/resolved) | Lossy mapping |
| Priority | No native field | Store in metadata or segments |
| Account contacts | People profiles | Lose account-level grouping |

> [!WARNING]
> Thena's account-centric model has no equivalent in Crisp. If your workflows depend on viewing all tickets for a single B2B customer account, you will need to encode account information into Crisp segments or People profile data fields. This is the biggest structural loss in this migration. Concretely: any dashboard view that filters by company, any report showing ticket volume by account, and any SLA tracking by account tier will break. These cannot be reconstructed from Crisp's data model without building external reporting on top of the Crisp API.

## Step 1: Extract Data from Thena

You have two extraction paths, and the right one depends on your data volume and plan.

### Option A: Thena Platform API (Recommended)

The Thena Platform API provides programmatic access to requests and their associated data. The base URL is `https://platform.thena.ai/v1/`. Authentication uses an `x-api-key` header — generate your key from **Dashboard → Organization Settings → Security and Access**.

The key endpoint for extraction:

```bash
curl -X GET https://platform.thena.ai/v1/requests \
  -H "x-api-key: YOUR_API_KEY"
```

A representative JSON response from `GET /v1/requests` has this shape:

```json
{
  "data": [
    {
      "id": "req_01HABCDEF",
      "title": "API authentication error",
      "status": "open",
      "priority": "high",
      "createdAt": "2024-01-15T09:32:00Z",
      "updatedAt": "2024-01-15T11:47:00Z",
      "requesterName": "Jane Smith",
      "requesterEmail": "jane@acme.com",
      "accountId": "acc_01HXYZ",
      "accountName": "Acme Corp",
      "assigneeEmail": "agent@yourcompany.com",
      "tags": ["billing", "enterprise"],
      "comments": [
        {
          "id": "cmt_01H111",
          "body": "I'm getting a 401 on the /auth endpoint.",
          "authorType": "user",
          "isInternal": false,
          "createdAt": "2024-01-15T09:32:00Z"
        },
        {
          "id": "cmt_01H222",
          "body": "Checking the API key scopes now.",
          "authorType": "agent",
          "isInternal": true,
          "createdAt": "2024-01-15T10:05:00Z"
        }
      ]
    }
  ],
  "page": 1,
  "totalPages": 47
}
```

*Note: Field names shown reflect documented API behavior. Confirm against live API output before mapping — Thena does not version-pin field names in their public documentation.*

**Rate limits:** Thena's standard tier allows **60 requests per minute** per user, org, and IP. Rate limit information is returned in response headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`). Exceeding the limit returns a `429 Too Many Requests` error.

**Worked time estimate for extraction:**

| Dataset Size | Tickets | Avg Messages/Ticket | Total API Calls | Time at 60 req/min |
|---|---|---|---|---|
| Small | 500 | 5 | 3,000 | ~50 minutes |
| Medium | 5,000 | 8 | 45,000 | ~12.5 hours |
| Large | 10,000 | 10 | 110,000 | ~30.5 hours |
| XL | 25,000 | 12 | 325,000 | ~90 hours |

*Assumes 1 API call per ticket fetch + 1 per message batch per ticket. Attachment downloads are additive and not included. Add 20–30% buffer for retries and backoff delays.*

For a workspace with 5,000 tickets averaging 8 messages each, budget approximately 15 hours of extraction time including retry buffer. Implement exponential backoff on 429 responses and cache extracted data locally to a staging database (PostgreSQL or SQLite) to avoid re-fetching on failures.

```python
import requests
import time
import json

API_KEY = "your-api-key"
BASE_URL = "https://platform.thena.ai/v1"

def fetch_all_requests():
    all_requests = []
    page = 1
    
    while True:
        response = requests.get(
            f"{BASE_URL}/requests",
            headers={"x-api-key": API_KEY},
            params={"page": page}
        )
        
        if response.status_code == 429:
            reset_time = int(response.headers.get("X-RateLimit-Reset", 60))
            time.sleep(max(reset_time - time.time(), 1))
            continue
        
        response.raise_for_status()
        data = response.json()
        
        if not data.get("data"):
            break
        
        all_requests.extend(data["data"])
        page += 1
        time.sleep(1)  # Respect rate limits
    
    return all_requests
```

Store extracted data in a local staging database rather than attempting to transform and load it in memory. This provides idempotency — if your script crashes, you do not have to re-fetch thousands of records from Thena.

**Thena webhook availability:** As of current documentation, Thena does not expose a webhook or event stream for historical ticket data. Polling via the `GET /requests` endpoint with `updatedAt` filtering is the only supported pattern for both bulk extraction and delta sync during cutover. This means there is no event-driven alternative to the polling approach described here.

### Option B: Dashboard Export (Limited)

Thena's dashboard offers a manual export from the **Requests** section under the **Customer Support** tab. You can export either "Filtered requests in view" or "All requests in time frame" in **JSON or XLSX format**.

Limitations of the dashboard export:
- Exports request-level metadata only — individual message threads and comments may not be fully included
- No programmatic pagination or automation
- File size and time frame constraints for large datasets
- No attachment export

The dashboard export works as a quick validation source or for very small migrations (< 500 tickets), but the API is the right path for any production migration.

For a deeper look at extraction options, see our guide on [how to export data from Thena](https://clonepartner.com/blog/blog/how-to-export-data-from-thena-methods-api-limits-formats/).

## Step 2: Transform Data for Crisp's Schema

The transformation layer is where most migration complexity lives. Similar to a [Thena to LiveChat migration](https://clonepartner.com/blog/blog/thena-to-livechat-migration-the-complete-technical-guide/), you are converting a ticket-based, account-centric model to a flat conversation stream.

### Conversation Structure for Crisp Import

Crisp's `crisp-import-conversations` tool expects each conversation as a JSON object with this shape:

```json
{
  "user": {
    "name": "Jane Smith",
    "email": "jane@acme.com",
    "country": "US"
  },
  "messages": [
    {
      "text": "I need help with API authentication",
      "date": 1700000000000,
      "from": "user"
    },
    {
      "note": "Internal: Customer is on Enterprise plan",
      "date": 1700003600000,
      "from": "operator"
    },
    {
      "text": "Here's the documentation link for API keys...",
      "date": 1700007200000,
      "from": "operator"
    }
  ]
}
```

### Field Mapping Logic

**Status mapping (lossy):**

| Thena Status | Crisp State |
|---|---|
| `open` | `unresolved` |
| `pending` | `pending` |
| `resolved` | `resolved` |
| `closed` | `resolved` |

Thena distinguishes `resolved` from `closed`; Crisp does not. Both map to `resolved`.

**Message mapping:**
- Thena comments with `type: message` → Crisp message with `"from": "user"` or `"from": "operator"` based on the author
- Thena internal notes → Crisp message with `"note": "..."` and `"from": "operator"`
- Timestamps must be Unix milliseconds
- Messages must be sorted chronologically

**Contact/People mapping:**
- Thena associates contacts with **Accounts** (company-level). Crisp uses individual **People** profiles.
- Extract the requester's name and email from each Thena request and map to the conversation's `user` object
- Store the Thena Account name as a Crisp segment (e.g., `account:acme-corp`) to preserve account groupings

**Conversation metadata (`meta.data`):** Crisp supports key-value pairs in `meta.data` on conversations. These are unstructured string fields with no enforced schema and no dedicated UI — they appear in the conversation sidebar and are queryable via the API but not filterable in the Crisp dashboard as of current documentation. Use them to store Thena-specific fields (request ID, account ID, priority, SLA tier) that have no direct Crisp equivalent. Example:

```json
{
  "meta": {
    "data": {
      "thena_request_id": "req_01HABCDEF",
      "thena_account_id": "acc_01HXYZ",
      "thena_priority": "high",
      "thena_sla_tier": "enterprise"
    }
  }
}
```

**Tags → Segments:**
- Thena tags map directly to Crisp segments as strings
- Add a `migrated-from-thena` segment to all imported conversations for identification and potential rollback

### Transforming Slack-Native Formatting

Thena messages originating from Slack carry Slack-specific formatting quirks. If you push raw Slack messages into Crisp, the output will be unreadable for your agents.

**Handling User Mentions:** Slack formats user mentions as `<@U12345678>`. Crisp agents will not know who `U12345678` is. Your transformation layer must:
1. Maintain a map of Slack User IDs to actual names (build this from the Thena/Slack users endpoint).
2. Use regex to find all instances of `<@U...>` in the message body.
3. Replace the ID with the user's actual name, e.g., `@Jane Doe`.

```python
import re

def replace_slack_mentions(text, user_map):
    # user_map: {"U12345678": "Jane Doe", ...}
    def replace_match(match):
        uid = match.group(1)
        return f"@{user_map.get(uid, uid)}"
    return re.sub(r'<@(U[A-Z0-9]+)>', replace_match, text)
```

**Handling Markdown:** Slack uses single asterisks for bold `*text*` and underscores for italics `_text_`. Crisp supports standard markdown. Run a translation function to convert Slack's proprietary markdown into standard markdown before loading.

**Channel references:** Slack channel links appear as `<#C12345678|general>`. Strip these or replace with `#general` using regex: `re.sub(r'<#[A-Z0-9]+\|([^>]+)>', r'#\1', text)`.

**HTML and Rich Text:** Thena messages from email or web sources may contain HTML formatting. Crisp text messages support limited formatting. Strip or convert HTML to plain text during transformation, preserving links where possible.

### Crisp API Error Reference

Understanding what errors Crisp returns mid-import prevents silent data loss:

| HTTP Status | Error Code | Cause | Mitigation |
|---|---|---|---|
| `400` | `invalid_format` | Malformed message body or missing required field | Validate JSON schema before sending |
| `400` | `session_not_found` | `session_id` in message POST doesn't match a created conversation | Check conversation creation succeeded before pushing messages |
| `409` | `too_many_messages` | Conversation exceeds ~10,000 messages | Split into multiple conversations; add cross-reference note |
| `409` | `session_already_exists` | Duplicate conversation creation attempt | Use `resume: true`; track created session IDs locally |
| `429` | `rate_limited` | Daily quota or per-minute limit exceeded | Exponential backoff; request quota increase before large imports |
| `403` | `subscription_limit` | Feature not available on current Crisp plan | Check plan-feature matrix before importing notes or participants |

### Transformation Code

```python
def transform_request_to_conversation(thena_request):
    messages = []
    
    for comment in thena_request.get("comments", []):
        msg = {
            "date": int(parse_timestamp(comment["createdAt"]) * 1000),
            "from": "operator" if comment.get("isInternal") or comment.get("authorType") == "agent" else "user"
        }
        
        if comment.get("isInternal"):
            msg["note"] = comment.get("body", "")
        else:
            msg["text"] = comment.get("body", "")
        
        messages.append(msg)
    
    messages.sort(key=lambda m: m["date"])
    
    return {
        "user": {
            "name": thena_request.get("requesterName", "Unknown"),
            "email": thena_request.get("requesterEmail", "unknown@example.com")
        },
        "messages": messages,
        "meta": {
            "data": {
                "thena_request_id": thena_request.get("id", ""),
                "thena_account_id": thena_request.get("accountId", ""),
                "thena_priority": thena_request.get("priority", ""),
            }
        }
    }
```

### Handling Attachments

Migrating attachments is the most common failure point in help desk migrations. You cannot simply pass a Thena/Slack URL to Crisp, because those URLs are authenticated or may use signed/expiring URLs. When the old system is deprecated, those links will break.

The attachment migration flow:

1. **Download:** Your script must download the file from the Thena/Slack URL during extraction. You will need to pass the appropriate authentication headers to access the file. Store files locally or in a temporary cloud bucket.
2. **Re-host:** Upload files to a publicly accessible URL or to Crisp's storage via the `/file` endpoint.
3. **Reference:** Import as `file` type messages in Crisp conversations with the new hosted URL.

```json
{
  "type": "file",
  "from": "user",
  "origin": "chat",
  "content": {
    "name": "error_log.pdf",
    "url": "https://image.crisp.chat/file/..."
  }
}
```

Download and re-host all attachments **before** they expire. If your Thena subscription lapses, you lose access to those files permanently.

### Handling Anonymous or Bot-Generated Requests

Thena's AI agents can auto-create requests that may lack a human requester email. Crisp requires a conversation to have either a `user.email` or `user.name`. For requests with no contact info:
- Use a placeholder email like `unknown+{request_id}@yourdomain.com`
- Or skip these records and document the gap

Decide this **before** migration, not during.

### GDPR and Data Residency Considerations

For B2B teams migrating European customer data, two constraints apply:

- **Data residency:** Crisp stores data on EU infrastructure if your workspace is configured for it. Confirm your Crisp workspace region before starting — importing EU customer data into a US-region workspace may create compliance exposure.
- **Right to erasure:** Verify that People profiles created during migration can be individually deleted via the Crisp API (`DELETE /v1/website/{website_id}/people/profile/{people_id}`). Document the Thena-to-Crisp ID mapping so you can fulfill erasure requests against both systems during the transition period.
- **Data processor agreements:** If you are using a third-party migration service or cloud storage bucket as intermediate staging, confirm that entity is covered under your DPA chain.

## Step 3: Load Data into Crisp

You have two loading options: the `crisp-import-conversations` tool (recommended for most cases) or direct API calls.

### Option A: crisp-import-conversations (Recommended)

Crisp maintains an open-source Node.js tool specifically for importing conversation data: [`crisp-import-conversations`](https://github.com/crisp-im/crisp-import-conversations). It handles conversation creation, message sequencing, participant setup, and state management.

**Setup:**

1. Create a Marketplace account at `marketplace.crisp.chat`
2. Create a plugin and request a **production token** with these scopes:
   - `website:conversation:initiate` (write)
   - `website:conversation:sessions` (write)
   - `website:conversation:messages` (write)
   - `website:conversation:states` (write)
   - `website:conversation:participants` (write)
3. Request a daily quota increase using the formula: **(n × 5) + (n × m)** where `n` = number of conversations and `m` = average messages per conversation
4. Activate the plugin as **private**

For imports under 10,000 API calls, you can use a **website token** instead — these do not require Marketplace approval and can be generated from **Settings → Workspace Settings → Advanced Configuration**.

The tool has built-in adapters for Zendesk, Gorgias, Help Scout, Tidio, GrooveHQ, and WHMCS — but **no Thena adapter exists**. You will need to either:
- Transform your Thena data into the tool's generic JSON format (shown above) and use it without an adapter
- Write a custom adapter in the `/adapters` directory

```javascript
var CrispImport = require("./lib/import");

var Import = new CrispImport(
  {
    websiteId: "YOUR_WEBSITE_ID",
    websitePlan: "plus",
    tier: "plugin",
    identifier: "YOUR_TOKEN_IDENTIFIER",
    key: "YOUR_TOKEN_KEY",
    urn: "YOUR_PLUGIN_URN",
    name: "thena-migration",
    defaultUserNickname: "Thena User",
    defaultOperatorNickname: "Support Agent"
  },
  {
    resume: false
  }
);

Import.importFromFile("./res/conversations.json")
  .then((result) => {
    console.log(`Import done. ${result.count} conversations imported.`);
  })
  .catch((error) => {
    console.error("Import failed:", error);
  });
```

> [!WARNING]
> Before starting the import, contact Crisp support to temporarily block outgoing emails for your workspace. Without this, Crisp may send email notifications for every imported conversation — flooding your customers' inboxes.

### Option B: Direct Crisp REST API

If you need finer control, use the Crisp REST API directly. The sequence for each conversation:

1. `POST /v1/website/{website_id}/conversation` — create the conversation
2. `PATCH /v1/website/{website_id}/conversation/{session_id}/meta` — set people data, segments, and `meta.data` fields
3. `POST /v1/website/{website_id}/conversation/{session_id}/message` — add each message
4. `PATCH /v1/website/{website_id}/conversation/{session_id}/state` — set final state

Authentication uses Basic Auth with your identifier:key pair and the `X-Crisp-Tier: plugin` header.

When using the REST API directly, be aware that the standard endpoints may not natively support backdating individual messages. If historical timestamp accuracy matters, inject the original date into the message text as a workaround:

```text
[Original Date: 2023-10-15 14:32 UTC] 
Jane Doe: Here is the solution to your issue.
```

The `crisp-import-conversations` tool handles this more gracefully through its `date` field, which is one reason to prefer it.

### Crisp Plan Limitations

The import tool respects Crisp plan limits:

| Plan | Notes (type: note) | Extra Participants |
|---|---|---|
| Free | Not supported | Max 1 |
| Mini | Supported | Max 3 |
| Essentials | Supported | Max 10 |
| Plus | Supported | Max 10 |

If your Thena requests have internal notes, you need at least the **Mini plan** on Crisp. Set the `WEBSITE_PLAN` config variable so the import tool skips unsupported API calls rather than failing.

## API Rate Limits: Both Sides

Rate limiting is the primary bottleneck for this migration.

### Thena (Extraction Side)

- **Standard tier:** 60 requests per minute per user/org/IP
- **Enterprise tier:** Custom limits (contact Thena sales)
- Rate limit headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
- 429 responses when exceeded

### Crisp (Loading Side)

- **Plugin tokens** bypass per-minute rate limits but are subject to a **daily quota** that resets every 24 hours
- **Development tokens** have lower quotas — fine for testing, not for production imports
- **Website tokens** work for imports under 10,000 API calls
- If you hit quota limits mid-import, request a quota increase through the Marketplace, then resume with `resume: true`
- GET/HEAD routes use a cache layer (Bloom) that can absorb repeated reads without triggering rate limits
- Conversations are limited to approximately **10,000 messages each** — the API returns `409 too_many_messages` if exceeded

> [!TIP]
> For a migration of 3,000 conversations with an average of 10 messages each, your Crisp quota requirement is approximately **(3,000 × 5) + (3,000 × 10) = 45,000** API calls. Request this quota before starting. For a 10,000-conversation migration at 10 messages average, budget 150,000 API calls and expect the import to run approximately 48–72 hours at standard quota rates.

## Idempotency and Resumability

Because network timeouts happen and rate limits will interrupt long-running imports, your migration must be resumable without duplicating data.

The `crisp-import-conversations` tool supports a `resume: true` option that skips already-imported conversations. Use this if:
- Your import hits a quota limit partway through
- A network failure interrupts the process
- You need to re-run after fixing transformation bugs

If you are using the direct REST API, track state in your staging database:

| Status | Meaning |
|---|---|
| `pending` | Not yet processed |
| `session_created` | Conversation shell exists in Crisp |
| `messages_pushed` | All messages loaded |
| `meta_set` | Segments and metadata applied |
| `complete` | State set, fully migrated |
| `failed` | Error encountered; inspect `error_detail` column |

Store Thena request IDs alongside Crisp session IDs in a local mapping table. This serves three purposes: deduplication on resume, audit trail for validation, and erasure request fulfillment under GDPR.

## Edge Cases and Failure Modes

### Slack-Native Thread Context

Thena's Slack-native conversations carry thread context, channel names, emoji reactions, and Slack-specific formatting. None of this translates directly to Crisp. You will lose:
- Slack thread structure (collapses to flat message stream)
- Emoji reactions
- Channel metadata
- Slack user IDs (map to email addresses where possible)

### Account-Level Data Loss

Thena organizes requests under **Accounts** — a company-level entity linking multiple contacts, contracts, and SLA policies to a single B2B customer. Crisp has no equivalent. The practical impact:

- **Breaks:** Any view filtering tickets by company; any report showing ticket volume, resolution time, or CSAT by account; any SLA tracking by customer tier
- **Partial workaround:** Encode account name as a Crisp segment (`account:acme-corp`) and account ID in `meta.data`. This allows API-level filtering but does not surface in the Crisp dashboard UI.
- **Cannot reconstruct:** Multi-contact account relationships, account-level SLA policies, and contract-linked ticket histories have no Crisp equivalent. These require external reporting built on the Crisp API if they are business-critical.

### SLA Policy Loss

Thena supports SLA policies with response time targets, escalation rules, and compliance tracking. Crisp has no built-in SLA engine. If SLA enforcement is required, you will need a third-party integration (e.g., a custom webhook to an SLA tracking system) or accept this as a functionality gap.

### Custom Fields and Forms

Thena supports custom ticket fields through Forms. Crisp's `meta.data` on conversations supports key-value string pairs with no enforced schema, no field-level validation, and no dashboard filter UI. Map critical structured fields to `meta.data`; encode categorical fields as segments for any filtering use case; accept that complex form data with field types, validation rules, or conditional logic will be lossy.

### Conversations Exceeding 10,000 Messages

If a Thena request has an unusually long thread (common in long-running enterprise accounts), it may approach or exceed Crisp's 10,000-message limit. Detection:

```python
if len(thena_request.get("comments", [])) > 9500:
    # Split into multiple conversations
    # Add cross-reference note to each: 
    # "Continued from: [original request ID], Part 1 of N"
```

## What Doesn't Transfer

| Feature | Transfers? | Notes |
|---|---|---|
| Request/ticket content | ✅ Yes | As conversation messages |
| Internal notes | ✅ Yes | Mini plan or higher required |
| Tags | ✅ Yes | As Crisp segments |
| Contact name/email | ✅ Yes | As People profiles |
| Timestamps | ✅ Yes | Converted to Unix ms |
| Attachments | ⚠️ Partial | Requires re-hosting URLs |
| Account groupings | ❌ No | Encode as segments; dashboard views break |
| SLA policies | ❌ No | No Crisp equivalent |
| Custom fields/forms | ⚠️ Partial | Key-value `meta.data` only; no validation or UI |
| Slack thread structure | ❌ No | Flattened to message stream |
| Workflow automations | ❌ No | Rebuild in Crisp bot builder |
| AI agent configurations | ❌ No | Rebuild with Crisp chatbot |
| CSAT data | ❌ No | No import path |
| Analytics/reports | ❌ No | Historical analytics stay in Thena |
| Account-level SLA tracking | ❌ No | No Crisp equivalent; requires external tooling |
| Priority field | ⚠️ Partial | Store in `meta.data`; no native priority field in Crisp |

## Validation and Testing

### Pre-Migration Checklist

- [ ] Verify Thena API key has access to all required endpoints
- [ ] Confirm Thena plan includes API access (Standard or higher)
- [ ] Generate Crisp plugin token with all 5 required write scopes
- [ ] Request sufficient Crisp daily quota based on the formula: `(n × 5) + (n × m)`
- [ ] Contact Crisp support to disable outgoing emails during import
- [ ] Decide on anonymous contact handling strategy
- [ ] Map all Thena tags to Crisp segment names
- [ ] Document which custom fields will be preserved vs. dropped
- [ ] Build Slack user ID → real name mapping table
- [ ] Confirm Crisp workspace data region (EU vs. US) for GDPR compliance
- [ ] Verify DPA coverage for any intermediate storage used during migration
- [ ] Test transformation with 50–100 conversations before running full migration
- [ ] Confirm Crisp plan supports internal notes (Mini or higher)

### Post-Migration Validation

1. **Count check:** Compare total requests in Thena vs. conversations created in Crisp
2. **Message count:** Spot-check 20+ conversations — verify message counts match
3. **Timestamp ordering:** Confirm messages appear in correct chronological order
4. **Contact mapping:** Verify People profiles link correctly to conversations
5. **Segment verification:** Check that tags transferred as segments; confirm `migrated-from-thena` tag is present
6. **Note visibility:** Confirm internal notes appear as Crisp notes (not visible to customers)
7. **State accuracy:** Verify resolved/pending states mapped correctly
8. **Attachment accessibility:** Click through imported file messages to confirm URLs work
9. **Metadata spot-check:** Verify `meta.data` fields populated correctly for sampled conversations
10. **Slack formatting check:** Confirm user mentions display as names, not raw Slack IDs

```bash
# Quick count validation
echo "Thena requests exported: $(cat thena_export.json | jq length)"
echo "Crisp conversations created: $(curl -s -u $CRISP_ID:$CRISP_KEY \
  -H 'X-Crisp-Tier: plugin' \
  'https://api.crisp.chat/v1/website/YOUR_WEBSITE_ID/conversations/list/1' \
  | jq '.data | length')"
```

## Planning the Cutover

The safest approach is a **parallel-run cutover** with a short delta window. The delta between bulk migration and widget cutover is where data gets lost — keep this window under 24 hours.

**Step-by-step:**

1. **Freeze Thena configuration** — no new tags, no workflow changes during migration
2. **Record bulk migration start time** — this timestamp is the anchor for your delta query
3. **Run the bulk historical import** to Crisp while both platforms remain active. Budget time based on the extraction estimates above.
4. **Validate** imported data thoroughly using the post-migration checklist
5. **Switch your chat widget/email routing** from Thena to Crisp
6. **Run a delta migration** immediately after cutover — query Thena for requests with `updatedAt` after the bulk import start time:
   ```bash
   GET /v1/requests?updatedAfter=2024-01-20T09:00:00Z
   ```
7. **Import delta conversations** to Crisp; use `resume: true` to skip already-migrated records
8. **Decommission Thena** after confirming all active conversations are in Crisp and attachment URLs are re-hosted

**Thena webhook/event stream availability:** Thena does not currently expose webhooks for ticket updates that would allow event-driven delta sync. The `updatedAt` polling approach above is the supported method for catching changes during the cutover window.

For related migration patterns involving Crisp, see our guides on [Ada to Crisp migration](https://clonepartner.com/blog/blog/ada-to-crisp-migration-a-technical-guide/), [Dixa to Crisp migration](https://clonepartner.com/blog/blog/dixa-to-crisp-migration-the-complete-technical-guide/), [Kustomer to Crisp migration](https://clonepartner.com/blog/blog/kustomer-to-crisp-migration-a-technical-guide/), or [Crisp to Enchant migration](https://clonepartner.com/blog/blog/crisp-to-enchant-migration-a-technical-guide/).

## When This Migration Gets Hard

This migration is straightforward if you have fewer than 1,000 tickets, simple tags, and no attachment dependencies. Complexity scales with:

- **High volume:** At 60 req/min extraction and 10 API calls per ticket average, a 10,000-ticket migration requires approximately 28 hours of extraction time excluding attachment downloads. At 25,000 tickets, plan for 90+ hours. Buffer an additional 20–30% for retries.
- **Attachment-heavy tickets:** Each attachment requires a download-and-rehost cycle. A workspace with 10,000 attachments averaging 2MB each requires 20GB of intermediate storage and significant additional extraction time.
- **Complex custom fields:** Mapping Thena's form-based custom fields to Crisp's `meta.data` key-value store loses field type information, validation rules, and conditional logic. Fields that drove agent workflows need to be rebuilt as Crisp segments or bot logic.
- **Account-centric workflows:** If your team relies on Thena's account view to manage B2B relationships, the structural loss in Crisp demands workflow redesign. This is not a data problem — it is an architecture mismatch that requires either accepting the gap or building external account management tooling.
- **EU data residency:** If customer data is subject to GDPR and your Crisp workspace is US-hosted, resolve this before any data moves.

> Need to migrate from Thena to Crisp without downtime or data loss? We'll scope the migration, handle the API constraints, and validate the results — typically in days, not weeks.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can I export data from Thena to Crisp directly?

No. There is no native connector or shared format between Thena and Crisp. You must extract data via Thena's Platform API (or dashboard JSON/XLSX export for small datasets), transform it to match Crisp's conversation schema, and load it using the crisp-import-conversations tool or Crisp REST API.

### What are the Thena API rate limits for data migration?

Thena's standard tier allows 60 requests per minute per user, org, and IP. Enterprise plans offer custom limits. Rate limit details are returned in response headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset). Exceeding the limit returns a 429 error.

### Does Crisp have a Thena import adapter?

No. Crisp's open-source crisp-import-conversations tool supports adapters for Zendesk, Gorgias, Help Scout, Tidio, GrooveHQ, and WHMCS — but not Thena. You need to transform your Thena data into the tool's generic JSON format or write a custom adapter.

### What data is lost when migrating from Thena to Crisp?

Account-level groupings, SLA policies, workflow automations, AI agent configurations, Slack thread structure, CSAT survey data, and complex custom field values do not transfer. Tags map to segments, and internal notes require Crisp's Mini plan or higher.

### How long does a Thena to Crisp migration take?

For a typical workspace with 1,000–5,000 tickets, expect 2–5 days including extraction, transformation, test import, validation, and production cutover. The primary bottleneck is API rate limits on both sides — Thena at 60 req/min and Crisp's daily plugin quota.
