Skip to content

Thena to Zoho Desk Migration: A Technical Guide

Technical guide for migrating from Thena to Zoho Desk. Covers API endpoints, credit-based rate limits, Slack identity resolution, mrkdwn conversion, and cutover planning.

Abdul Aleem Abdul Aleem · · 18 min read
Thena to Zoho Desk Migration: A Technical Guide
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

Thena to Zoho Desk Migration: A Technical Guide

Info

TL;DR: Thena is a Slack-native, AI-first B2B ticketing platform. Zoho Desk is a department-centric, traditional helpdesk with a credit-based API system. No native migration path exists. Thena's Account → Contact → Request model must be transformed into Zoho Desk's Department → Account → Contact → Ticket → Thread hierarchy using a custom ETL pipeline. Key challenges: Slack user identity resolution, mrkdwn-to-HTML conversion, Zoho Desk's credit-based API limits (not simple rate limits), a 20 MB attachment ceiling, and the department constraint — every Zoho ticket must belong to a department Thena does not have. Budget 72–120 engineer-hours for a mid-size dataset (5K–30K requests).

Last reviewed: August 2026 against Thena Platform API and Zoho Desk API v1.

Migrating from Thena to Zoho Desk means leaving a Slack-first, AI-driven B2B support tool for a traditional multi-channel helpdesk organized around departments, tickets, and email-based threads. The platforms serve overlapping use cases with fundamentally different data architectures.

There is no built-in connector, no third-party tool supporting Thena as a source, and Zoho Desk's CSV import omits conversation threading. Just as with a Thena to Kustomer migration, this is a custom data transformation project — treat it as one from the start.

This guide covers API endpoints, data-model mapping, rate-limit math, attachment handling, and cutover logic. If you are still evaluating your data export options, start with our guide to exporting data from Thena.

What This Migration Actually Involves

Thena organizes support around Requests — conversations captured from Slack, email, MS Teams, web chat, and Discord. Each Request holds threaded messages, AI-generated metadata (title, summary, tags, sentiment), and is linked to an Account and Contact. User identity is often tied to Slack User IDs. Thena also documents both Requests and Tickets — some workspaces have request objects that never became formal tickets. Decide before the first export whether to migrate these as tickets or skip them. Thena's data model: Account → Contact → Request → Messages.

Zoho Desk organizes support around Tickets within Departments. Each ticket belongs to a department, links to a Contact (and optionally an Account), and contains Threads (conversation entries visible to the customer) and Comments (internal notes). In Zoho, a ticket comment is not the same thing as a channel thread — that separation is the first design constraint to internalize. Zoho Desk's data model: Organization → Department → Ticket → Threads/Comments, with Contacts and Accounts as cross-department entities.

The core transformation:

Thena Entity Zoho Desk Entity Notes
Account Account Direct map. accountName required.
Contact Contact lastName or email required.
Request Ticket Department assignment required — Thena has no equivalent.
Request Messages (customer) Thread (incoming) Slack mrkdwn → HTML conversion needed.
Request Messages (agent) Thread (outgoing) fromEmailAddress required for email threads.
Internal Notes Comment isPublic: false for private comments.
AI Tags/Summary Custom Fields (cf_*) Create custom fields in Zoho Desk first.
Attachments Uploads → Ticket/Thread attachments 20 MB per-file limit. Two-step upload flow.

The Department Constraint

In Zoho Desk, every ticket must belong to a Department — departmentId is a required field on ticket creation. As we've noted in our Zoho Desk to Freshdesk migration guide, departments are top-level organizational units that control ticket routing and agent access. Thena does not use departments natively. You need a routing table in your migration middleware before the first ticket is created.

The most common approach: map Thena Slack Channels (or Thena teams) to specific Zoho Desk Departments. If Thena teams do not map cleanly to Zoho departments, decide early whether a Thena team becomes a Desk department, a team within a department, or a reporting-only field. Making this choice after tickets are loaded is where rework starts.

Why Teams Move from Thena to Zoho Desk

