---
title: "Front to Thena Migration: A Technical Guide"
slug: front-to-thena-migration-a-technical-guide
date: 2026-08-12
author: Roopi
categories: [Front, Migration Guide, Help Desk]
excerpt: "No native importer exists between Front and Thena. This guide covers the API endpoints, data-model mapping, rate limits, and ETL pipeline needed for the migration."
tldr: Front to Thena migration requires a custom API-based ETL pipeline. No native importer exists. Budget 1–4 weeks depending on dataset size and API rate limits.
canonical: https://clonepartner.com/blog/front-to-thena-migration-a-technical-guide/
---

# Front to Thena Migration: A Technical Guide


# Front to Thena Migration: A Technical Guide

> [!NOTE]
> **TL;DR: Front → Thena Migration**
>
> Front is an email-first shared inbox; Thena is a Slack-native, AI-first B2B ticketing platform. No native migration tool exists between them. You must build a custom ETL pipeline using Front's Core API and Thena's Platform API. Expect to extract Front conversations, transform HTML email bodies into Slack-compatible formats, map Front Teammates to Thena Agents, and work within strict rate limits on both sides. Budget 1–4 weeks depending on dataset size.

Migrating from Front to Thena means translating a **shared-inbox conversation system** into an **account-centric, Slack-native B2B ticketing system**. Front anchors its architecture around the email protocol — treating SMS, WhatsApp, and webchat as extensions of a shared inbox. Thena flips this model, building its core around Slack and MS Teams, treating internal messaging channels as the primary interface for ticketing, routing, and AI triage.

Because these platforms solve the same problem using different architectural paradigms, this is a data-model transformation, not a simple field copy. Thena does not offer a built-in Front importer, and no third-party migration tool currently supports Front as a source and Thena as a target. Every migration requires a custom API-based ETL pipeline. If you're evaluating whether to leave Front at all, start with our [Top Front Alternatives in 2026](https://clonepartner.com/blog/blog/top-front-alternatives-in-2026-tco-features-migration-paths/) overview.

*Last verified against Front Core API (api2.frontapp.com) and Thena Platform API v2 (platform.thena.ai), tested June 2025. Both vendors update regularly — re-verify rate limits and endpoint availability before starting your migration.*

## What This Migration Actually Involves

