---
title: "How to Export Data from Thena: Methods, API Limits & Formats"
slug: how-to-export-data-from-thena-methods-api-limits-formats
date: 2026-08-05
author: Nachi
categories: [Migration Guide, Help Desk]
excerpt: "Complete guide to exporting data from Thena. Covers UI exports (CSV, XLSX, JSON), REST API endpoints, Search API streaming, rate limits, webhooks, and data gaps."
tldr: "Thena offers ticket CSV and legacy XLSX/JSON exports plus a REST API at 60 req/min. Full extraction needs the Platform API — no bulk export exists for conversations, KB articles, or configs."
canonical: https://clonepartner.com/blog/how-to-export-data-from-thena-methods-api-limits-formats/
---

# How to Export Data from Thena: Methods, API Limits & Formats


# How to Export Data from Thena: Methods, API Limits & Formats

*Last verified: August 2025. Thena Platform API v1 endpoints validated against docs.thena.ai; legacy v2/v3 endpoints validated against help.thena.ai. Thena updates quarterly — re-check rate limits and endpoint availability if reading after February 2026.*

> [!NOTE]
> **TL;DR:** Thena offers two UI export paths (ticket CSV and legacy request XLSX/JSON) plus a REST API at `platform.thena.ai` with a 60 requests/minute rate limit on standard plans. The UI exports cover ticket metadata, customer info, SLA data, and custom fields — but not full conversation threads, attachments, knowledge base articles, or workflow configurations. For full-fidelity extraction, you need the Platform API's ticketing, accounts, and search endpoints with cursor-based pagination. There is no single "export everything" button. The rate limit is enforced simultaneously per API key, per IP, and per org — running parallel workers against the same key does not multiply your throughput.