The most common triggers:

  • Pricing pressure: Thena's paid plans start above $20/user/month and scale with seat count. Zoho Desk starts at $14/agent/month (Standard tier) with broader feature coverage at lower price points. Check Thena's current pricing page and Zoho Desk's pricing page for current rates — both vendors adjust pricing periodically.
  • Multi-channel requirements: Teams outgrowing Slack-only support need email, phone, social, and web form channels that Zoho Desk handles natively.
  • Zoho ecosystem consolidation: Organizations already using Zoho CRM, Zoho Analytics, or Zoho Projects want a unified platform with native data sharing.
  • Workflow automation: Zoho Desk's Blueprint (process enforcement), SLA escalation rules, and round-robin assignment automation are more mature than Thena's workflow layer.

Source-Side Extraction: Thena Platform API

Thena's Platform API is the only reliable extraction path for migration-quality data. The UI export (XLSX/JSON) gives you request metadata but strips message threading and attachment references. (docs.thena.ai)

Authentication and Rate Limits

All Thena API requests require an x-api-key header. Generate your key from Organization Settings → Security and Access.

Rate limits:

  • Standard tier: 60 requests per minute per user, org, and IP
  • Enterprise tier: Custom limits based on your plan

Monitor X-RateLimit-Remaining in response headers and back off when it approaches zero. At 60 req/min, extracting 10,000 requests with individual detail calls takes roughly 3–4 hours.

Warning

Plan-gated access: Thena's comments API is documented as available only for Standard and Enterprise tier organizations. Confirm your plan gives you comment API access before designing the pipeline. (docs.thena.ai)

Key Extraction Endpoints

# List all requests (paginated)
GET https://platform.thena.ai/v1/requests
  -H "x-api-key: YOUR_API_KEY"
 
# Get single request with messages
GET https://platform.thena.ai/v1/requests/{requestId}
  -H "x-api-key: YOUR_API_KEY"
 
# List accounts
GET https://platform.thena.ai/v1/accounts
  -H "x-api-key: YOUR_API_KEY"
 
# List contacts/users
GET https://platform.thena.ai/v1/users
  -H "x-api-key: YOUR_API_KEY"

Thena also exposes a Typesense-powered search API useful for scoping incremental exports and building delta passes (docs.thena.ai):

{
  "q": "*",
  "query_by": "title,description",
  "filter_by": "createdAt:>2026-01-01&&statusName:!=resolved",
  "sort_by": "createdAt:asc"
}

Use this same search API for delta extraction during cutover. To extract only records modified after your initial snapshot, filter by updatedAt rather than createdAt:

{
  "q": "*",
  "query_by": "title,description",
  "filter_by": "updatedAt:>2026-08-01T00:00:00Z",
  "sort_by": "updatedAt:asc"
}

Replace the timestamp with your initial extraction cutoff. Run this query once before freezing Thena to capture any requests updated during the historical load.

Extraction Order

  1. Accounts — extract all, build thena_account_id → zoho_account_id mapping
  2. Contacts/Users — extract all, resolve Slack identities to emails, build mapping
  3. Requests — extract with full message history and attachment URLs
  4. Attachments — download binary files from Thena/Slack CDN URLs to local staging

Slack file URLs are authenticated and time-limited. Download them during extraction, not at load time.

Thena API Response Field Names

The field names below are drawn from Thena Platform API v1 response payloads. Verify against your own API responses before hardcoding — Thena's schema is not yet published as a versioned OAS document.

Key fields observed in request objects:

  • id — Thena request UUID
  • title — AI-generated or manually set title
  • original_message_text — raw Slack mrkdwn content of the originating message
  • original_message_user_slack_name — Slack handle of the originating user
  • original_message_user_slack_profile_real_name — display name from Slack profile
  • original_message_source — source channel type (SLACK, EMAIL, etc.)
  • createdAt / updatedAt — ISO 8601 timestamps
  • statusName — current request status string
  • aiSummary — AI-generated summary text
  • tags — array of AI-assigned tag strings
Warning

Field name verification: Inspect actual API responses from your Thena workspace rather than relying solely on this list. Thena does not publish a versioned field schema, and field names may vary by plan tier or workspace configuration.

