Thena to Crisp Migration: A Technical Guide
Technical guide to migrating from Thena to Crisp: API extraction, data model mapping, crisp-import-conversations tool, rate limits, and edge cases.
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
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.
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 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 |
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:
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:
{
"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.
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_requestsStore 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.
Step 2: Transform Data for Crisp's Schema
The transformation layer is where most migration complexity lives. Similar to a Thena to LiveChat migration, 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:
{
"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
userobject - 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:
{
"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-thenasegment 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:
- Maintain a map of Slack User IDs to actual names (build this from the Thena/Slack users endpoint).
- Use regex to find all instances of
<@U...>in the message body. - Replace the ID with the user's actual name, e.g.,
@Jane Doe.
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
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:
- 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.
- Re-host: Upload files to a publicly accessible URL or to Crisp's storage via the
/fileendpoint. - Reference: Import as
filetype messages in Crisp conversations with the new hosted URL.
{
"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. It handles conversation creation, message sequencing, participant setup, and state management.
Setup:
- Create a Marketplace account at
marketplace.crisp.chat - 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)
- Request a daily quota increase using the formula: (n × 5) + (n × m) where
n= number of conversations andm= average messages per conversation - 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
/adaptersdirectory
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);
});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:
POST /v1/website/{website_id}/conversation— create the conversationPATCH /v1/website/{website_id}/conversation/{session_id}/meta— set people data, segments, andmeta.datafieldsPOST /v1/website/{website_id}/conversation/{session_id}/message— add each messagePATCH /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:
[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_messagesif exceeded
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 inmeta.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:
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
- Count check: Compare total requests in Thena vs. conversations created in Crisp
- Message count: Spot-check 20+ conversations — verify message counts match
- Timestamp ordering: Confirm messages appear in correct chronological order
- Contact mapping: Verify People profiles link correctly to conversations
- Segment verification: Check that tags transferred as segments; confirm
migrated-from-thenatag is present - Note visibility: Confirm internal notes appear as Crisp notes (not visible to customers)
- State accuracy: Verify resolved/pending states mapped correctly
- Attachment accessibility: Click through imported file messages to confirm URLs work
- Metadata spot-check: Verify
meta.datafields populated correctly for sampled conversations - Slack formatting check: Confirm user mentions display as names, not raw Slack IDs
# 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:
- Freeze Thena configuration — no new tags, no workflow changes during migration
- Record bulk migration start time — this timestamp is the anchor for your delta query
- Run the bulk historical import to Crisp while both platforms remain active. Budget time based on the extraction estimates above.
- Validate imported data thoroughly using the post-migration checklist
- Switch your chat widget/email routing from Thena to Crisp
- Run a delta migration immediately after cutover — query Thena for requests with
updatedAtafter the bulk import start time:GET /v1/requests?updatedAfter=2024-01-20T09:00:00Z - Import delta conversations to Crisp; use
resume: trueto skip already-migrated records - 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, Dixa to Crisp migration, Kustomer to Crisp migration, or Crisp to Enchant migration.
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.datakey-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.
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.