Skip to content

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

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.

Nachi Nachi · · 21 min read
How to Export Data from Thena: Methods, API Limits & Formats
TALK TO AN ENGINEER

Planning a migration?

Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.

Schedule a free call
  • 1,500+ migrations completed
  • Zero downtime guaranteed
  • Transparent, fixed pricing
  • Project success responsibility
  • Post-migration support included

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.

Info

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 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.

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:

{
  "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:

{
  "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:

{
  "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:

{
  "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

# 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.
# 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.

Danger

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:

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()
Info

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 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 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 and Thena to SurveySparrow Migration Guide.

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.

More from our Blog