Destination-Side Constraints: Zoho Desk API

Authentication

Zoho Desk uses OAuth 2.0. Every request needs two headers:

Authorization: Zoho-oauthtoken {access_token}
orgId: {your_org_id}

Access tokens expire periodically — store your refresh token and regenerate as needed.

Determining your data center region: Your org's data center region is encoded in your Zoho account URL. If your Zoho portal loads at yourdomain.zoho.eu, your data center is EU. The pattern:

Portal URL pattern API base URL
*.zoho.com https://desk.zoho.com/api/v1/
*.zoho.eu https://desk.zoho.eu/api/v1/
*.zoho.in https://desk.zoho.in/api/v1/
*.zoho.com.au https://desk.zoho.com.au/api/v1/
*.zoho.jp https://desk.zoho.jp/api/v1/

Using the wrong regional base URL returns authentication errors that resemble token failures. Check your portal URL before writing a single API call. (desk.zoho.com)

Credit-Based Rate Limiting

Zoho Desk does not use simple requests-per-minute rate limiting. It uses a credit-based system where each API call costs credits, and your daily credit pool depends on your edition and user count.

Edition Base Credits + Per Agent Concurrency Limit
Free/Trial 5,000 0 5
Standard 50,000 250 10
Professional 75,000 500 15
Enterprise/Zoho One 100,000 1,000 25

Credit costs by operation:

  • Create a ticket: 1 credit
  • Create a thread/comment: 1 credit
  • Upload an attachment: 1 credit
  • List tickets (0–2,000 records): 3 credits
  • Search operations: 3 credits

Credits reset every 24 hours based on your data center's timezone. Once exhausted, the API returns 429 Too Many Requests with a Retry-After header.

Concurrency limits restrict the number of API calls active simultaneously across your entire org — not per token or per app. Your migration job competes with existing CRM syncs, marketplace apps, and custom integrations. Exceeding this limit triggers EXCEEDED_MAXIMUM_ALLOWED_CONCURRENT_REQUESTS errors.

Tip

Migration math example: Enterprise edition with 50 agents = 100,000 + (50 × 1,000) = 150,000 credits/day. Migrating 10,000 tickets with 3 messages each requires ~10,000 ticket creates + ~30,000 thread creates + contact/account creates — roughly 50K–60K credits. Completable in one day on Enterprise, but would take 2–3 days on Standard with 20 agents (~55K credits/day). For larger datasets, contact Zoho Support to request a temporary API limit increase during the migration window.

Pre-Creating Custom Fields in Zoho Desk

Before loading any ticket data, create the custom fields that will hold Thena-specific metadata. Without these fields, the cf_* values in your ticket payloads will be silently dropped.

Via Zoho Desk Setup UI: Navigate to Setup → Customization → Layouts and Fields → Ticket Fields. Use "Add Field" to create each custom field, then note the system-generated field API name (formatted as cf_fieldname).

Via API: Custom fields can also be created programmatically, but the field management API is under the admin/setup scope. Confirm your OAuth scope includes Desk.settings.UPDATE before attempting API-based field creation.

Fields to create before migration:

Field Label API Name Type Purpose
Thena Request ID cf_thena_request_id Text (255) Deduplication key
AI Summary cf_ai_summary Multi-line Text Thena AI-generated summary
Slack Handle cf_slack_handle Text (255) Original Slack username
Thena Source cf_thena_source Text (255) Source channel (SLACK, EMAIL, etc.)

Zoho Desk caps custom fields per edition: 50 (Standard), 150 (Professional), 230 (Enterprise). If you are on Standard and already have custom fields, audit your field count before migration.

Key Load Endpoints

# Create account
POST https://desk.zoho.com/api/v1/accounts
  -H "Authorization: Zoho-oauthtoken {token}"
  -H "orgId: {orgId}"
  -d '{"accountName": "Acme Corp", "email": "support@acme.com"}'
 
# Create contact
POST https://desk.zoho.com/api/v1/contacts
  -d '{"lastName": "Smith", "email": "smith@acme.com", "accountId": "..."}'
 