**Front** is a shared-inbox platform designed around **Conversations** — threaded message streams spanning email, SMS, chat, social, and WhatsApp, organized into team inboxes with assignment, collision detection, and rule-based routing. Each Conversation belongs to one or more Inboxes, is linked to a Contact, optionally tied to an Account, and tagged for classification. When ticketing is enabled, Front layers additional status categories (Open, Waiting, Resolved) on top of the base conversation statuses. ([dev.frontapp.com](https://dev.frontapp.com/reference/conversations?utm_source=openai))

**Thena** is an AI-native B2B customer support platform organized around **Tickets**. Thena unifies conversations from Slack, MS Teams, Discord, email, and web chat into a single ticketing workspace with account management, SLAs, workflow automation, and AI-powered features like auto-tagging, sentiment analysis, and AI-generated summaries. Every ticket belongs to an Account, has an assigned agent, and carries AI metadata. Thena creates four default parent statuses for new organizations: Open, In progress, On hold, and Closed. ([docs.thena.ai](https://docs.thena.ai/platform/core-concepts/teams/overview?utm_source=openai))

The core mismatch: Front's data model is **inbox-and-conversation-centric** — a single conversation can live in multiple inboxes. Thena's model is **account-and-ticket-centric** — every ticket requires a `teamId` and is tied to an account hierarchy. Moving between them demands careful schema mapping at every level.

### Entity Relationship Comparison

The following diagram encodes the full relational schema for both platforms. Use it to identify where referential integrity constraints will force sequenced loading.

**Front Entity Graph:**
```
Company
  └── Workspace
        ├── Inbox (many)
        │     └── Conversation (many) ←── Tag (many-to-many)
        │           ├── Message (many)
        │           │     └── Attachment (many)
        │           ├── Comment (many, internal)
        │           └── Assignee → Teammate
        ├── Contact ←── Account (optional)
        ├── Teammate (many)
        └── Custom Field (scoped to Workspace)
```

**Thena Entity Graph:**
```
Organization
  └── Team (maps to Front Inbox)
        └── Ticket (many) ←── Tag (many-to-many)
              ├── Description (first message)
              ├── Comment (many, public or private)
              │     └── Attachment (many)
              ├── Assigned Agent → User
              └── Account (required for SLA; optional at creation)
                    ├── primaryDomain (required)
                    ├── secondaryDomain (optional)
                    └── Parent Account (optional, for subsidiaries)
```

**Key structural asymmetries this diagram exposes:**
- Front conversations can belong to multiple Inboxes simultaneously; Thena tickets have exactly one owning Team. This forces a canonical-inbox decision during transformation.
- Front Accounts are optional and can exist without domains; Thena Accounts require `primaryDomain`. Front instances without CRM integration often have conversations with no `_account_id` at all — in a 40K-conversation migration, this typically affects 10–15% of records and requires a separate attribution strategy.
- Thena's Comment model supports `is_private` at the field level; Front separates internal Comments from external Messages entirely. The mapping is straightforward but must be explicitly set per record.

## Data Model Mapping: Front vs. Thena

Before writing a single line of extraction code, map Front's schema to Thena's. This table covers the primary entities:

| Front Entity | Thena Equivalent | Mapping Notes |
|---|---|---|
| **Conversation** | **Ticket** | 1:1 mapping. Front conversation thread becomes Thena ticket history. Status mapping required. Multi-inbox conversations need one canonical Thena team. |
| **Message** | **Ticket Comment / Description** | First message → ticket description. Subsequent messages → public comments. Preserve author, timestamp, and inbound/outbound direction. |
| **Comment** | **Internal Note / Private Comment** | Front's internal `@`-mention comments map to Thena's private comments (`is_private: true`). Thena renders these as internal notes visible only to agents — verify this matches your team's expectation before assuming parity. |
| **Contact** | **Contact** | Email is the primary key on both sides. Phone, name, and custom fields require field-by-field mapping. |
| **Account** | **Account** | Front accounts can exist without domains. Thena accounts require `name` and `primaryDomain`, and support parent-subsidiary relationships. Domain cleanup may be needed before load. ([help.front.com](https://help.front.com/en/articles/2285?utm_source=openai)) |
| **Tag** | **Tag** | Direct mapping by name. Create all tags in Thena before import. |
| **Teammate** | **Agent / User** | Map Front teammate IDs to Thena user IDs. Build a lookup table before migration begins. |
| **Inbox** | **Team / Board** | Front inboxes map to Thena teams or ticket boards. No 1:1 API equivalent — requires an organizational decision. |
| **Custom Field** | **Custom Field** | Map field-by-field. Type compatibility must be verified — a Front `enum` must match a Thena `dropdown` or `single_select`; a Front `boolean` has no direct Thena equivalent and must be mapped to `checkbox` or a two-option `dropdown`. |
| **Rule** | **Workflow** | No automated migration. Rebuild manually in Thena's workflow builder. |
| **SLA Policy** | **SLA Policy** | No automated migration. Recreate in Thena's SLA management module. |

> [!NOTE]
> **What doesn't transfer automatically:** Front rules, macros, message templates, analytics history, Front Chat widget config, AI-generated Topics, Copilot history, shared drafts, collision detection state, and channel connection settings all require manual recreation in Thena or must be accepted as lost. Thena generates its own AI metadata (titles, summaries, sentiment, tags) post-ingestion — Front's AI outputs have no import path.

## Extracting Data from Front

Front's Core API is the only reliable extraction path for migration-scale data. Front also documents a sample export application that handles conversations, messages, comments, and attachments. ([dev.frontapp.com](https://dev.frontapp.com/docs/sample-application))

### Key Endpoints

- **`GET /conversations`** — List and search conversations. Supports filtering by inbox, tag, assignee, and date range via the `q` parameter.
- **`GET /conversations/search/{query}`** — Targeted slices using Front's search syntax: `is:open`, `tag:...`, `before:...`, `after:...`. Allows up to 15 filters per query with AND logic between different filter types. ([dev.frontapp.com](https://dev.frontapp.com/reference/search-conversations?utm_source=openai))
- **`GET /conversations/{id}/messages`** — All messages in a thread, including sender, recipients, body (HTML), and attachments.
- **`GET /conversations/{id}/comments`** — Internal comments (private notes).
- **`GET /contacts`** — All contacts with handles, custom fields, and linked accounts.
- **`GET /accounts`** — Accounts with custom fields and domain information.
- **`GET /tags`** — All tags across company, workspace, and personal scopes.

### Front API Rate Limits

Front's rate limits are **per-company, not per-token**:

| Plan | Global Rate Limit |
|---|---|
| Starter | 50 requests/minute |
| Professional | 100 requests/minute |
| Enterprise | 200 requests/minute |

The conversation search endpoint is further capped at **40% of the company rate limit**. Burst rate limits also apply: Tier 2 resources like conversations and messages are capped at 5 requests per resource per second. ([dev.frontapp.com](https://dev.frontapp.com/docs/rate-limiting))

Front returns a `429 Too Many Requests` response when limits are exceeded. Implement exponential backoff — don't just retry immediately.

```python
import requests
import time
import json

BASE_URL = "https://api2.frontapp.com"
HEADERS = {
    "Authorization": "Bearer YOUR_FRONT_API_TOKEN",
    "Accept": "application/json"
}

def fetch_paginated(endpoint, params=None):
    """Generic paginated fetch with rate-limit handling."""
    results = []
    url = f"{BASE_URL}{endpoint}"
    while url:
        resp = requests.get(url, headers=HEADERS, params=params)
        if resp.status_code == 429:
            time.sleep(60)  # Back off on rate limit
            continue
        resp.raise_for_status()
        data = resp.json()
        results.extend(data.get("_results", []))
        url = data.get("_pagination", {}).get("next", None)
        params = None  # Pagination URL includes params
        time.sleep(1.2)  # Stay under 50 rpm default
    return results

# Extract conversations with messages and comments
conversations = fetch_paginated("/conversations")
for conv in conversations:
    conv_id = conv["id"]
    conv["_messages"] = fetch_paginated(f"/conversations/{conv_id}/messages")
    conv["_comments"] = fetch_paginated(f"/conversations/{conv_id}/comments")
```

> [!TIP]
> **Rate limit math matters.** For a 10K-conversation migration on the 50 rpm Starter plan, extraction alone takes 10+ hours. Each conversation needs 1+ calls for messages plus 1 for comments, so net throughput is roughly 15–16 fully-extracted conversations per minute. If you have 50K+ conversations, consider purchasing Front's API rate limit add-on (available on Professional plan and above) before starting.

### Don't Rely on Front's Support Export or Analytics Export

Front offers a support-led account export, but it only includes shared-inbox content. It **excludes** individual inbox conversations, contact data, and tags. Front acknowledges export requests within 72 hours — not something to schedule during cutover week. ([help.front.com](https://help.front.com/en/articles/2225))

Front's analytics conversation export is capped at **31 days per export**, **100,000 rows**, and only covers inboxes with ticketing enabled. Useful for QA spot-checks, not for rebuilding full history. ([help.front.com](https://help.front.com/en/articles/4920896?utm_source=openai))

> [!WARNING]
> **Timestamps:** Front uses Unix time with millisecond precision. Thena uses ISO 8601 timestamps. Convert during transformation — don't store raw Unix timestamps in your intermediate format.

## Transformation: Mapping Front Data to Thena's Schema

The transformation layer is where most DIY migrations fail. Front and Thena diverge on several structural assumptions.

### Status Mapping

Front conversations carry a base `status` of `open`, `archived`, `deleted`, or `spam`. When ticketing is enabled, Front adds status categories: Open, Waiting, and Resolved. Thena's default statuses are Open, In progress, On hold, and Closed.

| Front Status | Thena Status | Notes |
|---|---|---|
| `open` (no assignee) | `Open` | Unassigned work |
| `open` (with assignee) | `In progress` | Active work |
| `waiting` (ticketing status) | `On hold` | Waiting on customer or third party. ([help.front.com](https://help.front.com/en/articles/1300288?utm_source=openai)) |
| `archived` / `resolved` | `Closed` | Completed work |
| `deleted` | Skip or `Closed` | Typically excluded from migration |
| `spam` | Skip | Do not migrate spam conversations |

> [!WARNING]
> **Snoozed is not the same as archived.** In Front's search, `is:snoozed` is its own state, but snoozed conversations can surface with `status: archived` in API responses — distinguished only through reminder metadata. If you map every `archived` record to Thena Closed, you will incorrectly close work that was only deferred. Detect snoozed conversations explicitly during extraction by checking for the presence of a `scheduled_reminders` array in the conversation payload. ([dev.frontapp.com](https://dev.frontapp.com/me/docs/search-1))

### Contact and Account Matching

Both platforms use **email domain** as the natural join key for accounts:

1. Extract all Front Accounts with their domain lists and custom fields.
2. Create corresponding Accounts in Thena via the Platform API. Thena accounts require `name` and `primaryDomain`, and support `secondaryDomain` plus parent-subsidiary relationships. Front accounts can exist without domains, so cleanup may be needed.
3. Build an `account_id_map: {front_account_id: thena_account_id}` lookup.
4. For Contacts, match on email address — create in Thena if not found, then link to the mapped Account.

**Handling conversations with no account ID:** Front instances that were not integrated with a CRM often have conversations where `_account_id` is null. This condition is more common than teams expect — it typically affects 10–15% of records in instances that grew organically without enforcing account linkage. For these records, you have three options:

- **Attribute to requestor only:** Create the ticket without `accountId`. Thena allows this; SLA policies based on account tier won't apply until backfilled.
- **Infer account from email domain:** Parse the requestor's email domain, match against existing Thena accounts, and link if a confident match exists. Flag ambiguous matches for manual review.
- **Create a placeholder account:** Useful when you want clean reporting immediately. Create an "Unattributed" account in Thena and assign all orphaned tickets to it, then let the team reclassify post-migration.

If your Front instance used custom fields to fake account hierarchy, this is the moment to decide whether to keep the flat model or upgrade into Thena's native parent-subsidiary account structure.

### HTML to Markdown Conversion

Front stores message bodies as HTML. Thena, being Slack-native, relies on Slack's `mrkdwn` format or plain text. Pushing raw HTML into Thena renders as broken code in the Slack interface.

Your transformation script must parse Front's `body` (HTML) and convert it to clean markdown. Python's `html2text` or Node.js's `turndown` handle the basics. Watch for:

- **Inline images** referenced as `cid:` attachments — these need separate download and re-upload
- **Email signatures** and inline CSS that inflate ticket descriptions
- **Tracking pixels** that should be stripped entirely
- **Front-specific formatting artifacts** in internal comments

### Resolving Assignees

When you extract a message from Front, the author is identified by a Front Teammate ID or a raw email address. Map this to a Thena User ID before constructing the payload.

If a historical agent has left the company and does not exist in Thena:
1. Provision a placeholder user in Thena to attribute historical actions to.
2. Or attribute actions to a generic system user and prepend the original author's name to the message body (e.g., `[Originally sent by alice@company.com]`).

### Handling Attachments

Front attachments are returned via `GET /messages/{id}/attachments`. These URLs are often authenticated. To migrate them:

1. Download the attachment from Front using your API token (counts against your rate limit).
2. Store temporarily — local disk or S3 staging bucket.
3. Upload to Thena via their attachment API or use `attachmentUrls` in the ticket create payload.
4. Replace the Front attachment URL in the message body with the new accessible URL.

For large-volume migrations, stage attachments in batches during off-peak hours. A 50K-conversation dataset with an average of 1.5 attachments per conversation means ~75K file transfers — plan storage and bandwidth accordingly.

### Conversation-to-Ticket Transformation Example

```python
def transform_conversation_to_ticket(front_conv, account_map, user_map, tag_map):
    messages = sorted(front_conv["_messages"], key=lambda m: m["created_at"])
    first_msg = messages[0] if messages else {}

    # Detect snoozed conversations before applying status map
    is_snoozed = bool(front_conv.get("scheduled_reminders"))

    # Status mapping
    if is_snoozed:
        thena_status = "On hold"  # Deferred, not completed
    else:
        status_map = {
            "open": "Open" if not front_conv.get("assignee") else "In progress",
            "archived": "Closed",
            "deleted": "Closed",
            "spam": None  # Skip
        }
        thena_status = status_map.get(front_conv.get("status"), "Open")

    if thena_status is None:
        return None

    return {
        "title": front_conv.get("subject") or first_msg.get("body", "")[:100],
        "description": html_to_markdown(first_msg.get("body", "")),
        "status": thena_status,
        "account_id": account_map.get(front_conv.get("_account_id")),
        "assigned_agent_id": user_map.get(
            front_conv.get("assignee", {}).get("id")
        ),
        "tags": [
            tag_map[t["id"]] for t in front_conv.get("tags", [])
            if t["id"] in tag_map
        ],
        "metadata": {
            "frontConversationId": front_conv["id"],
            "frontPrimaryInboxId": front_conv.get("_inbox_id"),
            "originalCreatedAt": unix_to_iso(front_conv["created_at"]),
            "wasSnoozed": is_snoozed
        },
        "comments": [
            {
                "body": html_to_markdown(msg.get("body", "")),
                "author_id": user_map.get(msg.get("author", {}).get("id")),
                "is_private": not msg.get("is_inbound", True),
                "created_at": unix_to_iso(msg["created_at"])
            }
            for msg in messages[1:]
        ]
    }
```

## Loading Data into Thena

Thena's Platform API v2 handles the write side. Authentication is via an `x-api-key` header, with API keys generated from **Dashboard → Organization Settings → Security and Access**.

> [!WARNING]
> **API access requires the Standard plan at minimum** ($79/user/month billed annually, verified June 2025). The Free plan does not include API access. Verify your Thena plan before starting migration development.

### Rate Limits and Throughput

Thena's standard-tier rate limit is **60 requests per minute** (per user, org, and IP). Enterprise plans offer custom limits. Confirm current limits against Thena's API documentation before building — this value has changed across Thena's API versions.

```
# Load throughput estimate at 60 rpm:
# Creating 1 ticket = 1 API call
#   + 1 per comment (N messages - 1)
#   + 1 for tag assignment
#   + 1 per attachment upload
#
# A conversation with 8 messages and 2 attachments:
#   1 + 7 + 1 + 2 = 11 API calls
#   At 60 rpm → ~5.4 such tickets per minute
#   10,000 tickets ≈ 31 hours of load time
```

This is the primary bottleneck. For large migrations (10K+ tickets), the combined extraction + load time can stretch to 3–5 days of continuous operation.

### Load Sequence

Order matters. Thena enforces referential integrity on some entities:

1. **Accounts** — Create all accounts first. Capture `{front_account_id: thena_account_id}` mapping.
2. **Contacts** — Create or match contacts. Link to accounts.
3. **Tags** — Create all tags. Capture name-to-ID mapping.
4. **Agents/Users** — Build `{front_teammate_id: thena_user_id}` from existing Thena users. Invite agents manually first — don't create via API.
5. **Tickets** — Create tickets with full metadata: title, description, status, assignee, account, tags, custom fields.
6. **Comments** — Append conversation history as comments on each ticket, preserving chronological order and author attribution.
7. **Attachments** — Upload and link to the relevant ticket or comment.

```python
import requests
import time

THENA_BASE = "https://platform.thena.ai/v1"
THENA_HEADERS = {
    "x-api-key": "YOUR_THENA_API_KEY",
    "Content-Type": "application/json"
}

def create_thena_ticket(ticket_data):
    resp = requests.post(
        f"{THENA_BASE}/tickets",
        headers=THENA_HEADERS,
        json=ticket_data
    )
    if resp.status_code == 429:
        time.sleep(60)  # Wait full minute on rate limit
        return create_thena_ticket(ticket_data)  # Retry
    resp.raise_for_status()
    return resp.json()
```

A practical Thena ticket payload that preserves source context:

```json
{
  "title": "Payment failed after renewal",
  "requestorEmail": "customer@example.com",
  "teamId": "SUPPORT",
  "statusName": "On hold",
  "description": "[Imported from Front cnv_123]\n[Original: 2026-05-14T09:22:11Z]\n\nCustomer reports renewal payment failure after invoice update.",
  "metadata": {
    "frontConversationId": "cnv_123",
    "frontTicketIds": ["TICKET-9831"],
    "frontAssigneeId": "tea_456",
    "frontPrimaryInboxId": "inb_789"
  }
}
```

> [!CAUTION]
> **Timestamp override limitation:** Thena's documented ticket-create parameters do **not** expose a `createdAt` override (verified against Thena Platform API v2, June 2025). Historical tickets will carry the migration date as their creation timestamp. Preserve original timestamps in metadata, a dedicated custom field, or as a prefix in the message body (e.g., `[Original Date: 2024-03-12 14:00 UTC]`). Re-verify this constraint against Thena's current API before starting — endpoint parameters may be added in future releases. ([docs.thena.ai](https://docs.thena.ai/api-reference/mcp/tickets/create-ticket?utm_source=openai))

> [!TIP]
> **Preserve source IDs everywhere.** Store `frontConversationId`, original assignee, source channel, original timestamps, and any Front `ticket_ids` in Thena custom fields or metadata. This single decision makes reconciliation, retries, and support escalations dramatically easier. ([dev.frontapp.com](https://dev.frontapp.com/reference/conversations))

## Edge Cases and Failure Modes

These are the issues that consistently surface in Front-to-helpdesk migrations, documented with their root causes and resolution paths:

### Multi-Inbox Conversations
A single Front conversation can appear in multiple inboxes. A Thena ticket needs one owning `teamId`. You need a deterministic rule for primary ownership — typically the first inbox the conversation appeared in, which is accessible via the `inboxes` array sorted by `created_at`. Preserve secondary inbox membership as tags or metadata fields.

**Resolution path:** Build your canonical-inbox logic during the transformation phase, not at load time. If you defer this decision to load time, you get inconsistent routing and broken SLA reporting. A safe default rule: if a conversation appears in exactly one inbox, that inbox owns it; if it appears in multiple, use the inbox with the most recent message activity.

### Conversations With No Account ID (Silent 10–15% Problem)
Front instances that were deployed without CRM integration, or where account linking was inconsistently enforced, often have a significant share of conversations where `_account_id` is null. This condition is invisible until you hit it during transformation.

**Resolution path:** Before writing transformation code, query `GET /conversations` with a filter and count records where the embedded `_links.related.contact` leads to a contact with no associated account. If this number exceeds 5% of your total conversation volume, design your account attribution strategy upfront rather than treating it as an exception handler.

### Deleted and Merged Conversations
Front's `deleted` and `spam` statuses usually indicate data you don't want in the new system. Merged conversations are trickier — the merge target has a conversation trail, but the source may still exist as a redirect. Export only the merge *target* to avoid duplicates.

### Snoozed Conversations
As noted in the status mapping section, snoozed conversations can surface as `archived` in the API. Check for `scheduled_reminders` in the conversation payload to detect them explicitly. Without this check, you will close work that was only deferred.

### Channel Mismatch
Front message types include email, SMS, WhatsApp, Intercom, calls, Facebook, Google Play, Front Chat, and X/Twitter. Thena's native channels are Slack, MS Teams, email, web chat, Discord, forms, and API-created tickets. If your Front history contains voice or social conversations, you can preserve them only as historical transcript data inside Thena tickets — not as native live channel objects. ([dev.frontapp.com](https://dev.frontapp.com/reference/messages?utm_source=openai))

### X/Twitter Content Redaction
Front explicitly redacts Twitter/X content in API responses for `tweet` and `tweet_dm` messages: body, text, blurb, and handles are replaced with redacted placeholders. This is a Front platform-level constraint tied to X/Twitter's API terms, not a bug you can work around. If that data matters, capture it from another compliant source before starting — Front's API alone won't give you full-fidelity X history. ([dev.frontapp.com](https://dev.frontapp.com/reference/messages?utm_source=openai))

### Export Scope Mismatch
Teams sometimes assume Front's support export is a full tenant dump. It isn't. Shared-inbox content is available; individual inbox content, tags, and contact data are not. If account managers worked customer threads in personal inboxes, you need an API strategy that explicitly covers those inboxes. ([help.front.com](https://help.front.com/en/articles/2225))

### Custom Field Type Mismatches
Front's `boolean` custom fields don't have a direct equivalent if Thena treats them as `checkbox` vs. `dropdown`. Front `enum` fields must map to Thena `dropdown` or `single_select` — verify option lists match exactly before load, or option values will create orphaned records. Test every custom field type mapping in a sandbox environment against real data samples before running the full migration.

### Historical Tickets Won't Be Slack-Native
Thena's strength is Slack-native support. Historical Front conversations imported via API will not have native Slack thread references — they'll appear as standard tickets in Thena's web interface. New conversations will be Slack-native; old ones won't. Set agent expectations about this before cutover.

### Rate Limit Stacking
Both APIs rate-limit independently. If you run extraction and load in parallel (which you should, using a queue-based architecture), monitor both rate-limit headers separately. A 429 from Front doesn't mean you need to pause Thena writes, and vice versa. Use separate rate-limit buckets in your pipeline controller for each API.

## Migration Timeline and Effort Estimate

| Dataset Size | Extraction Time | Transform (Dev) | Load Time | QA & Validation | Total Elapsed |
|---|---|---|---|---|---|
| 1K–5K conversations | 2–6 hrs | 4–8 hrs | 4–12 hrs | 4–8 hrs | 3–5 days |
| 5K–25K conversations | 6–24 hrs | 8–16 hrs | 1–3 days | 1–2 days | 1–2 weeks |
| 25K–100K conversations | 1–4 days | 16–40 hrs | 3–7 days | 2–3 days | 2–4 weeks |
| 100K+ conversations | 4+ days | 40+ hrs | 7+ days | 3–5 days | 4–8 weeks |

These estimates assume a single engineer working at standard API rate limits on both sides (50 rpm Front Starter, 60 rpm Thena Standard). Parallelism helps on the extraction side — paginate by inbox and run multiple inbox extractions concurrently — but load throughput is gated by Thena's 60 rpm limit unless you're on an Enterprise plan with custom rate limits.

**The dominant time variable is message density, not conversation count.** A dataset of 10K conversations with an average of 12 messages each requires 120K+ API calls on the Front extraction side alone. Audit your average messages-per-conversation before committing to a timeline.

## Cutover Strategy

The cleanest approach:

1. **Freeze date.** Pick a cutover date 1–2 weeks out. Communicate to all agents.
2. **Historical migration.** Run the full ETL pipeline for all conversations created before the freeze date. This runs in the background while your team continues working in Front.
3. **Delta sync.** On cutover day, re-extract conversations created or updated after your initial extraction started. Use Front's **Events** endpoint — filter by `types` and `after` timestamp. Key nuance: event filtering works on **`emitted_at`**, not the target object's `created_at`. Event IDs are sequential and never reused, so your delta-sync watermark should be based on event ID or `emitted_at`, not message timestamps. ([dev.frontapp.com](https://dev.frontapp.com/reference/events))
4. **Validation.** Run reconciliation counts: total conversations exported vs. tickets created. Spot-check 50–100 tickets for correct assignee, tags, status, full message history, and attachment presence. Thena's entity search API supports `filter_by`, `sort_by`, and `streaming=true` (capped at 250 per page, verify current limit before use), which is efficient for reconciliation. ([docs.thena.ai](https://docs.thena.ai/api-reference/search/introduction))
5. **Switch.** Point communication channels (email forwarding, chat widgets, Slack integrations) to Thena. Disable active processing in Front.
6. **Parallel read period.** Keep Front in read-only mode for 2–4 weeks so agents can reference historical context if something didn't transfer.

> [!TIP]
> **Don't skip the delta sync.** Conversations created during the migration window typically account for 3–8% of total volume. Missing them means your agents start on Thena with gaps in their most recent customer interactions — the ones they're most likely to need.

## What You Lose in This Migration

Be upfront with stakeholders about what doesn't survive:

- **Front rules and macros** — Must be rebuilt as Thena workflows
- **Front Chat widget config** — Thena uses its own web chat widget; reconfigure from scratch
- **Analytics history** — Front's analytics data does not export in a format Thena can ingest
- **Message templates / canned responses** — Export manually from Front, recreate in Thena
- **Collision detection state** — Real-time collaboration metadata does not transfer
- **AI-generated Topics and Copilot history** — Not exportable; Thena generates its own AI metadata for new tickets
- **Shared drafts** — Draft state doesn't survive; finalize or discard all in-progress drafts before cutover
- **Granular routing history** — Front tracks events like "Teammate A moved this to Inbox B." Thena's API does not support importing arbitrary historical audit logs. You retain messages, comments, and final state — not the routing breadcrumb trail.
- **Original creation timestamps on tickets** — Thena's ticket-create API does not accept a `createdAt` override. All imported tickets will show the migration date as their creation date unless you embed original timestamps in metadata or description fields.

## When to Use a Custom Pipeline vs. Alternatives

A custom API pipeline is appropriate in most cases, but the decision depends on specific signals:

**Use a custom API pipeline when:**
- Dataset exceeds 500 conversations with meaningful message history
- You have engineering resources available for 2–4 weeks of build and monitoring
- Historical context in Thena is required for active account management or SLA reporting

**Consider a manual approach when:**
- Dataset is under 500 conversations with shallow message histories
- Conversations are predominantly closed — historical read access in Front is acceptable
- Engineering resources are unavailable or cost-prohibitive for a short migration window

**Consider a clean-start approach when:**
- Your primary motivation is Slack-native support for new conversations, not preserving history
- Front conversation history is primarily closed/resolved with no ongoing SLA implications
- Team is willing to keep Front as a read-only archive for 6–12 months while Thena handles new volume

The clean-start approach eliminates all ETL risk and gets your team productive on Thena immediately. The cost is losing the ability to search historical context from Thena's interface — agents must switch to Front for pre-migration records. This is an acceptable operational trade-off for many teams.

**The break-even point for engineering effort:** At Thena's 60 rpm load rate, building and operating a custom pipeline costs roughly 40–80 hours of engineering time across scoping, development, testing, cutover, and validation. At $150–200/hour fully loaded, this is a $6,000–$16,000 investment. For datasets under 1,000 conversations, manual recreation is often faster and cheaper. For datasets over 5,000 conversations, the API pipeline pays for itself in reduced error rate and auditability.

For related migration paths, see our [Front to Zendesk](https://clonepartner.com/blog/blog/how-to-migrate-from-front-to-zendesk-the-complete-technical-guide/), [Front to Freshchat](https://clonepartner.com/blog/blog/front-to-freshchat-migration-a-technical-guide/), [Front to Intercom](https://clonepartner.com/blog/blog/front-to-intercom-migration-the-2026-technical-guide/), and [Front to HubSpot Service Hub](https://clonepartner.com/blog/blog/front-to-hubspot-service-hub-migration-the-complete-guide/) guides. For pre-migration preparation, our [Front Migration Checklist](https://clonepartner.com/blog/blog/front-migration-checklist/) covers the full sequence.

## Getting This Migration Done

This migration is technically straightforward but operationally demanding — the API constraints on both sides turn a logically simple ETL into a multi-day operation requiring monitoring, retries, and careful validation. The most common failure modes are not technical: they're decisions deferred until load time (canonical inbox selection, account attribution for orphaned conversations, custom field type mismatches) that should have been resolved during the transformation design phase.

If you're building this pipeline yourself: resolve the schema decisions before writing extraction code, instrument your pipeline with per-entity success/failure counters from day one, and run a 100-conversation sample load against a Thena sandbox before committing to the full dataset.

> Planning a Front to Thena migration? Our engineers handle the full pipeline — extraction, mapping, loading, and validation — so you don't have to build it from scratch.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Is there a native migration tool to move from Front to Thena?

No. Neither Front nor Thena provides a built-in migration tool for this path, and no third-party connector currently supports it. You must build a custom ETL pipeline using Front's Core API and Thena's Platform API. Datasets under 500 conversations may work with a manual CSV approach.

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

Rules, macros, message templates, analytics history, Front Chat widget configuration, AI Topics, Copilot history, shared drafts, collision detection state, and granular routing history do not transfer. These must be rebuilt manually in Thena or accepted as lost.

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

For a 10K-conversation dataset on default API rate limits, expect 10+ hours of extraction and 30+ hours of loading, plus development and validation time. Total elapsed time is typically 1–2 weeks. Datasets over 25K conversations can take 2–4 weeks or more.

### Can I use Front's analytics export for a historical migration?

No. Front's analytics conversation export is capped at 31 days per export and 100,000 rows, and only covers inboxes with ticketing enabled. Use the Core API for primary extraction.

### Does Thena's free plan support API-based data import?

No. Thena's API access requires the Standard plan ($79/user/month billed annually) or higher. The Free plan does not include API access, so you cannot run a programmatic migration against it.