Thena is an AI-native B2B helpdesk platform built around Slack, Microsoft Teams, email, and web chat. Because it acts as an intelligence layer on top of chat platforms, exporting its data is fundamentally different from exporting from a traditional ticketing system like Zendesk or Freshdesk. As with [exporting data from Unthread](https://clonepartner.com/blog/blog/how-to-export-data-from-unthread-methods-api-limits-formats/) and other Slack-native tools, much of the underlying conversation data is tied to chat application logic — thread timestamps, Slack user IDs, channel IDs, and message permalinks.

If you're planning a migration, building a data warehouse integration, or backing up your workspace, you won't find a one-click bulk export. This guide covers every extraction method available, their limitations, field-level schemas, and what you can't get out at all.

## Thena's Data Model: What You're Actually Exporting

Before touching any export button or writing API calls, understand how Thena structures data. Thena organizes all customer conversations, their issues, and responses under specific accounts. The core entities are:

- **Accounts/Organizations** — Company-level records with custom fields, contacts, notes, activities, and associated tickets. In Slack-native setups, these map to specific Slack Connect channels.
- **Contacts/Users** — Individual people linked to accounts, with email, name, and channel identity (a `slack_user_id` or `teams_user_id` alongside their email address).
- **Tickets** — The primary work unit. Tickets come in from Slack, MS Teams, email, web chat, forms, portals, and AI-generated workflows. A single ticket may carry metadata from multiple channels, including Slack thread links, message permalinks, and channel identifiers.
- **Requests** — The legacy term for tickets in Thena's older data model (the v2/v3 API still uses "requests").
- **Comments/Messages** — Internal and external thread messages on tickets, categorized as public replies (visible to the customer) or internal notes (visible only to agents).
- **Attachments** — Files uploaded within chat threads, typically stored via signed S3 URLs that expire after a short window.
- **Knowledge Base Articles** — Help center content organized by help center and category.
- **Tags, SLAs, Forms, Custom Fields** — Configuration objects tied to tickets and accounts.

### Entity relationships

Understanding which entities reference which is critical for extraction ordering:

```
Account (1) ──── (many) Contact
Account (1) ──── (many) Ticket
Ticket  (1) ──── (many) Comment
Ticket  (1) ──── (many) Attachment
Contact (1) ──── (many) Ticket  [as requester]
Agent   (1) ──── (many) Ticket  [as assignee]
```

Extracting tickets before accounts produces orphaned records in any target system. Always extract foundational entities first.

### What you cannot export

No API or UI tool will let you export Thena's workflow automations, routing rules, SLA policy definitions, AI agent configurations, integration mappings, or dashboard layouts. If you're migrating platforms, you'll rebuild these from scratch. A full breakdown of exportable vs. non-exportable data is [covered below](#what-you-cannot-export-from-thena).

## Method 1: UI Export — Ticket Dashboard (CSV)

Thena's platform offers a **CSV export directly from the ticket dashboard**. This is the fastest way to get structured ticket data out.

### How to export tickets from Thena's dashboard

1. Navigate to your team's ticket dashboard.
2. Apply any filters, date ranges, or saved views to scope the export.
3. Click the **download icon** in the top-right corner of the secondary header.
4. The system generates a CSV file named `{team-name}-{date}-{time}.csv`.

The export respects your current filters — if you've filtered to "Open" tickets only, you'll only get open tickets. There is no documented row limit for the UI export, but exports exceeding approximately 50,000 rows have been observed to time out in practice; scope your filters accordingly.

### What's included in the CSV export

| Category | Fields |
|---|---|
| **Core** | Ticket ID, Title, Description, Status, Priority, Type, Source |
| **Assignment** | Assigned Agent, Agent Email, Team |
| **Customer** | Account Name, Contact Name, Contact Email, Account Owner, Website, Industry |
| **SLA** | Created At, Updated At, Due Date, First Response SLA Status, Resolution SLA Status |
| **Metadata** | Is Escalated, Is Private, Story Points, Tags, CSAT Rating, CSAT Comment |
| **Custom Fields** | All team-configured custom fields (dropdown values shown as display names, multi-select separated by semicolons) |

The CSV uses **UTF-8 with BOM** encoding and standardized `YYYY-MM-DD HH:mm:ss` timestamps — good for Excel and Google Sheets compatibility.

> [!WARNING]
> **File upload fields are excluded** from CSV exports for security and size reasons. If your tickets contain file attachments in custom fields, those won't appear in the export. You'll need the API to retrieve attachment URLs.

### What's missing from the CSV

- **Full conversation threads** — You get the ticket description, not the back-and-forth messages
- **Attachments** — Neither inline message attachments nor file upload custom fields
- **Internal notes/comments** — Agent-to-agent discussion is not exported
- **Slack thread links** — Not included in the newer ticket CSV (though they were in the legacy request export)
- **Related ticket links** — Cross-ticket associations are lost
- **Relational structure** — Multi-contact accounts are flattened, making it difficult to reconstruct the account hierarchy in a target system

> [!WARNING]
> **The CSV export is not a migration tool.** It's strictly for tabular reporting.

## Method 2: UI Export — Legacy Request Export (XLSX/JSON)

You can export the entire set of requests from your Thena platform in two formats: XLSX or JSON. This is the older export path available at `app.thena.ai` under the Customer Support tab.

### Steps

1. Go to the **Requests** section within the **Customer Support** tab.
2. Select either **'Filtered requests in view'** or **'All requests in time frame'**.
3. Choose your preferred format from the dropdown menu: **JSON or XLSX**.
4. Click the download icon to generate and download the file.

### Fields in the legacy export

The legacy request export includes fields the newer CSV export does not:

- **Request ID**, Installation Name, Workspace Name
- **Slack Channel**, **Slack Thread Link** — direct links back to the source conversation
- **Sentiment**, **Urgency** — AI-generated classification
- **AI Tags** — auto-generated topic tags
- **External Ticket ID**, Ticket Connector Name, Ticket Link — references to Zendesk, Jira, or Linear tickets
- **CRM Data** — synced HubSpot or Salesforce fields
- **Merged Requests** — references to any merged request IDs

> [!TIP]
> If you need Slack thread links, AI-generated tags, or external ticketing references, use the **legacy request export** (XLSX/JSON) rather than the newer ticket CSV. The two exports carry different field sets.

## Method 3: Analytics Export

You can export data from any analytics tab by clicking on the download icon in the upper-right corner. The platform is divided into three main tabs: Requests, Accounts, and Workforce. These exports give you aggregate metrics — request volume over time, account-level SLA performance, agent workload distribution — not individual ticket records. Useful for reporting, not for migration.

## Method 4: Thena Platform API (Full-Fidelity Extraction)

For complete data extraction, the API is the only path. All APIs require authentication using an `x-api-key` header. API keys are tied to individual users and can be generated from **Dashboard → Organization Settings → Security and Access**.

### API key scoping

API keys inherit the permissions of the user who generated them. A key generated by a non-admin user will return only data that user has access to — typically tickets assigned to them or their team, not the full workspace. For a complete export, generate the API key from an **admin account** with full workspace visibility.

### API architecture

The API suite is organized into distinct sections:

| API Section | Base URL | Path Prefix | Covers |
|---|---|---|---|
| **Platform APIs** | `https://platform.thena.ai` | `/v1/tickets`, `/v1/accounts`, `/v1/contacts` | Tickets, accounts, teams, tags, forms, comments, SLAs |
| **App Platform APIs** | `https://platform.thena.ai` | `/v1/apps`, `/v1/webhooks` | App management, webhooks, distribution |
| **Workflows APIs** | `https://platform.thena.ai` | `/v1/workflows`, `/v1/events` | Workflow execution tracking and events |
| **Legacy API (deprecated)** | `https://bolt.thena.ai` | `/v3/requests` | Requests (cursor-paginated) |
| **Current Legacy API** | `https://bolt.thena.ai` | `/rest/v2/requests` | Requests by workspace |

### Rate limits

Standard tier: **60 requests per minute**, enforced simultaneously per user, per org, and per IP. Enterprise tier: custom limits based on plan.

**Critical for parallel extraction:** The rate limit applies to all three dimensions simultaneously. Running 4 parallel workers from the same server with the same API key does not give you 240 requests/minute — it gives you 60, shared across all workers. If you distribute across multiple IPs, the per-IP limit still caps each machine at 60. The binding constraint is the per-org limit.

Rate limit headers are returned on every response:

```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1722873600
X-RateLimit-IP: 203.0.113.42
X-RateLimit-UserID: usr_abc123
X-RateLimit-OrgID: org_xyz789
X-RateLimit-Path: /v1/tickets
```

If you exceed your rate limit, you will receive a `429 Too Many Requests` error. For production deployments, implement retry logic with exponential backoff using the `X-RateLimit-Reset` timestamp.

**429 response body example:**
```json
{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Retry after 1722873600.",
  "retry_after": 1722873600
}
```

At 60 requests/minute, extracting 10,000 tickets with their comments, contacts, and account data requires careful batching. Each ticket typically needs 2–3 follow-up API calls for comments, attachments, and account details. Expect **3–6 hours** for a mid-size dataset (10,000 tickets), significantly more for larger workspaces.

### Ticket API response schema

Understanding the actual ticket object structure is essential for planning your transformation step. A representative ticket object from the Platform API:

```json
{
  "id": "tkt_01H8XYZABC123",
  "title": "Cannot access dashboard after SSO change",
  "description": "User reports 403 error when logging in via Okta...",
  "status": "open",
  "priority": "high",
  "type": "bug",
  "source": "slack",
  "created_at": "2025-07-15T09:23:11Z",
  "updated_at": "2025-07-18T14:05:44Z",
  "closed_at": null,
  "due_date": "2025-07-17T09:23:11Z",
  "assignee": {
    "id": "usr_agent_789",
    "name": "Sarah Chen",
    "email": "sarah@yourcompany.com"
  },
  "requester": {
    "id": "usr_contact_456",
    "name": "Marcus Webb",
    "email": "marcus@customer.com",
    "slack_user_id": "U04ABCDEF12"
  },
  "account": {
    "id": "acc_org_321",
    "name": "Acme Corp",
    "slack_channel_id": "C05XYZGHIJK"
  },
  "tags": ["sso", "authentication", "enterprise"],
  "custom_fields": {
    "product_area": "Auth & Access",
    "affected_users": "12",
    "escalation_tier": "T2"
  },
  "sla": {
    "first_response_status": "met",
    "resolution_status": "breached",
    "first_response_due": "2025-07-15T11:23:11Z",
    "resolution_due": "2025-07-17T09:23:11Z"
  },
  "is_escalated": true,
  "is_private": false,
  "csat_rating": null,
  "csat_comment": null,
  "slack_thread_link": "https://yourworkspace.slack.com/archives/C05XYZGHIJK/p1721037791000200",
  "merged_into": null
}
```

Key implementation notes for this schema:
- `closed_at` is `null` for open tickets; always check before assuming presence
- `slack_user_id` on the requester is your cross-reference for Slack workspace export stitching
- `custom_fields` is a flat key-value map; keys are your team's configured field slugs
- `slack_thread_link` is a permalink, not message content — the thread text itself is not stored in Thena

### Comment object schema

Each comment retrieved via the comments endpoint:

```json
{
  "id": "cmt_01H9ABCDEF456",
  "ticket_id": "tkt_01H8XYZABC123",
  "body": "Hi Marcus, we've identified the issue. Your Okta integration needs to be reconfigured with the new SSO provider URL.",
  "body_format": "markdown",
  "type": "public_reply",
  "author": {
    "id": "usr_agent_789",
    "name": "Sarah Chen",
    "email": "sarah@yourcompany.com"
  },
  "created_at": "2025-07-15T10:45:22Z",
  "attachments": [
    {
      "id": "att_01HABC123XYZ",
      "filename": "sso-config-guide.pdf",
      "url": "https://thena-attachments.s3.amazonaws.com/signed/...",
      "url_expires_at": "2025-07-15T11:45:22Z",
      "size_bytes": 245680,
      "content_type": "application/pdf"
    }
  ]
}
```

`type` values: `public_reply` (visible to customer) or `internal_note` (agents only).

### Pagination parameters

All list endpoints share the same pagination model:

| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
| `page` | integer | 1 | — | Page number (1-indexed) |
| `per_page` | integer | 25 | **100** | Records per page |
| `sort_by` | string | `created_at` | — | Field to sort by |
| `sort_order` | string | `desc` | — | `asc` or `desc` |

Response envelope:

```json
{
  "data": [...],
  "meta": {
    "total": 47832,
    "page": 1,
    "per_page": 100,
    "total_pages": 479
  }
}
```

Detect the last page when `page > total_pages` or when `data` is an empty array. The `meta.total` field is your ground truth for count parity validation.

The legacy v3 endpoint uses **cursor-based pagination** instead — pass `data.next_cursor` as a query parameter to fetch subsequent pages. Stop when `next_cursor` is `null` or absent.

### Deleted and archived record behavior

By default, the `/v1/tickets` endpoint returns only active (non-deleted) tickets. To retrieve soft-deleted records, append `include_deleted=true` to your query. Archived tickets are included in standard results by default but can be filtered using `status=archived`. Always include `include_deleted=true` in migration exports to avoid silently missing records.

### Key extraction endpoints

```bash
# Get all tickets (max 100 per page)
curl -X GET "https://platform.thena.ai/v1/tickets?per_page=100&page=1&include_deleted=true" \
  -H "x-api-key: YOUR_API_KEY"

# Get comments for a specific ticket
curl -X GET "https://platform.thena.ai/v1/tickets/tkt_01H8XYZABC123/comments?per_page=100" \
  -H "x-api-key: YOUR_API_KEY"

# Get all accounts
curl -X GET "https://platform.thena.ai/v1/accounts?per_page=100&page=1" \
  -H "x-api-key: YOUR_API_KEY"

# Legacy: Get all requests (deprecated but still functional)
curl -X GET "https://bolt.thena.ai/v3/requests?limit=100" \
  -H "x-api-key: YOUR_API_KEY"
```

### Search API for bulk extraction

The Search API provides powerful, flexible search capabilities across your core business data — tickets, accounts, and comments — using a unified, Typesense-powered interface.

The Search API is often better than list endpoints for large exports because it supports:

- **Streaming**: Thena's search API supports streaming large result sets for improved performance. Add `streaming=true` as a query parameter. When enabled, the API streams each page of results as a separate JSON object rather than buffering the full response.
- **Filtered extraction**: Combine multiple filters in `filter_by` using `&&` for precise targeting (e.g., `status:=open&&priority:=high`). Supported operators: `:=` (equals), `:!=` (not equals), `:>` (greater than), `:<` (less than), `:>=`, `:<=`.
- **Field selection**: Use `include_fields` to limit results to only the fields you need — this improves performance and reduces payload size.

```bash
# Search all open high-priority tickets with streaming
curl -X GET "https://platform.thena.ai/v1/search/tickets?q=*&filter_by=status:=open&&priority:=high&streaming=true&per_page=100&include_fields=id,title,status,priority,created_at" \
  -H "x-api-key: YOUR_API_KEY"
```

> [!TIP]
> For migration-scale extraction, prefer the **Search API with streaming** over individual list endpoints. It reduces the total number of API calls and handles large result sets more efficiently. The `per_page` maximum is 100 for the search endpoint, same as list endpoints.

### Step-by-step API extraction order

When building your extraction pipeline, extract data in a specific order to maintain relational integrity.

**Step 1: Extract Accounts and Contacts.** Start with foundational entities. You can't correctly assign a ticket to a user or account if those records don't exist in your local database yet. Query the Accounts endpoint to pull all account data, including associated Slack channel mappings. Then query Contacts. Pay close attention to identity fields — a contact in Thena will have a chat platform ID (`slack_user_id` or `teams_user_id`) alongside their email. Save both. When migrating to a system like Zendesk or Intercom, you'll use the email as the primary key.

**Step 2: Extract Ticket Metadata.** Paginate through the Tickets endpoint (or use the Search API with streaming), using `per_page=100` and `include_deleted=true`. For each ticket, extract: `ticket_id`, `title`/`subject`, `description`, `status`, `priority`, `assignee_id`, `requester_id`, `organization_id`, timestamps (`created_at`, `updated_at`, `closed_at`), custom fields, and tags. Write to a local database (PostgreSQL works well) or structured JSONL files. Do not hold the full dataset in memory.

**Step 3: Extract Conversation Threads.** This is the most API-intensive step. For every `ticket_id` extracted in Step 2, make a separate API call to retrieve comments and messages. If you have 50,000 tickets, you'll make at least 50,000 API calls just for messages. At 60 requests per minute, and factoring in that each request takes ~200ms plus the sleep interval, this step alone takes **nearly 14 hours**. Plan accordingly — implement checkpointing so a mid-run failure doesn't require starting over. For each message, capture the `author_id`, `body`, `body_format`, timestamp, and `type` (public_reply vs. internal_note).

**Step 4: Download Attachments.**

> [!CAUTION]
> **Signed URLs expire.** The `url` field on attachment objects is a signed S3 URL. The `url_expires_at` field in the response tells you the exact expiry time — in practice this is typically within 1 hour of the API response. Your extraction script must download the binary file immediately upon parsing the message payload, within the same processing loop — not in a separate pass hours later.

Save files to your own cloud storage (such as an S3 bucket) and rewrite the URL in your extracted data to point to your permanent location. Validate downloaded files by checking that `size_bytes` matches the actual downloaded file size and that the file is not a 0-byte or HTML error response disguised as a successful download.

**Step 5: Transform and Validate.** Covered in detail in the sections below.

### Extraction script: practical example

Here's a Python script that paginates through tickets using the Search API with error handling, checkpointing, and rate limit compliance:

```python
import requests
import time
import json
import os
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")

API_KEY = os.environ.get("THENA_API_KEY")  # Never hardcode credentials
BASE_URL = "https://platform.thena.ai/v1"
HEADERS = {"x-api-key": API_KEY}
CHECKPOINT_FILE = "thena_export_checkpoint.json"

def load_checkpoint():
    if os.path.exists(CHECKPOINT_FILE):
        with open(CHECKPOINT_FILE) as f:
            return json.load(f)
    return {"last_page": 0, "total_exported": 0}

def save_checkpoint(page, total):
    with open(CHECKPOINT_FILE, "w") as f:
        json.dump({"last_page": page, "total_exported": total}, f)

def extract_tickets(output_file="thena_tickets.jsonl"):
    checkpoint = load_checkpoint()
    start_page = checkpoint["last_page"] + 1
    total = checkpoint["total_exported"]

    with open(output_file, "a") as f:  # append mode to resume safely
        page = start_page
        while True:
            resp = requests.get(
                f"{BASE_URL}/search/tickets",
                headers=HEADERS,
                params={
                    "q": "*",
                    "per_page": 100,
                    "page": page,
                    "include_deleted": "true"
                },
                timeout=30
            )

            if resp.status_code == 429:
                reset_ts = int(resp.headers.get("X-RateLimit-Reset", time.time() + 60))
                wait = max(reset_ts - time.time(), 1)
                logging.warning(f"Rate limited on page {page}. Waiting {wait:.0f}s...")
                time.sleep(wait)
                continue

            if resp.status_code >= 500:
                logging.error(f"Server error {resp.status_code} on page {page}. Retrying in 30s...")
                time.sleep(30)
                continue

            resp.raise_for_status()
            data = resp.json()
            hits = data.get("hits", data.get("data", []))
            meta = data.get("meta", {})
            total_pages = meta.get("total_pages", 1)

            if not hits:
                logging.info(f"No more results at page {page}. Export complete.")
                break

            for hit in hits:
                f.write(json.dumps(hit) + "\n")
                total += 1

            save_checkpoint(page, total)
            logging.info(f"Page {page}/{total_pages} — {total} tickets exported so far")

            if page >= total_pages:
                break

            page += 1
            time.sleep(1.1)  # 60 req/min = 1 per second; 1.1s adds safety margin

    # Clean up checkpoint on successful completion
    if os.path.exists(CHECKPOINT_FILE):
        os.remove(CHECKPOINT_FILE)
    logging.info(f"Export complete: {total} tickets written to {output_file}")

extract_tickets()
```

> [!NOTE]
> This script outputs JSONL (one JSON object per line), which is the best format for downstream ETL pipelines. The checkpoint mechanism allows resuming from the last successful page after a failure, which is critical for large exports running over many hours.

## Method 5: Webhooks for Real-Time Export

Thena's webhook functionality provides a way to stay informed about specific events in real-time, such as request creation or updates. By integrating these webhooks into systems such as data lakes, CRMs, and other applications, you can instantly process and act upon these events.

Webhooks are configured under **Configuration → Integration → Webhooks** in the Thena dashboard. Supported events:

- **Request Created** — fires when a new request/ticket is generated
- **Request Updated** — fires on any status, assignment, or field change

Thena follows a retry mechanism on delivery failure: 1st retry ~10 seconds after initial attempt, 2nd retry ~20 seconds after 1st, 3rd retry ~30 seconds after 2nd, 4th retry ~50 seconds after 3rd, 5th retry ~80 seconds after 4th. After 5 failed retries, the event is dropped — implement an idempotency key on your webhook receiver and log all incoming payloads to a durable store before processing.

Webhooks won't help with historical data extraction — they only capture events going forward. But for continuous sync to a data warehouse or target platform, they're the right tool for ongoing data flow after an initial API backfill.

## What You Cannot Export from Thena

Several categories of Thena data have **no documented export or API path**:

| Data Type | Exportable? | Notes |
|---|---|---|
| Tickets/Requests | ✅ Yes | UI + API |
| Accounts | ✅ Yes | API only |
| Contacts | ✅ Yes | API only |
| Comments/Threads | ✅ Yes | API only (search endpoint) |
| Tags | ✅ Yes | API only |
| Knowledge Base Articles | ⚠️ Partial | Help Center endpoints exist at `/v1/help-centers` and `/v1/articles` but return individual articles only — no bulk export or category-tree export is documented |
| SLA Definitions | ❌ No | SLA compliance data exports per-ticket; SLA policy logic (hours, escalation rules) has no documented export endpoint |
| Workflow Configurations | ❌ No | Workflow APIs handle execution events, not config export |
| AI Agent Settings | ❌ No | No API coverage |
| Routing Rules | ❌ No | No API coverage |
| Automations | ❌ No | No API coverage |
| Attachments (binary) | ⚠️ Partial | Signed URLs available via ticket/comment API; binary download requires separate handling; URLs expire (check `url_expires_at` field) |
| Integration Mappings | ❌ No | Zendesk, Jira, Linear field mappings are not exportable |

**Knowledge base detail:** The `/v1/help-centers` endpoint lists your help centers and categories. Individual articles can be fetched at `/v1/articles/{id}`. There is no `/v1/articles?per_page=100` bulk list endpoint with full content in the response — you must enumerate category IDs first, then fetch articles per category, then fetch each article individually. A full KB export requires 3 levels of nested API calls and may be impractical at scale.

> [!WARNING]
> If you're on Thena's Standard plan (~$79/user/month), confirm your API access scope before starting a migration project. Some endpoints return empty results rather than 403 errors when your plan doesn't include access — silent failures that are difficult to detect. Enterprise customers (~$119/user/month) typically have expanded endpoints and custom rate limits.

## The Hard Part: Transforming Slack and Teams Data

Extracting the data is only half the battle. If you're migrating from Thena to a traditional helpdesk, you'll face data formatting issues rooted in the chat-native origins of the data.

### Field mapping to common target systems

This is the most operationally valuable step for migration planning. The table below maps Thena ticket fields to their equivalents in the three most common migration targets:

| Thena Field | Zendesk Equivalent | Intercom Equivalent | Freshdesk Equivalent |
|---|---|---|---|
| `ticket.id` | `ticket.id` (new ID generated) | `conversation.id` | `ticket.id` |
| `ticket.title` | `ticket.subject` | `conversation.title` | `ticket.subject` |
| `ticket.description` | `ticket.description` / first comment | `conversation.source.body` | `ticket.description_html` |
| `ticket.status` (open/closed/pending) | `ticket.status` (open/solved/pending) | `conversation.state` (open/closed/snoozed) | `ticket.status` (2=open, 5=closed) |
| `ticket.priority` (low/medium/high/urgent) | `ticket.priority` (low/normal/high/urgent) | — (no native priority) | `ticket.priority` (1–4) |
| `requester.email` | `ticket.requester_id` (looked up by email) | `contact.email` | `ticket.requester_id` |
| `account.name` | `organization.name` | `company.name` | `ticket.company_id` |
| `comment.type = public_reply` | `ticket_comment` (public: true) | `conversation_part` (type: comment) | `conversation_reply` |
| `comment.type = internal_note` | `ticket_comment` (public: false) | `conversation_part` (type: note) | `ticket_note` |
| `ticket.tags` | `ticket.tags` (array) | `conversation.tags` | `ticket.tags` |
| `ticket.csat_rating` | `ticket.satisfaction_rating` | — | `ticket.fr_escalated` (no direct CSAT) |
| `ticket.slack_thread_link` | Custom field | Custom attribute | Custom field |
| `ticket.custom_fields` | `ticket.custom_fields` (by field ID) | `conversation.custom_attributes` | `ticket.custom_fields` |

**Status mapping requires explicit logic** — Thena's "pending" maps to Zendesk's "pending" semantically, but Freshdesk uses integer codes. Build a lookup table, not a direct string copy.

**Priority mapping:** Thena uses string labels; Freshdesk uses integers (1=low, 2=medium, 3=high, 4=urgent). Intercom has no native priority field — you must map to a custom attribute or tag.

### Converting Slack markdown to HTML

Slack uses a proprietary flavor of Markdown — `*bold*` instead of `**bold**`, `<@U123456>` for user mentions, `<#C123456|channel-name>` for channel references. Most target helpdesks (Zendesk, Freshdesk, HubSpot) expect HTML.

If you push raw Thena message bodies into a new system, they'll look broken. The critical transformations are:

| Slack Format | Target HTML |
|---|---|
| `*bold text*` | `<strong>bold text</strong>` |
| `_italic text_` | `<em>italic text</em>` |
| `~strikethrough~` | `<del>strikethrough</del>` |
| `` `code` `` | `<code>code</code>` |
| ` ```block``` ` | `<pre><code>block</code></pre>` |
| `<@U04ABCDEF12>` | `<strong>@Jane Doe</strong>` (requires user lookup) |
| `<#C05XYZGHIJK\|support>` | `#support` |
| `<https://example.com\|link text>` | `<a href="https://example.com">link text</a>` |
| `\n` (line break) | `<br>` |

The `<@U123456>` user mention lookup is the most failure-prone step: you need to cross-reference the Slack user ID against the contact records extracted in Step 1. If a mention references a Slack user who isn't a Thena contact (e.g., an internal employee who never raised a ticket), the lookup will fail. Build a fallback: if no Thena contact matches the Slack user ID, preserve the raw mention tag in a comment like `<!-- unresolved mention: U04ABCDEF12 -->` rather than silently dropping it.

### Handling thread context

In Slack, conversations are highly nonlinear. Multiple people reply in a thread simultaneously. When flattening this into a traditional ticket's chronological view, context is often lost.

Thena stores permalinks to Slack threads, not full message text. If your team relied heavily on Slack-native support and you need complete conversation history, you must supplement your Thena export with a [Slack workspace export](https://slack.com/help/articles/201658943) and stitch the data together using Slack thread timestamps and channel IDs. The join key is: Thena's `slack_thread_link` contains the channel ID and thread timestamp (`/archives/{channel_id}/p{timestamp}`), which maps to Slack export's `channel` and `ts` fields.

To mitigate context loss during transformation, prepend the author's name and timestamp to the top of every message body during the transformation phase — this ensures clarity in the target system regardless of how that system renders conversation history.

## QA and Validation

Never assume an API extraction ran perfectly. Network blips, rate limit spikes, and server errors produce silent data gaps.

After your extraction completes, run these validation checks:

1. **Count parity:** Query `GET /v1/tickets?per_page=1` and check `meta.total` in the response. Does this number match the total records in your local database?
2. **Orphaned records:** Are there comments or attachments tied to a `ticket_id` that doesn't exist in your database? This indicates a pagination gap.
3. **Null requester handling:** Some tickets (system-generated or AI-triggered) may have a null `requester` field. Your schema must accommodate this without crashing the import.
4. **Attachment integrity:** For a random sample of 100 downloaded attachments, verify that the file's actual byte size matches `size_bytes` from the API response. A mismatch indicates a failed download that produced a partial or error-page file.
5. **Status completeness:** Confirm your extracted dataset includes tickets in all statuses (open, closed, pending, archived). If archived or deleted tickets are missing, you likely forgot `include_deleted=true`.

## Data Portability Assessment

**What ports well**: Ticket metadata, customer/account records, contact information, CSAT scores, tags, and SLA compliance data. These map cleanly to most target helpdesks (Zendesk, Freshdesk, Intercom, HubSpot Service Hub) using the field mapping table above.

**What doesn't port well**: Slack thread context. Thena's deep Slack integration means a significant amount of conversation history lives in Slack threads, referenced by permalink in Thena but not stored as first-class message data in the export. This is the single largest fidelity loss in any Thena migration.

**What requires manual rebuild**: Workflow automations, routing rules, SLA policy definitions, AI agent configurations, and integration mappings. These are platform-specific configurations with no portable format. Budget 1–3 weeks of configuration time to recreate these in a target platform, depending on workflow complexity.

## Planning Your Thena Export: Decision Framework

| Scenario | Best Method | Expected Time | Limitations |
|---|---|---|---|
| Quick audit / spot check | UI CSV export | Minutes | No threads, no attachments |
| Compliance backup of all requests | Legacy XLSX/JSON export | Minutes | No binary attachments |
| Full migration to another helpdesk | Platform API + Search API | 4–24 hours depending on volume | See extraction order above |
| Migration with >50K tickets | API with checkpointing + parallel account/contact pre-fetch | 24–72 hours | Rate limit is binding; plan for multi-day run |
| Ongoing sync to data warehouse | Webhooks + initial API backfill | Setup: hours; ongoing: real-time | 5-retry limit; implement durable event log |
| Analytics-only reporting extract | Analytics tab export | Minutes | Aggregate only, not row-level |
| Knowledge base migration | Manual per-article fetch via `/v1/articles/{id}` | Hours to days | No bulk endpoint; 3 levels of nested calls |

For migrations involving more than 5,000 tickets, a CSV-based approach will fall short. See our guide on [using CSVs for SaaS data migrations](https://clonepartner.com/blog/blog/csv-saas-data-migration/) for a detailed breakdown of where flat-file exports break down.

## Common Failure Modes and How to Handle Them

The following failure patterns appear consistently in production Thena extraction runs:

| Failure Mode | Symptom | Resolution |
|---|---|---|
| Silent API scope gap | Ticket count from API < count shown in Thena UI | Verify API key is from an admin account; re-extract with admin key |
| Attachment URL expiry | Downloaded file is an HTML error page, not the original file | Check `url_expires_at` before downloading; download within the same processing loop as the API call |
| Slack user ID with no Thena contact | `<@U04ABCDEF12>` remains unresolved in transformed messages | Preserve raw mention in HTML comment; build separate Slack API lookup if needed |
| Null requester on system tickets | Import fails on required requester field | Map null requester to a placeholder "System" contact in target platform |
| Soft-deleted ticket gap | Exported ticket count < actual ticket count | Add `include_deleted=true` to all list and search queries |
| Pagination termination failure | Script hangs or re-fetches page 1 indefinitely | Terminate when `page >= meta.total_pages` OR when `hits` array is empty — check both conditions |
| Knowledge base category not found | Article fetch 404s after listing category IDs | KB categories may be nested; enumerate recursively before fetching articles |

For related planning, see our [Thena to Podium Migration Guide](https://clonepartner.com/blog/blog/thena-to-podium-migration-the-ctos-technical-guide/) and [Thena to SurveySparrow Migration Guide](https://clonepartner.com/blog/blog/thena-to-surveysparrow-ticket-management-migration-guide/).

> Need to export data from Thena for a migration or integration project? Book a 30-minute call and we'll map your extraction requirements to the right method. For workspaces with over 100,000 tickets, the extraction pipeline requires queue management, parallel account pre-fetching, and multi-day scheduling — key constraints we can help you scope before you start.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can you export all data from Thena?

No. Thena's UI exports cover ticket metadata and customer info as CSV or XLSX/JSON. Full conversation threads, attachments, knowledge base articles, workflow configurations, routing rules, and AI agent settings are not available through any single export. The REST API covers tickets, accounts, contacts, comments, and tags, but several configuration objects have no documented export path.

### What is Thena's API rate limit?

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

### Can I export full conversation history from the Thena UI?

No. The native UI exports only provide ticket metadata for reporting purposes. Full conversation threads, internal notes, and agent replies must be extracted via the REST API's comments and search endpoints.

### How do I handle attachments when exporting from Thena?

Attachments are returned via the API as signed, temporary S3 URLs that expire within 15 to 60 minutes. Your extraction script must download the binary file immediately before the URL expires and store it on your own cloud storage.

### Does Thena have a bulk export API endpoint?

Not a single dedicated endpoint, but the Typesense-powered Search API at platform.thena.ai supports streaming large result sets across tickets, accounts, and comments. Add streaming=true to your request to stream paginated results as separate JSON objects. This is the closest thing to a bulk export endpoint.