# Create ticket
POST https://desk.zoho.com/api/v1/tickets
  -d '{"subject": "...", "departmentId": "...", "contactId": "...", "description": "...", "status": "Open"}'
 
# Add thread to ticket
POST https://desk.zoho.com/api/v1/tickets/{ticketId}/sendReply
  -d '{"channel": "EMAIL", "content": "...", "fromEmailAddress": "...", "to": "...", "contentType": "html"}'
 
# Upload attachment (two-step: upload first, then reference ID)
POST https://desk.zoho.com/api/v1/uploads
  (multipart/form-data with file returns an id to reference in thread/comment)

A sample ticket creation payload with custom fields:

{
  "departmentId": "7189000000012345",
  "contactId": "7189000000098765",
  "subject": "API Authentication Failure",
  "description": "User reported a 401 error on the /v2/users endpoint.",
  "status": "Closed",
  "createdTime": "2024-03-15T14:30:00.000Z",
  "cf": {
    "cf_thena_request_id": "req_987654321",
    "cf_ai_summary": "User experienced auth issues due to expired token."
  }
}

Field Mapping: Thena → Zoho Desk

Ticket Fields

Thena Request Field Zoho Desk Ticket Field Transform
title / AI-generated title subject Direct map. Max 255 chars.
original_message_text description Slack mrkdwn → HTML. Max 65,535 chars.
id Custom Field: cf_thena_request_id Essential for deduplication and delta syncs.
Status (Open/Closed/etc.) status Map to layout-specific picklist values (see crosswalk below).
Priority priority Map to High/Medium/Low or custom values (see crosswalk below).
original_message_source (AI, MANUAL) cf_thena_source Custom field.
aiSummary cf_ai_summary Custom text field.
tags Tags API Use POST /tickets/{id}/associateTag.
original_message_source channel Map SLACK→Web, EMAIL→Email, etc.
createdAt createdTime ISO 8601 format. Zoho Desk does accept createdTime on ticket creation and stores it as the ticket's creation timestamp. Confirmed against API v1 — test in your environment with a 5-ticket proof of concept before relying on this for historical fidelity.
Assignee assigneeId Map Thena user → Zoho Desk agent ID.

Status and Priority Crosswalk

Thena and Zoho Desk both use configurable status and priority values. The defaults typically align as follows — but pull your live Zoho Desk field values from Setup → Customization → Layouts and Fields before hardcoding this crosswalk, because layout-specific picklists override defaults.

Status mapping (Thena default → Zoho Desk default):

Thena Status Zoho Desk Status
Open Open
In Progress In Progress
Pending On Hold
Resolved Resolved
Closed Closed

Priority mapping (Thena default → Zoho Desk default):

Thena Priority Zoho Desk Priority
Urgent High
High High
Medium Medium
Low Low
None / unset (omit field; Zoho assigns default)

Store this crosswalk in a config file, not hardcoded logic. Both systems allow admins to add or rename values — a config-driven approach means reruns stay deterministic if the mapping changes.

Contact Fields

Thena Field Zoho Desk Field Notes
original_message_user_slack_profile_real_name firstName + lastName Split on first space. If single token, use as lastName.
Slack email (resolved via users.info) email Primary join key. Required for reliable ticket association.
original_message_user_slack_name cf_slack_handle Preserve as custom field for auditability.
Account association accountId Map via account ID lookup table.

Thread Timestamp Behavior

Danger

Confirmed limitation — thread timestamps are not overridable via sendReply: The Zoho Desk sendReply and addComment endpoints do not accept a timestamp parameter. Thread and comment timestamps are set to the API call time, not the original message time. This is confirmed against the Zoho Desk OAS spec (raw.githubusercontent.com) and the v1 API documentation. There is no import-mode API for threads.

Consequence: Conversation chronology is preserved only by insertion order, not by displayed timestamps. Import messages strictly in the order they were sent (ascending createdAt), sequentially per ticket, to maintain readable conversation flow. If exact timestamps matter for compliance or SLA reporting, embed the original timestamp in the thread body: <p><em>Original message: 2024-03-15 14:30 UTC</em></p>.

