---
title: "Gladly to Unthread Migration: The CTO's Technical Guide"
slug: gladly-to-unthread-migration-the-ctos-technical-guide
date: 2026-06-05
author: Raaj
categories: [Migration Guide, Gladly, Unthread]
excerpt: "Technical guide for CTOs migrating from Gladly to Unthread. Covers API rate limits, data model mapping, conversation-splitting logic, and validation."
tldr: Migrating Gladly to Unthread requires splitting lifelong conversation timelines into discrete Slack tickets while handling Gladly's 10 req/sec API limit and .jsonl export format.
canonical: https://clonepartner.com/blog/gladly-to-unthread-migration-the-ctos-technical-guide/
---

# Gladly to Unthread Migration: The CTO's Technical Guide


Migrating from Gladly to Unthread is a schema translation project, not a simple export/import. Gladly centers support history on a customer profile and a continuous conversation timeline. Unthread tracks discrete conversations in Slack with statuses, assignees, ticket types, and SLA clocks. The engineering task: extract Gladly history at scale, split lifelong timelines into logical tickets, remap customers and agents to Slack identities, migrate attachments, and load everything into Unthread without breaking chronology. ([help.gladly.com](https://help.gladly.com/what-is-gladly))

Teams usually make this move when standardizing on Slack-native support, want an API-first operating model, or no longer need Gladly's broader B2C channel mix. If voice, SMS, and deep consumer-profile routing are still core to your service model, this is not a natural 1:1 switch. ([help.gladly.com](https://help.gladly.com/docs/features-and-capabilities-overview))

This guide covers the data model clash, API constraints, migration approaches, field-level mapping, and the validation steps needed to plan a Gladly-to-Unthread cutover. For background on Gladly's architecture, see [The Ultimate Guide to Mastering Gladly](https://clonepartner.com/blog/blog/ultimate-guide-gladly-2026/).

## The Core Data Model Clash: Lifelong Conversations vs. Slack Threads

The fundamental challenge of this migration is architectural, not cosmetic.

**Gladly** is built around a person-centric model. Every customer has a single profile, and every interaction — email, chat, voice, SMS, social — is threaded into one continuous, lifelong conversation timeline. There are no discrete ticket numbers. An agent sees the entire customer history as a single scrollable stream, regardless of channel or topic. Gladly explicitly positions itself as putting people, not tickets, at the center of the support experience. ([help.gladly.com](https://help.gladly.com/what-is-gladly))

**Unthread** operates on the opposite principle. It is a Slack-native ticketing system where each Slack thread, DM, or channel message becomes a discrete, trackable ticket. Each ticket has a defined lifecycle: open, in progress, resolved. Unthread groups top-level Slack messages into conversations based on a configurable time interval (defaulting to one hour), and threads are always tracked as individual conversations. ([docs.unthread.io](https://docs.unthread.io/docs/introduction-to-unthread/introduction))

This means a single Gladly customer with 18 months of interactions across email, chat, and phone has **one** conversation object in Gladly. In Unthread, that same history needs to become **many** individual tickets, each mapped to a Slack thread with the correct timestamps, assignee, and resolution state.

> [!WARNING]
> Do not map one Gladly customer profile to one Unthread ticket. A customer with three years of history will generate a single, massive ticket containing hundreds of unrelated messages. This breaks SLA tracking, overwhelms the Slack interface, and makes the data operationally useless. ([help.gladly.com](https://help.gladly.com/docs/whats-the-difference-between-a-conversation-and-a-ticket))

### Why This Breaks Naive Migration

- **No 1:1 mapping exists.** You must define splitting logic — by topic change, time gap, channel switch, or a combination.
- **Customer identity must be resolved.** Gladly merges profiles across channels into a single record. Unthread ties conversations to Slack users or customer records. If your customers are not Slack users (common when moving from B2C on Gladly to B2B on Unthread), you need to represent historical identities as external user profiles or Unthread Customer objects.
- **Attachments are channel-bound.** Gladly stores attachments across email, chat, and social. Unthread is constrained by Slack's limits: 10 files per message, 20MB maximum per file.

### Conversation Splitting Logic

Your transformation layer needs deterministic rules to split Gladly timelines into discrete tickets:

- **Time-gap splitting:** If more than N hours elapse between consecutive messages, start a new ticket. Common thresholds range from 2 hours (closer to Unthread's own default grouping behavior) to 7 days (for low-volume accounts). Pick what matches your operating model.
- **Topic-based splitting:** If Gladly topics change within a conversation, create a new ticket per topic.
- **Channel-based splitting:** Each channel switch (email → chat → phone) becomes a separate ticket.
- **Reopened issues:** If an issue reopens after a long dormant period, create a new conversation with a backlink to the source.

Keep splitting rules in version control so QA can replay the same segmentation logic. Expect 3–5 iterations with your support ops team before the heuristic is production-ready.

## Migration Approaches: What Actually Works

Four viable paths, each with different trade-offs in engineering effort, data fidelity, and risk. The hard constraints are vendor-documented: Gladly's default API limit is 10 requests per second, large exports are `.jsonl` and expire after 14 days, Unthread list endpoints use cursor-based pagination with a 100-per-page cap, and Slack imports must respect 10 attachments and 20MB per message. ([developer.gladly.com](https://developer.gladly.com/rest/))

### 1. Export API + Custom ETL Script

**How it works:** Use Gladly's Export API to generate `.jsonl` files containing conversation items, customers, agents, and topics for a specified date range. Parse the output, apply transformation logic to split timelines into discrete tickets, then push to Unthread's REST API.

**When to use it:** Medium-to-large datasets where you need full control over the transformation logic.

**Pros:**
- Full control over splitting logic and field mapping
- Handles large datasets without hitting per-request rate limits
- Can be version-controlled and tested incrementally

**Cons:**
- Requires significant engineering time (2–4 weeks for a production-quality pipeline)
- You own all error handling, retry logic, and data validation
- Gladly's conversation timeline API returns at most 1,000 items per conversation — the `Gladly-Limited-Data` response header flags truncated results, but the API does not paginate further

> [!NOTE]
> Gladly's Export API returns `.jsonl` format (one JSON object per line). Export files are available for 14 days after generation. One-time exports covering periods greater than six months may require coordination with Gladly Support and could incur a service fee. ([developer.gladly.com](https://developer.gladly.com/rest/))

**Complexity:** High

### 2. REST API Per-Record Extraction

**How it works:** Use Gladly's standard REST endpoints to fetch customers, conversations, and timeline items one by one. Transform in-memory, then POST to Unthread.

**When to use it:** Small datasets (under 5,000 conversations) or test migration runs to validate your mapping logic before committing to a full pipeline.

**Pros:**
- Simpler to implement than the bulk export path
- Good for validation and dry runs

**Cons:**
- Gladly enforces a **10 requests per second** rate limit across all HTTP methods. At that rate, extracting 50,000 conversation items takes over 80 minutes of pure API time — before transformation or loading.
- The conversations list endpoint returns at most 100 conversations per customer and is not paginated
- Not viable for enterprise-scale datasets

**Complexity:** Medium

### 3. iPaaS / Middleware (Zapier, Make)

**How it works:** Configure workflows that map Gladly events or scheduled exports to Unthread API actions. Unthread has native Zapier and Make integrations with webhook support. ([docs.unthread.io](https://docs.unthread.io/docs/webhooks))

**When to use it:** Ongoing lightweight sync of new conversations during a transition period. Not for historical migration.

**Pros:**
- No code required for basic field mapping
- Quick to set up for low-volume forward sync

**Cons:**
- Cannot handle the conversation-splitting transformation — iPaaS tools map fields, they do not restructure data models
- No support for batch operations or large historical datasets
- Limited error handling: Unthread retries failed webhooks up to 4 times; Gladly retries webhooks up to 4 times over an hour. Ordering drift during retries is a real risk. ([docs.unthread.io](https://docs.unthread.io/docs/webhooks))

In a public Nango case study, Unthread described moving away from Merge's unified API model because the generic abstraction missed needed data and still left system-specific behavior to handle. That failure mode applies to any overly generic migration adapter for this project. ([nango.dev](https://www.nango.dev/case-studies/unthread))

**Complexity:** Low

### 4. Managed Migration Service

**How it works:** A dedicated migration team handles extraction, transformation, loading, and validation. Custom scripts are built specifically for the Gladly-to-Unthread data model transformation.

**When to use it:** When you need high data fidelity, cannot afford downtime during cutover, or lack internal engineering bandwidth for a multi-week project.

**Pros:**
- Handles the conversation-splitting problem with tested, production-grade logic
- Includes validation, rollback planning, and delta migration capability
- Your engineering team stays on product work

**Cons:**
- External cost
- Requires sharing API credentials with the migration partner

**Complexity:** Low (for your team)

### Approach Comparison

| Approach | Best For | Scale | Historical Data | Ongoing Sync | Complexity | Main Risk |
|---|---|---|---|---|---|---|
| Export API + Custom ETL | Medium–large datasets, full control | Medium–Enterprise | ✅ | ❌ | High | Engineering time, edge cases |
| REST API Per-Record | Small datasets, test runs | Small | ✅ | ❌ | Medium | Rate limits, truncated data |
| iPaaS (Zapier/Make) | Forward-only sync | Small ongoing | ❌ | ✅ | Low | Ordering drift, no splitting |
| Managed Service | Any size, high fidelity | Any | ✅ | ✅ (delta) | Low (for you) | External cost |

## Handling Gladly API Limits and Export Formats

Gladly's rate limiting is strict and uniform. Every HTTP method — GET, POST, PUT, PATCH, DELETE — is capped at **10 requests per second** at the organization level. You can monitor consumption via the `Ratelimit-Limit-Second` and `Ratelimit-Remaining-Second` response headers. Exceeding the limit returns HTTP 429. ([developer.gladly.com](https://developer.gladly.com/rest/))

For any migration involving more than a few thousand records, the Export API is the only practical extraction method:

- **Export jobs are date-range scoped.** You specify a start and end time, and Gladly generates files covering all communications delivered in that window.
- **Output format is `.jsonl`** — one JSON object per line. File types include `customers`, `conversation_items`, `agents`, and `topics`.
- **Files expire after 14 days.** Download them immediately or automate the pull on job completion.
- **Conversation items with invalid attributes may be silently excluded** — undelivered emails or auto-replies generated by Gladly rules can be absent from exports.
- **The conversation timeline API caps at 1,000 items per conversation.** If a customer has a longer history, the `Gladly-Limited-Data` response header will be `true`, but the API will not paginate further. For high-volume customers, the Export API is the only path to complete data.

> [!WARNING]
> Do not load a multi-gigabyte Gladly `.jsonl` export into memory at once. Use a streaming parser or stage the data in an intermediary database (PostgreSQL, for example) before running transformation logic. This avoids out-of-memory exceptions and gives you a queryable layer for debugging.

### Parsing .jsonl for Transformation

Each line in a Gladly `.jsonl` export contains a self-contained JSON object with fields like `id`, `conversationId`, `customerId`, `initiator`, `timestamp`, and `content`. Your transformation layer must:

1. Group items by `conversationId`
2. Sort by `timestamp` within each group (use source `id` as a tiebreaker for timestamp collisions)
3. Apply splitting logic to create discrete Unthread tickets
4. Map `initiator.type` (`CUSTOMER` or `AGENT`) to the correct Unthread user or customer record

## Mapping Gladly Data to Unthread's Schema

Unthread's API follows RESTful conventions with JSON request/response bodies. Authentication uses an `X-Api-Key` header tied to a service account. List endpoints use **cursor-based pagination** — responses include a `cursors` object with `next` and `previous` strings. You must parse this object and pass the `next` cursor to subsequent requests; offset pagination will silently miss records. ([docs.unthread.io](https://docs.unthread.io/docs/api-docs/api-reference))

### Core Object Mapping

| Gladly Source | Unthread Target | Transformation Notes |
|---|---|---|
| Customer Profile (`id`, `name`, `emails`, `phones`) | Account + Customer | Map `externalCustomerId` to `account.externalCrmMetadata.id`. Gladly allows multiple emails/phones; choose a primary identifier. In B2B Slack Connect setups, map to an Account with associated email domains and Slack channels. |
| Conversation (lifelong timeline) | Multiple Conversations (Tickets) | Split using time-gap, topic, or channel heuristics. One Gladly conversation → N Unthread tickets. |
| Conversation Item (`content`, `timestamp`, `initiator`) | Message / Reply | Convert to Slack-compatible markdown. Preserve chronological order. Use `onBehalfOf` for authorship attribution. |
| Agent (`id`, `name`, `email`) | User (Workspace Member) | Match by email to a Slack workspace member. Provision agents in Unthread before migration. Fall back to a placeholder assignee if no match exists. |
| Topic / Tag | Tag | Direct mapping. Flatten Gladly's hierarchical topics. Preserve original topic IDs in metadata for analytics continuity. |
| Conversation Status | Status (`open`, `in_progress`, `on_hold`, `closed`) | Map through a deterministic status table. Preserve original Gladly status in metadata. |
| Attachment (binary + metadata) | Attachment (multipart upload) | Download from Gladly, re-upload to Unthread. Slack enforces 10 files per message, 20MB per file. |
| Voicemail / Phone Transcript | Note or archived text | Unthread has no native voice channel. Convert to text-based notes or omit from scope. |

### Key Transformation Rules

**Customer identity in Unthread's model:** Unthread distinguishes between **Accounts** (external customer organizations), **Customers** (channel-level support settings), and **Users** (Slack workspace members). A Gladly person may need to map to an Unthread Account plus a Customer or Slack channel, plus an external user representation — not just one record. If the historical customer does not exist in your Slack workspace, create them as an Unthread Customer object so the historical context is preserved without failing the API request. ([docs.unthread.io](https://docs.unthread.io/docs/global-workspace-settings/accounts))

**Message formatting:** Gladly stores message content in its own rich-text format. Unthread expects Slack-compatible markdown. HTML tags, inline images, and email signatures need to be stripped or converted during transformation.

**Conversation type:** Unthread conversations have a `type` field: `slack`, `email`, or `triage`. If no shared Slack channel exists for a migrated issue, decide per segment whether the target should be `slack`, `email`, or `triage` — not once for the entire migration. ([docs.unthread.io](https://docs.unthread.io/docs/api-docs/api-reference))

## Step-by-Step Migration Process

Use a straightforward pipeline: **extract → stage → transform → load → validate**. Authenticate to Gladly with token-based Basic auth and to Unthread with an `X-Api-Key` service-account header.

### Phase 1: Pre-Migration Audit

- Export a full inventory of Gladly customers, conversations, agents, topics, and tags
- Count total conversation items — this determines whether you use the REST API or Export API
- Identify merged customer profiles (Gladly returns `301` for merged records and `404` for update attempts on merged IDs)
- Catalog all attachment types and sizes; flag anything exceeding Slack's 20MB limit
- Define the conversation-splitting heuristic with your support ops team
- Identify unused tags, inactive agents, and dead inboxes — exclude them from scope
- Set up the Unthread workspace, connect Slack, and configure service accounts for API access

### Phase 2: Extract from Gladly

For datasets larger than a few thousand conversations, use the Export API:

```python
# Trigger Gladly Export Job
import requests

GLADLY_BASE = "https://yourorg.gladly.com"
HEADERS = {"Authorization": "Basic <base64(email:token)>"}

# Create export job
job = requests.post(f"{GLADLY_BASE}/api/v1/export/jobs", headers=HEADERS, json={
    "startAt": "2024-01-01T00:00:00.000Z",
    "endAt": "2025-12-31T23:59:59.000Z"
})
job_id = job.json()["id"]

# Poll until COMPLETED, then download .jsonl files:
# customers.jsonl, conversation_items.jsonl, agents.jsonl, topics.jsonl
```

Supplement with REST calls for attachments, recordings, and transcripts where the export does not include them. Download everything to a secure, encrypted staging environment. Back up immediately — export files disappear from the API after 14 days.

### Phase 3: Transform and Split Timelines

Parse the `.jsonl` records. Group messages by `conversationId`. Sort chronologically. Apply the splitting heuristic:

```python
from collections import defaultdict
from datetime import datetime, timedelta

TIME_GAP = timedelta(hours=168)  # 7-day threshold; adjust to match your model

def split_into_tickets(conversation_items):
    grouped = defaultdict(list)
    for item in conversation_items:
        grouped[item["conversationId"]].append(item)
    
    tickets = []
    for conv_id, items in grouped.items():
        items.sort(key=lambda x: x["timestamp"])
        current_ticket = [items[0]]
        for i in range(1, len(items)):
            prev_time = datetime.fromisoformat(items[i-1]["timestamp"].rstrip("Z"))
            curr_time = datetime.fromisoformat(items[i]["timestamp"].rstrip("Z"))
            if curr_time - prev_time > TIME_GAP:
                tickets.append(current_ticket)
                current_ticket = [items[i]]
            else:
                current_ticket.append(items[i])
        tickets.append(current_ticket)
    return tickets
```

Convert message content to Slack-compatible markdown. Build customer and agent crosswalks. Tag every staged record with immutable source IDs for traceability.

### Phase 4: Load Setup Objects, Then Conversations

Create Unthread accounts, customers, tags, ticket types, and user mappings **before** any conversation load. Then create conversations and replay messages in chronological order:

```python
UNTHREAD_BASE = "https://api.unthread.io/api"
UT_HEADERS = {"X-Api-Key": "your-service-account-key", "Content-Type": "application/json"}

def create_ticket(ticket_items, customer_map):
    first_msg = ticket_items[0]
    conv = requests.post(f"{UNTHREAD_BASE}/conversations", headers=UT_HEADERS, json={
        "type": "email",  # or slack/triage, per source channel
        "markdown": first_msg["content"]["content"],
        "status": "open",
        "onBehalfOf": {
            "email": customer_map[first_msg["customerId"]]["email"],
            "name": customer_map[first_msg["customerId"]]["name"]
        },
        "metadata": {
            "sourceConversationId": first_msg["conversationId"],
            "sourceCustomerId": first_msg["customerId"]
        }
    })
    conv_id = conv.json()["id"]
    
    for item in ticket_items[1:]:
        requests.post(f"{UNTHREAD_BASE}/conversations/{conv_id}/messages",
                      headers=UT_HEADERS, json={
            "markdown": item["content"]["content"],
            "authorType": "agent" if item["initiator"]["type"] == "AGENT" else "customer"
        })
    
    # Patch final status after message replay
    requests.patch(f"{UNTHREAD_BASE}/conversations/{conv_id}",
                   headers=UT_HEADERS, json={"status": "closed"})
```

Preserve source IDs in metadata on every created object. This makes debugging and reconciliation possible after the load completes.

### Phase 5: Migrate Attachments

Gladly exports provide URLs for attachments, but these are authenticated and fetched through redirect endpoints. Your script must:

1. Download the binary file from the Gladly URL
2. POST it to Unthread's attachment endpoint as a multipart form upload
3. Link the resulting Unthread attachment ID to the specific thread reply
4. Handle Slack's 10-file-per-message limit by splitting over-limit payloads across multiple messages if necessary

Inline images pasted directly into Gladly chats are often stored as base64-encoded strings or authenticated URLs embedded in the HTML message body. If your script does not parse the message content to extract and re-host these images, Unthread will display broken image links.

### Phase 6: Validate

Do not cut over without systematic validation:

| Check | Method | Pass Criteria |
|---|---|---|
| Record count | Compare Gladly export totals vs. Unthread API counts | ≤ 0.1% variance |
| Chronological order | Sample 100 tickets, verify message timestamps ascending | 100% pass |
| Customer attribution | Cross-reference 50 customers across both systems | All messages linked to correct customer |
| Attachment integrity | Download 50 random Unthread attachments, compare checksums to source | 100% match |
| Tag mapping | Verify all Gladly topics appear as Unthread tags | Complete coverage |
| Merged profiles | Confirm merged Gladly profiles resolved to correct Unthread customer | No duplicates |

Run at least two test migrations. The first will expose mapping gaps. The second validates your fixes. Automate the validation — write comparison scripts that produce a pass/fail report after each load run rather than relying on manual spot-checks.

## Edge Cases That Break Migrations

Migrations fail on the edges, not the happy path. For recovery tactics when things go wrong, read [Helpdesk Migration Failed? The Engineer's Rescue Guide](https://clonepartner.com/blog/blog/helpdesk-migration-failed-the-engineers-rescue-guide/).

**Merged customer profiles.** Gladly returns a `301` redirect when you request a customer ID that was merged into another. Your extraction logic must follow these redirects and deduplicate, or you will create duplicate customers in Unthread. Merged profiles can also cause conversation history to appear out of order — your sorting layer must handle this explicitly.

**Truncated timelines.** The conversation timeline API returns at most 1,000 items per conversation. High-volume customers (enterprise accounts with years of chat history) will have incomplete data unless you use the Export API. The `Gladly-Limited-Data` header flags this, but the API does not paginate further.

**Inline images in email threads.** Gladly stores inline images as part of the message content — sometimes as base64 strings, sometimes as authenticated URLs. These must be extracted, re-hosted, and referenced as Slack-compatible URLs. Slack does not reliably render arbitrary external image URLs in all contexts.

**Voicemail and phone transcripts.** Gladly stores voicemail content and call metadata on separate API endpoints. Unthread has no native voice channel. Convert these to text-based notes or explicitly exclude them from scope.

**Missing export items.** Gladly's documentation notes that rare conversation items with invalid attributes can be silently excluded from exports. Undelivered emails and auto-replies generated by rules are common culprits. Flag gaps during reconciliation. ([developer.gladly.com](https://developer.gladly.com/rest/))

**Rate-limit cascading.** At 10 requests/second on Gladly's side, a poorly designed extraction script will spend more time waiting than working. Implement exponential backoff with jitter and monitor the `Ratelimit-Remaining-Second` header to throttle proactively. On the Unthread side, the public documentation does not specify a general API rate limit as of this writing — keep worker concurrency conservative and implement retry logic with backoff for any 429 responses.

## Post-Migration: What to Rebuild in Unthread

Once data is loaded, the migration is not done. Unthread's operational model is fundamentally different from Gladly's, and several components cannot be migrated — they must be rebuilt:

- **Routing rules.** Gladly uses inbox-based routing with skills matching. Unthread routes via Slack channels, assignments, and automation rules. These must be reconfigured from scratch.
- **SLA policies.** Gladly SLAs are tied to conversations. Unthread SLAs are tied to tickets with distinct response and resolution targets. Rebuild these in Unthread's dashboard.
- **Knowledge base.** If you were using Gladly's Answers, migrate that content separately. Unthread can sync documentation from web-based sources or native integrations.
- **Automations and triggers.** Unthread supports custom TypeScript automations, webhook triggers, and Zapier/Make integrations. Rebuild your Gladly rules as Unthread automations.
- **Agent training.** Your team is moving from a dedicated agent desktop to a Slack-native interface. The workflow muscle memory is completely different. Plan at least one week of parallel operation before cutting over.

Keep Gladly active in read-only mode for at least 30 days post-migration. Do not delete Gladly data until validation is complete and your team has confirmed no missing records.

Keep workspace setup on a separate track from historical data movement. [Migration is not implementation](https://clonepartner.com/blog/blog/data-migration-vs-implementation-guide/) — mixing the two usually hides data issues until cutover week.

## Why In-House Migrations Fail at the Data Layer

The technical surface area of this migration is deceptive. The API calls are straightforward. The data model transformation is not.

Teams that build in-house typically underestimate three costs:

1. **Splitting logic iteration.** The first version of your conversation-splitting heuristic will produce bad tickets — too granular, too broad, or inconsistent across channels. Expect multiple rounds of review with support ops before the logic is production-ready.
2. **Attachment re-hosting.** Gladly attachment URLs are authenticated. You must download every file, re-upload to a location Unthread can access, and respect Slack's file constraints. For a 50,000-conversation migration, attachment handling alone can take days.
3. **Validation engineering.** Building comparison scripts, sampling frameworks, and rollback procedures is a separate workstream that often takes as long as the migration itself.

The real cost is not the API integration — it is the 4–6 weeks of engineering time diverted from product work. Because migrations are one-off projects, the scripts rarely get the rigorous error handling of production systems. This leads to silent failures: missing attachments, broken threads, and corrupted timelines that surface weeks after cutover.

## When to Use a Managed Migration Service

**Build in-house if** you have a dedicated engineer who can commit 4–6 weeks, a dataset under 10,000 conversations, and a straightforward splitting heuristic.

**Use a managed service if:**

- Your dataset exceeds 50,000 conversation items
- You have merged customer profiles that need deduplication
- You need zero downtime during cutover
- Your engineering team cannot absorb a multi-week project without slipping product commitments
- You need delta migrations to keep both systems in sync during a phased rollout
- You have significant attachment or voice history

ClonePartner has handled complex helpdesk migrations — including the kind of timeline-to-ticket transformation this migration requires — across 1,500+ projects. For Gladly to Unthread transitions, we provide:

- **Automated timeline splitting** based on customizable time-gap and topic heuristics
- **Rate limit handling** using optimized export ingestion for Gladly and conservative cursor-based loading for Unthread
- **Point-in-time delta migrations** — your team continues working in Gladly while we migrate historical data. On cutover day, we sync only the final delta, ensuring no disruption to support operations. See [Zero Downtime Guaranteed](https://clonepartner.com/blog/blog/zero-downtime-data-migration/) for how this works.
- **Complete attachment and inline image migration** — extracted, re-hosted, and linked to the correct Unthread thread replies

Your engineering team stays on product work. We deliver a correctly structured Unthread database.

> Need help migrating from Gladly to Unthread? Our team handles extraction, transformation, validation, and delta cutover — including the conversation-splitting logic that makes this migration unique. Book a 30-minute technical scoping call.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can you directly export data from Gladly to Unthread?

No. There is no native export-to-Unthread path. You must extract data from Gladly using its Export API (.jsonl files) or REST API, transform the data to split lifelong conversations into discrete tickets, and then load into Unthread via its REST API.

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

Gladly enforces a default rate limit of 10 requests per second across all HTTP methods (GET, POST, PUT, PATCH, DELETE). Exceeding this returns HTTP 429. Monitor the Ratelimit-Remaining-Second response header to throttle proactively. For bulk extraction, use the Export API instead of per-record REST calls.

### How do you map Gladly conversations to Unthread tickets?

Gladly uses a single lifelong conversation per customer. You must apply a splitting heuristic — typically based on time gaps between messages, topic changes, or channel switches — to convert one Gladly conversation into multiple discrete Unthread tickets. Expect several iterations before the splitting logic is production-ready.

### What format does the Gladly Export API use?

Gladly's Export API returns .jsonl (JSON Lines) files where each line is a self-contained JSON object. File types include customers, conversation_items, agents, and topics. Files are available for download for 14 days after generation.

### How long does a Gladly to Unthread migration take?

A DIY API-based migration typically takes 4–6 weeks of engineering time including script development, splitting logic iteration, and validation. A managed migration service like ClonePartner can complete the same migration in days using pre-built extraction and transformation pipelines.