Ticket createdTime behaves differently — Zoho Desk does accept and store a supplied createdTime on ticket creation. Run a 5-ticket proof of concept in your environment to verify this holds for your Zoho Desk edition and configuration before relying on it.

Slack Identity Resolution

This is the hardest part of extraction. Thena stores Slack user IDs and profile fields, but Zoho Desk requires a valid contactId (for customers) or assigneeId (for agents). If you inject a ticket without resolving identity, Zoho Desk attributes it to the API service account — ruining historical metrics.

The resolution pipeline:

  1. Extract all unique Slack user IDs from Thena request messages and contact records.
  2. Call the Slack users.info API for each unique Slack user ID to retrieve the canonical email address: GET https://slack.com/api/users.info?user={slack_user_id}. Slack's users.info endpoint is Tier 4 (100+ calls/minute allowed per workspace token), but workspace-level rate limits still apply — add a 0.5-second sleep between calls if resolving more than a few hundred users to avoid 429s on large workspaces.
  3. Query Zoho Desk: GET /api/v1/contacts/search?email={email}.
  4. If a match exists, cache the contactId.
  5. If no match, create the contact (POST /api/v1/contacts), capture the new contactId, and cache it.
  6. Store the full mapping in a persistent store (Redis, SQLite, or flat file) for lookup during ticket and thread creation.

Slack-only contacts without email addresses: Zoho Desk requires either lastName or email to create a contact. If a Thena contact only has a Slack profile name and no resolvable email, create the contact with the Slack display name as lastName and store the Slack ID in cf_slack_handle.

Agent identity: Build a separate mapping of Thena assignee → Zoho Desk agent ID. Retrieve all Zoho Desk agents via GET /api/v1/agents and match by email.

Slack mrkdwn → HTML Conversion

Thena stores message content in Slack's mrkdwn format. Zoho Desk threads accept HTML (contentType: "html"). Without a conversion layer, your Zoho tickets will be littered with unreadable syntax like <@U12345678> and <http://example.com|Click Here>.

Slack mrkdwn HTML equivalent Notes
*bold* <b>bold</b>
_italic_ <i>italic</i>
~strikethrough~ <s>strikethrough</s>
`code` <code>code</code>
```code block``` <pre>code block</pre>
<@U12345> Resolve to display name via identity cache Fall back to @slack_handle if unresolved
<#C12345|channel> #channel (plain text)
<https://url|text> <a href="https://url">text</a>
:emoji: Unicode character or text fallback Use an emoji shortcode library
> quoted text <blockquote>quoted text</blockquote>

Use a library like slackify-html (Node.js) or build a regex-based converter. Common edge cases: nested formatting (e.g., *_bold italic_*), emoji shortcodes, and user mentions where the Slack ID is not in your identity cache.

If Thena provides a contentHtml field on the message object, prefer that over converting raw mrkdwn — it avoids conversion errors entirely. (docs.thena.ai)

Attachment Handling

Zoho Desk enforces a 20 MB maximum attachment size per file. Attachment uploads follow a two-step flow:

  1. Download the attachment from Thena/Slack CDN URL to local staging during extraction (not at load time — Slack URLs are authenticated and time-limited).
  2. Check file size. If >20 MB, upload to external cloud storage (S3, GCS) and embed the link in the thread body instead.
  3. Upload to Zoho Desk via POST /api/v1/uploads (returns an id).
  4. Reference the upload id in the thread's attachmentIds array when creating the thread or comment.
Warning

Oversized files: Your script must catch failures for files exceeding 20 MB, log them, and append a note to the ticket body: "Attachment [filename] exceeded Zoho Desk's 20 MB limit and was stored externally at [URL]." Log the filename, original size, and Thena request ID to a separate audit file for post-migration review.

CSV Import: Where It Helps and Where It Doesn't

Zoho Desk supports CSV import for tickets, contacts, and accounts through Setup > Data Administration > Import. Record limits per batch (help.zoho.com):

  • Enterprise: 30,000 records
  • Professional: 20,000 records
  • Standard: 10,000 records

CSV import does not preserve:

  • Ticket threads/conversations
  • Attachments
  • Comment history
  • Thread authorship or timestamps

CSV is useful for bulk-loading accounts and contacts before running the API-based ticket migration — it saves API credits and is faster for parent records. But it is not a substitute for API-driven migration when conversation history, attachments, or comments matter.

ETL Pipeline Architecture

Execution Order

1. Pre-flight
   ├── Create Zoho Desk departments (map from Thena channels/teams)
   ├── Create custom fields (cf_thena_request_id, cf_ai_summary, cf_slack_handle, cf_thena_source)
   ├── Build agent email → Zoho Desk agentId mapping
   └── Verify data center region matches API base URL

2. Extract from Thena
   ├── Accounts → local staging
   ├── Contacts → local staging (with Slack ID resolution via users.info)
   └── Requests + Messages + Attachments → local staging

3. Load to Zoho Desk
   ├── Accounts (CSV or POST /api/v1/accounts) → build ID map
   ├── Contacts (CSV or POST /api/v1/contacts) → build ID map
   ├── Tickets (POST /api/v1/tickets) → build ID map
   ├── Threads per ticket (POST /tickets/{id}/sendReply) → chronological order, ascending createdAt
   └── Comments per ticket (POST /tickets/{id}/comments)

4. Validation
   ├── Record count comparison (Thena total vs. Zoho Desk total)
   ├── Thread count per ticket spot-check (sample 50 tickets)
   └── Attachment integrity verification (sample 20 attachments)

Idempotency and Error Recovery

Store the mapping of thena_id → zoho_id in a persistent store. If the pipeline crashes mid-run:

  • Accounts/Contacts: Check if the email already exists in Zoho Desk before creating (use GET /api/v1/contacts/search?email={email}).
  • Tickets: Query GET /api/v1/tickets/search?cf_thena_request_id={id} before creating. If a result exists, skip creation and use the existing ticketId.
  • Threads: No native deduplication in Zoho — track which thena_message_id values have been loaded per ticket in your local state store.

Duplicate contact handling in Zoho Desk: Org-level settings control whether contacts with matching emails are merged, rejected, or duplicated (Setup → Data Administration → Duplicate Handling). Verify your org's setting before the full load — the wrong configuration can silently break contact-ticket associations or create phantom duplicates. Test with 10 contacts before running the full contact import.

Edge Cases and Failure Modes

Multi-channel conversations. A single Thena Request might span Slack, email, and web chat messages. In Zoho Desk, a ticket's threads are channel-tagged but live in a single conversation view. Map all messages to email-channel threads unless channel-level fidelity is a reporting requirement.

Nested Thena comments. Thena comments can be nested via parentCommentId. Zoho Desk comments are flat. Flatten nested comments into chronological order and add lightweight context markers (e.g., "In reply to [original message excerpt]:") to preserve thread context. (docs.thena.ai)

AI metadata with no Zoho equivalent. Thena's AI-generated summaries, sentiment scores, and auto-detected request sources have no standard fields in Zoho Desk. Pre-create custom fields before migration. Custom field caps by edition: 50 (Standard), 150 (Professional), 230 (Enterprise). Audit your existing custom field count on Standard before adding migration fields.

Data center region mismatch. See the authentication section above. Using the wrong regional base URL returns authentication errors that look like token failures. Check your portal URL first.

Duplicate contact handling. Verify your Zoho Desk org setting at Setup → Data Administration → Duplicate Handling before running contact imports. Test behavior with a small batch.

Requests with no messages. Some Thena requests may have been created programmatically or via API with no message content. These create tickets in Zoho Desk with empty descriptions — valid but worth flagging in validation.

Cutover Strategy

  1. Keep Thena active during migration. Route new requests normally while the historical load runs.
  2. Set a cutover timestamp. All Thena requests created before this timestamp get migrated; new ones will go directly to Zoho Desk.
  3. Run the historical load. Extract and load closed/older Thena requests first to validate the pipeline before processing recent open tickets.
  4. Delta sync. Re-extract requests with updatedAt greater than your initial extraction timestamp using the Thena search API filter shown in the extraction section. Load the delta into Zoho Desk, using cf_thena_request_id to detect and skip already-loaded tickets.
  5. Freeze Thena. Disable new request creation in Thena. Run one final delta extraction for any records updated in the window between delta sync and freeze.
  6. Validate. Spot-check 50–100 tickets across different accounts, channels, and date ranges.
  7. Go live. Update Slack bot integrations, email forwarding rules, and web form targets to point at Zoho Desk.

Validation sampling strategy — do not sample randomly. Target these specific cases, as mapping bugs surface disproportionately in them:

  • The oldest migrated ticket (tests timestamp handling)
  • The newest migrated ticket (tests delta sync accuracy)
  • The ticket with the most threads (tests thread ordering and credit consumption)
  • A ticket with private/internal comments (tests isPublic: false handling)
  • A ticket with attachments (tests two-step upload flow)
  • A ticket that was reassigned or changed status multiple times (tests field mapping)
  • A ticket from a contact that was Slack-only with no email (tests fallback identity handling)

For a detailed approach to minimizing downtime, see our guide to zero-downtime help desk migrations.

Estimated Timeline and Effort

Dataset Size Extraction Transform Load Validation Total
Small (<5K requests) 4–8 hrs 8–16 hrs 8–16 hrs 4–8 hrs 24–48 hrs
Mid (5K–30K requests) 8–16 hrs 16–32 hrs 24–48 hrs 8–16 hrs 56–112 hrs
Large (30K+ requests) 16–24 hrs 32–48 hrs 48–72 hrs 16–24 hrs 112–168 hrs

The transform phase dominates. Slack identity resolution and mrkdwn conversion take more engineering time than the API calls themselves. The load phase duration depends entirely on your Zoho Desk edition and credit budget — Standard tier with a small team can stretch a mid-size load across 2–3 days due to the daily credit ceiling.

Load phase estimates assume single-threaded execution constrained by concurrency limits. Parallelizing up to your edition's concurrency ceiling (10 for Standard, 25 for Enterprise) can compress load time proportionally, but requires careful state management to avoid race conditions on shared contacts and accounts.


Frequently Asked Questions

Can I migrate from Thena to Zoho Desk using CSV import?
Only partially. Zoho Desk's CSV import handles accounts, contacts, and base ticket records, but it does not preserve ticket threads, attachments, comments, or conversation history. API-driven migration is required for full ticket history.
What are Zoho Desk API rate limits for a migration?
Zoho Desk uses a credit-based system, not simple rate limits. Daily credits depend on your edition and user count — for example, Enterprise gets 100,000 base + 1,000 per user. Creating a ticket costs 1 credit. Org-wide concurrency limits (5–25 simultaneous calls depending on edition) also apply.
How long does a Thena to Zoho Desk migration take?
For a mid-size dataset (5K–30K requests), expect 72–120 engineer-hours including pipeline development. The load phase alone can take 1–3 days depending on your Zoho Desk credit budget and concurrency limits.
How do I handle Slack user identities when migrating to Zoho Desk?
Thena stores Slack user IDs that must be resolved to email addresses. Build a Slack-to-email mapping using the Slack users.info API, then create or match Zoho Desk contacts with those emails. Store the original Slack handle in a custom field for reference.
What happens to Thena AI metadata like summaries and tags in Zoho Desk?
Zoho Desk has no native fields for AI summaries or sentiment. Pre-create custom fields (cf_ai_summary, cf_thena_source) in your Zoho Desk layout before migration. Thena tags can be mapped to Zoho Desk's tag system via the associateTag API.

More from our Blog

Zero-Downtime Help Desk Data Migration: How to Keep Support Running During the Move
Help Desk

Zero-Downtime Help Desk Data Migration: How to Keep Support Running During the Move

This guide details the 3-stage technical process for a zero-downtime help desk migration. Learn how to use an initial bulk data transfer, a continuous delta migration (Change Data Capture), and a seamless final cutover to move platforms without any service interruption. Discover how an engineer-led approach can guarantee a 100% accurate, 50x faster migration.

Raajshekhar Rajan Raajshekhar Rajan · · 7 min read