---
title: "How to Export Data from Groove: Methods, API Limits & Portability"
slug: how-to-export-data-from-groove-methods-api-limits-portability
date: 2026-08-12
author: Abdul
categories: [Groove, Migration Guide, Help Desk]
excerpt: "Complete guide to exporting data from GrooveHQ. Covers the built-in JSON export, REST API v1, GraphQL v2, KB extraction, rate limits, and portability gaps."
tldr: "Use Groove's built-in export for archives. For migration-ready data, use REST v1 for tickets, messages, customers, attachments, and KB content, then validate counts and keep a delta sync running until cutover."
canonical: https://clonepartner.com/blog/how-to-export-data-from-groove-methods-api-limits-portability/
---

# How to Export Data from Groove: Methods, API Limits & Portability


# How to Export Data from Groove: Methods, API Limits & Portability

Groove gives you three paths to get data out: a **built-in JSON export** for conversations, the **REST API v1** (deprecated but functional), and the **GraphQL API v2** (actively developed). There is no native CSV export, no "download everything" button that covers all object types, and no bulk knowledge base export from the admin UI. If you need tickets plus KB articles plus customer records plus attachments, you are combining at least two of these methods — and working around rate limits on both.

This guide covers every extraction method with real technical constraints, walks through what data you can and cannot get out, and flags the gaps that silently break migrations.

**Verified against:** Groove REST API v1 docs (`doc.groovehq.com`), Groove GraphQL API v2 docs (`developer.groovehq.com`), and Groove help center documentation as of mid-2026.

> [!NOTE]
> **Branding note:** On June 2, 2026, Groove moved under the **Groove Classic** name inside the Helply master brand. Existing accounts, data, and `yourcompany.groovehq.com` logins remain in place, so the export methods in this guide still apply. ([helply.com](https://helply.com/blog/groove-is-now-helply?utm_source=openai))

If you are planning to move this data into another helpdesk, see our migration-specific guides: [Groove to Zendesk](https://clonepartner.com/blog/blog/groove-to-zendesk-migration-data-mapping-apis-rate-limits/) or [Groove to Zammad](https://clonepartner.com/blog/blog/groove-to-zammad-migration-a-technical-guide/).

## What Data Can You Export from Groove?

Groove's data model is flat compared to platforms like Zendesk or Freshdesk — it centers on individual customers rather than organizations, and most configuration objects (rules, canned responses, reporting) have no API surface at all. Here is what exists and how each object type can be extracted:

| Data Object | Built-in Export | REST API v1 | GraphQL API v2 |
|---|---|---|---|
| Conversations/Tickets | ✅ Full JSON | ✅ `GET /v1/tickets` | ✅ Conversations query |
| Messages (replies + notes) | ✅ Included in export | ✅ `GET /v1/tickets/:id/messages` | ✅ Nested in conversations |
| Customers | ❌ Not separate | ✅ `GET /v1/customers` | ✅ Contacts query |
| Agents | ❌ Not included | ✅ `GET /v1/agents` | ✅ Agents query |
| Mailboxes | ❌ Not included | ✅ `GET /v1/mailboxes` | ✅ Mailboxes query |
| Tags/Labels | ✅ On each ticket | ✅ On ticket objects | ✅ Tags query |
| Attachments | ✅ URLs only | ✅ `GET /v1/attachments` | ✅ Via messages |
| Groups | ❌ Not included | ✅ `GET /v1/groups` | ✅ Teams query |
| Folders | ❌ Not included | ✅ `GET /v1/folders` | ✅ Folders query |
| Knowledge Base articles | ❌ No bulk export | ✅ `GET /v1/kb/:id/articles` | ✅ KB Articles query |
| KB Categories | ❌ No bulk export | ✅ `GET /v1/kb/:id/categories` | ✅ KB Categories query |
| Custom Fields | ✅ On conversations | ✅ On ticket objects | ✅ `customFieldValues` |
| Instant Replies | ❌ | ❌ | ❌ |
| Rules/Automations | ❌ | ❌ | ❌ |
| Reports/Analytics | ❌ | ❌ | ❌ |

> [!WARNING]
> **Not exportable:** Instant Replies (canned responses), automation rules, round-robin configurations, CSAT survey results, and reporting data have no export or API surface. Screenshot or manually document these before deactivating your account.

## Method 1: Groove's Built-in Conversation Export

The fastest way to get ticket history out of Groove is the **built-in export** available to account Owners and Admins.

### How to request the export

1. Go to **Settings → Company → More → Exports**
2. Click **Request Export**
3. Wait for the email notification (or watch the status on the settings page)
4. Download the GZIP file from the same settings page

### What you get

The export produces a **GZIP-compressed JSON file** following Groove's v1 Tickets API "full" conversations format. It includes ticket metadata (status, assignee, tags, custom fields), all messages in each conversation thread, and customer contact information embedded in each ticket. ([help.groovehq.com](https://help.groovehq.com/help/export-your-groove-tickets?utm_source=openai))

### Constraints

- **JSON only** — no CSV option
- **Conversations only** — customers, agents, mailboxes, groups, folders, and KB articles are not included as separate exports
- **One export at a time** — you cannot queue multiple export requests
- **Processing time varies** — Groove processes exports in a FIFO queue shared across all customers; small accounts take minutes, large accounts can take **up to 72 hours** ([help.groovehq.com](https://help.groovehq.com/help/export-your-groove-tickets?utm_source=openai))
- **Short-lived download links** — URLs are generated on Amazon S3 and expire quickly; they cannot be shared
- **No date filtering** — you get everything or nothing; there is no way to export a specific date range

> [!TIP]
> **Request your export early.** If processing takes 72 hours, you do not want that delay falling in the middle of your cutover window.

### When the built-in export is enough

If all you need is an archive of conversation history — for compliance, backup, or a quick migration to a platform that can ingest Groove's JSON format — the built-in export works. It is zero-code and captures ticket threads with full message bodies.

If you need customers as a standalone dataset, KB articles, agent metadata, or granular control over what you extract, you need the API.

## Method 2: REST API v1

**Groove's REST API v1** is officially deprecated — the docs show a banner stating it is no longer in active development and recommending GraphQL. But it remains functional and is the more documented, more battle-tested extraction surface for complete data pulls.

### Authentication

All requests require a Bearer token in the `Authorization` header. Generate this token from your Groove account settings.

```bash
curl -i https://api.groovehq.com/v1/tickets \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Key endpoints for data extraction

| Endpoint | Purpose | Pagination |
|---|---|---|
| `GET /v1/tickets` | List all conversations | Page-based, max 50/page |
| `GET /v1/tickets/:number` | Single ticket detail | N/A |
| `GET /v1/tickets/:number/messages` | Messages in a ticket | Page-based, max 50/page |
| `GET /v1/customers` | List all customers | Page-based |
| `GET /v1/customers/:email` | Single customer by email | N/A |
| `GET /v1/agents` | List all agents | Not paginated |
| `GET /v1/mailboxes` | List all mailboxes | Not paginated |
| `GET /v1/groups` | List all groups | Not paginated |
| `GET /v1/folders` | List all folders | Not paginated |
| `GET /v1/attachments?message=:id` | Attachments for a message | Per-message |
| `GET /v1/kb/:kb_id/articles` | KB articles | Page-based |
| `GET /v1/kb/:kb_id/categories` | KB categories | Page-based |
| `GET /v1/tickets/count` | Ticket counts by folder | N/A |

Ticket listing supports filters like `assignee`, `customer`, `state`, and `folder` — useful for mailbox-by-mailbox or owner-by-owner extraction instead of a single giant dump. ([doc.groovehq.com](https://doc.groovehq.com/tickets))

### Pagination

The REST API uses **page-based pagination** with `page` and `per_page` parameters. Maximum `per_page` for tickets and messages is **50**. The response includes a `meta.pagination` object:

```json
"meta": {
  "pagination": {
    "current_page": 1,
    "total_pages": 23,
    "total_count": 23,
    "next_page": "https://api.groovehq.com/v1/tickets?page=2"
  }
}
```

For a full extraction, iterate through every page of tickets, then for **each ticket** fetch messages separately via `GET /v1/tickets/:number/messages`. This is inherently chatty — a 5,000-ticket account with an average of 6 messages per ticket requires at least **5,100 API calls** (100 ticket pages + ~5,000 message fetches assuming single-page threads). At 200 calls/minute on the Standard plan, that minimum call count alone takes over 25 minutes — before accounting for retries, attachment fetches, or customer lookups.

**Structural quirk:** The ticket listing endpoint uses **HATEOAS-style links** rather than embedding related objects. You get URLs to the assignee, customer, and messages — not the actual data inline. A "full" extraction means following those links, which multiplies API calls significantly beyond the minimum estimate above.

### Rate limits

Groove's published API rate limits are plan-based: **200 calls per minute** on Standard, **400** on Plus, and **800** on Pro. ([help.groovehq.com](https://help.groovehq.com/help/billing-faq?utm_source=openai)) The API returns **HTTP 429 (Too Many Requests)** when you exceed them.

Practical guidance:

- Implement **exponential backoff with jitter** on 429 responses
- Log every 429 and its `Retry-After` header (when present) to calibrate your throttle
- Do not run extraction scripts during peak business hours — you share the rate limit pool with your live support team
- On Standard (200 calls/min), a complete extraction of a 10,000-ticket account with 6 messages per ticket and no attachment fetches requires roughly 10,200 calls minimum — about 51 minutes of pure API time at sustained throughput, longer with backoff

> [!WARNING]
> **The `summary` field trap:** The ticket listing endpoint returns a `summary` field that is intentionally truncated. If you need the full message body, you **must** fetch messages separately via the messages endpoint — the full content is in the `body` field of each message object. Do not build a migration off the `summary`. ([doc.groovehq.com](https://doc.groovehq.com/tickets))

### Sample extraction script

```python
import requests
import time
import json

BASE_URL = "https://api.groovehq.com/v1"
HEADERS = {"Authorization": "Bearer YOUR_TOKEN"}

def fetch_paginated(endpoint, key):
    """Fetch all pages from a paginated Groove endpoint."""
    results = []
    page = 1
    while True:
        resp = requests.get(
            f"{BASE_URL}/{endpoint}",
            headers=HEADERS,
            params={"page": page, "per_page": 50}
        )
        if resp.status_code == 429:
            wait = int(resp.headers.get("Retry-After", 60))
            time.sleep(wait)
            continue
        resp.raise_for_status()
        data = resp.json()
        results.extend(data.get(key, []))
        pagination = data.get("meta", {}).get("pagination", {})
        if page >= pagination.get("total_pages", 1):
            break
        page += 1
        time.sleep(0.5)  # Conservative throttle
    return results

# Fetch core data
tickets = fetch_paginated("tickets", "tickets")
customers = fetch_paginated("customers", "customers")

# Fetch messages per ticket
for ticket in tickets:
    ticket["messages"] = fetch_paginated(
        f"tickets/{ticket['number']}/messages", "messages"
    )
```

This is a starting point, not production code. Add proper error handling, logging, checkpoint/resume logic (so you do not restart from scratch on failure), and attachment downloading for any real extraction. Without checkpoint/resume, a network failure at ticket 4,800 of 5,000 sends you back to zero.

## Method 3: GraphQL API v2

Groove's GraphQL API v2 is the officially recommended API. It operates on a single endpoint — `https://api.groovehq.com/v2/graphql` — and uses the same Bearer token authentication.

```bash
curl https://api.groovehq.com/v2/graphql \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"query { __typename }"}'
```

### Advantages over REST v1

- **Fewer round trips:** Fetch a conversation with its messages, customer, assignee, and tags in a single query instead of 3–4 REST calls — eliminating the HATEOAS link-following overhead
- **Field selection:** Request only the fields you need, reducing payload size
- **Strongly typed schema:** Introspect the schema to discover all available types and fields
- **Rich filtering:** The conversations query supports filters like `channelId`, `contactEmail`, `folderId`, `keywords`, `states`, `tagNames`, and before/after created or updated timestamps ([developer.groovehq.com](https://developer.groovehq.com/graphql/overview/start/?utm_source=openai))

### Pagination

Groove's GraphQL uses **cursor-based pagination** (Relay-style). Request a `first` count and get a `pageInfo` object with `hasNextPage` and `endCursor`. Pass `endCursor` as the `after` argument to page forward. Unlike REST's page numbers, cursor pagination is stable under concurrent writes — if a new ticket is created mid-extraction, you will not skip or double-count pages.

```graphql
query ConversationsPage($after: String) {
  conversations(first: 50, after: $after) {
    edges {
      node {
        id
        number
        subject
        state
        createdAt
        assignee { email name }
        contact { email name }
        tags { edges { node { name } } }
        messages(first: 100) {
          edges {
            node {
              body
              createdAt
              note
              author {
                ... on Agent { email }
                ... on Customer { email }
              }
              attachments { url filename }
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

Note that `note` (the boolean distinguishing public replies from private agent notes) is included in the message node above — map this field explicitly. Losing the public/private distinction breaks agent workflows in any target helpdesk.

For full thread fidelity, Groove's GraphQL docs recommend querying the `events` connection on a conversation when you need messages, ratings, state changes, and related activity alongside each other. That is more powerful but also more complex than walking REST endpoints for bulk export. ([developer.groovehq.com](https://developer.groovehq.com/graphql/references/queries/?utm_source=openai))

### What's still missing in GraphQL

Groove's developer docs note that the GraphQL API is still being built out. Based on current documentation:

- The docs warn that **Inbox and Knowledge Base APIs are still being built**, so coverage gaps compared to REST v1 are possible ([developer.groovehq.com](https://developer.groovehq.com/graphql/overview/start/?utm_source=openai))
- **Instant Replies, automation rules, and reporting data** are not exposed in either API
- **Webhooks** are only configurable through REST v1 (`POST /v1/webhooks`)

GraphQL does expose export records with states like `REQUESTED`, `QUEUED`, `EXPORTING`, `COMPLETED`, and `FAILED` — useful for monitoring if you are building against the schema. ([developer.groovehq.com](https://developer.groovehq.com/graphql/references/common/objects/export/?utm_source=openai))

### The REST v1 vs. GraphQL v2 state model mismatch

This is the most dangerous discrepancy between the two APIs for migration engineers. **REST API v1 ticket states include `pending`.** GraphQL v2's `ConversationState` enum includes `SNOOZED`, `DELETED`, and `TRASH` — but **not `PENDING`**.

If you extract historical ticket states from REST and apply that vocabulary to a GraphQL-based import or transformation layer, tickets with `pending` state will fail validation or silently default to `open`. Validate your state mapping against live account data from both APIs before writing any transformation code. Do not assume the APIs share a state vocabulary.

## REST v1 vs. GraphQL v2: Which Should You Use?

| Factor | REST API v1 | GraphQL API v2 |
|---|---|---|
| Status | Deprecated (still works) | Actively developed |
| Documentation | More examples, battle-tested | Growing, some gaps |
| Request efficiency | Chatty — HATEOAS links require following each relation | Efficient — nested queries fetch relations inline |
| Pagination | Page-based (max 50/page), unstable under concurrent writes | Cursor-based (Relay), stable under concurrent writes |
| KB access | Full CRUD | Available, Groove warns of ongoing gaps |
| Webhook management | Yes (`POST /v1/webhooks`) | No |
| State vocabulary | Includes `pending` | Includes `SNOOZED`, `DELETED`, `TRASH` — no `PENDING` |
| Community tooling | More ready-made tools | Fewer options |
| Long-term viability | Could be removed eventually | Recommended path |

**Recommendation by use case:**

- **One-time archive or migration, team comfortable with REST:** Use v1. Better-documented coverage, battle-tested, and the HATEOAS overhead is tolerable for a single run.
- **Ongoing sync or integration:** Invest in GraphQL v2. Cursor pagination is more robust, and nested queries cut total API calls significantly.
- **Large accounts (10,000+ tickets):** GraphQL's ability to fetch a conversation with messages and metadata in one query can reduce extraction time by 60–70% compared to REST's per-ticket message fetches — assuming GraphQL covers all the object types you need, which must be verified before committing.
- **KB-heavy accounts:** Verify GraphQL KB coverage against REST v1 before switching. Groove's own docs flag this as incomplete.

## Exporting Knowledge Base Articles

Groove's knowledge base has **no bulk export feature** in the admin UI. You have two API paths and one support-assisted option.

### REST API v1

1. List all knowledge bases: `GET /v1/kb`
2. For each KB, list categories: `GET /v1/kb/:kb_id/categories`
3. For each KB, retrieve articles: `GET /v1/kb/:kb_id/articles`
4. Each article object includes `title`, `body` (HTML), `slug`, `state`, `tags`, `category_id`, `meta_description`, and timestamps

Article and category search endpoints support `keyword=*` to pull everything and `unpaginated=true` — but Groove caps that mode at **100 results**. Larger KBs need real pagination. ([doc.groovehq.com](https://doc.groovehq.com/knowledge-base-articles?utm_source=openai))

If your KB is multilingual, include the translations endpoints. Groove documents translations by RFC 5646 locale code. ([doc.groovehq.com](https://doc.groovehq.com/knowledge-bases))

Groove support can also assist with KB exports in CSV format on request. ([help.groovehq.com](https://help.groovehq.com/help/groove-knowledge-base-overview?show-layout=body&utm_source=openai))

### GraphQL API v2

Query KB articles through the GraphQL endpoint with cursor-based pagination. This lets you pull article content, categories, and metadata in fewer requests — but note the API coverage caveat above; Groove's own docs flag KB as an area still being built out.

### Edge cases

**Embedded images:** KB article bodies may contain image references hosted on Groove's CDN or S3. Download these images separately and rewrite the `src` attributes in the HTML body — they will not resolve after account deactivation. This applies to both API paths and is easy to miss when validating an export on a still-active account.

**Protected KBs:** The public KB API returns **404 Not Found** when the knowledge base is password-protected or IP-restricted. If your export script gets 404s on KB endpoints, check access controls before assuming the KB is empty. Use admin-authenticated endpoints or temporarily lift restrictions during extraction. ([doc.groovehq.com](https://doc.groovehq.com/knowledge-base-public?utm_source=openai))

**KB `unpaginated=true` cap:** If you use `unpaginated=true` as a shortcut, results are capped at 100. A KB with 150 articles will silently return only 100 with no error or pagination metadata indicating truncation. Paginate explicitly for any KB over 100 articles.

## Data You Cannot Export

Some Groove data has no export path through any method:

- **Instant Replies / Canned Responses:** No API endpoint in REST v1 or GraphQL v2. Screenshot each reply or manually copy the template text before canceling your account.
- **Rules / Automations:** No API endpoint. Document each rule's trigger, conditions, and actions by hand. You will rebuild these in your target platform — and the rebuild is non-trivial since rule logic is platform-specific.
- **Round-robin assignment configurations:** Not exportable. Note the assignment logic from **Settings → Ticketing** before deactivating.
- **CSAT survey results:** Not available through the API. Export from Groove's reports UI if the option is available to your plan, or screenshot the data.
- **Report data:** Not API-accessible. Take manual exports or screenshots from the reporting dashboard. Historical metrics cannot be reconstructed from ticket data alone.

> [!NOTE]
> **Before requesting any export**, audit your Groove account. Document active integrations, Instant Reply templates, and automation rules. This data will not be in your JSON export or API pulls, and you cannot retrieve it after account closure.

## Handling Attachments

Both the built-in export and the APIs return **URLs** to attachments, not the files themselves. These URLs point to Amazon S3 and may become inaccessible after account closure.

Your extraction process must:
1. Iterate through every message and identify attachment URLs
2. Download the binary files during extraction — before account cancellation
3. Map each file to its parent ticket and message ID in your local storage structure

Groove enforces a **20 MB file size limit** per attachment and caps uploads at **25 attachments per message**. Those are upload limits, but they are useful boundary checks when planning a re-import into a target system with stricter rules. ([groovehq.com](https://www.groovehq.com/docs/attachments?utm_source=openai))

> [!WARNING]
> **Do not cancel your Groove subscription before downloading all attachments.** The URLs in your JSON export and API responses require the files to still be hosted. Download everything first, verify file counts against message-level attachment metadata, then cancel.

## API Limits and Edge Cases That Break Exports

- **Built-in export:** Single-request FIFO jobs. Large accounts can take up to 72 hours. ([help.groovehq.com](https://help.groovehq.com/help/export-your-groove-tickets?utm_source=openai))
- **REST pagination cap:** Tickets, messages, and customers top out at `per_page=50`, creating heavy API traffic for large accounts. ([doc.groovehq.com](https://doc.groovehq.com/tickets))
- **Plan-based rate limits:** 200 calls/min on Standard, 400 on Plus, 800 on Pro. ([help.groovehq.com](https://help.groovehq.com/help/billing-faq?utm_source=openai))
- **API call volume math:** A 5,000-ticket account with 6 messages/ticket requires at minimum 5,100 REST API calls (100 ticket pages + 5,000 message fetches). Add customer lookups, attachment checks, and KB extraction and the real number is 2–3× higher.
- **State model mismatch between APIs:** REST includes `pending`; GraphQL includes `SNOOZED`, `DELETED`, `TRASH` but not `PENDING`. Validate state mapping against real account data before transforming historical records.
- **`summary` is truncated.** Use message bodies from the messages endpoint, not summary previews. ([doc.groovehq.com](https://doc.groovehq.com/tickets))
- **KB `unpaginated=true` caps at 100 results** with no error or truncation signal. Paginate anything larger. ([doc.groovehq.com](https://doc.groovehq.com/knowledge-base-articles?utm_source=openai))
- **Protected KBs return 404** from public search endpoints by design — not an empty result set. ([doc.groovehq.com](https://doc.groovehq.com/knowledge-base-public?utm_source=openai))
- **HATEOAS overhead on REST v1:** Every ticket listing response returns URLs to related objects, not the objects themselves. Following those links for assignee, customer, and messages turns one page fetch into 150+ calls for a 50-ticket page with full relations.
- **Cursor stability advantage in GraphQL:** REST page-based pagination can skip or duplicate records if tickets are created or updated during a long extraction. Cursor-based pagination in GraphQL does not have this problem, making it more reliable for large accounts where extraction takes hours.

## Making a Groove Export Portable

Data portability means your export no longer depends on Groove's UI or an active account. For most teams, that means normalizing extracted data into stable entity files and storing binaries separately.

A clean portable directory structure:

```text
groove-export/
  conversations.jsonl
  messages.jsonl
  customers.jsonl
  attachments/
  mailboxes.json
  folders.json
  agents.json
  groups.json
  kb/
    knowledge_bases.json
    categories.jsonl
    articles.jsonl
    settings.json
    translations/
  manifest.json
```

Preserve Groove IDs, ticket numbers, customer identifiers, mailbox/folder IDs, author type, the `note` boolean (public reply vs. private note — losing this distinction breaks agent workflows in any target helpdesk), timestamps, tags, custom field values, attachment metadata, and raw HTML bodies.

`manifest.json` should record extraction timestamp, Groove account ID, total counts per entity type, API version used (REST v1 or GraphQL v2), and a checksum per file. This gives you an audit trail if counts don't match during import validation.

If your target system only accepts CSV, flatten threads deliberately instead of stuffing an entire conversation into one cell. [Using CSVs for SaaS Data Migrations](https://clonepartner.com/blog/blog/csv-saas-data-migration/) covers the trade-offs.

### Validation

Compare multiple totals, not just one. Groove's list endpoints return pagination totals, and `tickets/count` gives counts grouped by folder and optionally filtered by mailbox. That combination catches partial exports far better than eyeballing the size of a ZIP file. Cross-reference your extracted ticket count against `tickets/count` totals per folder — if numbers diverge by more than a rounding artifact, something failed silently. ([doc.groovehq.com](https://doc.groovehq.com/tickets))

### Transformation for relational helpdesks

If you are moving to [Zendesk](https://clonepartner.com/blog/blog/groove-to-zendesk-migration-data-mapping-apis-rate-limits/), [Zammad](https://clonepartner.com/blog/blog/groove-to-zammad-migration-a-technical-guide/), or a similar system, Groove's flat data model requires non-trivial transformation:

**The "missing organization" problem:** Groove's data model is customer-centric — it does not have an organization or company layer. Zendesk and Zammad both group users by Organization, and tickets are associated with organizations through user membership. To reconstruct this structure, you typically analyze customer email domains (e.g., grouping all `@acmecorp.com` addresses) and programmatically create organizations in the target system, linking users before importing tickets. This logic must be built — it does not come from Groove's export. Accounts with individual consumers rather than B2B customers may not need this, but B2B support operations almost always do.

**State mapping:** Groove's ticket states must map to your target system's exact status values. Get this wrong and all imported tickets default to "Open," destroying historical reporting. Map every Groove state explicitly — and account for the REST vs. GraphQL vocabulary divergence if your extraction used both APIs.

**The `note` boolean:** Groove messages have a `note` boolean (true = private agent note, false = public reply). Zendesk calls this `public` (the inverse). Zammad uses ticket article `internal` flag. Invert and rename this field correctly — if you lose it, all private notes become public-facing replies in your target system.

**Custom fields:** If your Groove account uses conversation-level custom fields, inspect them via the API's `customFieldValues` before assuming the default export captures everything you need. ([help.groovehq.com](https://help.groovehq.com/help/custom-fields?utm_source=openai)) Custom fields do not have a universal import format — you will define equivalent fields in the target system first, then map by semantic meaning, not field ID.

**HTML body handling:** Groove stores message bodies as HTML. If your target system expects markdown (Zammad, for example, can use both), convert HTML to markdown during transformation rather than importing raw HTML and letting the target render it inconsistently.

## Delta Sync and Cutover Planning

Migrations are not instantaneous. If extraction and loading takes three days, your team is still working in Groove during that window. You need a strategy for capturing changes made after your initial extraction snapshot.

**API polling approach:** After the initial extraction, run a final script querying for tickets with `updatedAt` after your cutoff date. Extract only new or modified tickets and push them to the target system. Note: filter on `updatedAt`, not `createdAt` — you need to catch existing tickets that received a new reply or state change during the migration window, not just new tickets created after your snapshot.

**Webhook approach:** Groove documents webhook events for ticket lifecycle changes, customer replies, notes, and agent replies. Set up webhooks before starting extraction to capture changes in real time until cutover is complete. Webhooks are configured through the REST v1 API (`POST /v1/webhooks`) or the Groove UI. ([doc.groovehq.com](https://doc.groovehq.com/webhooks?utm_source=openai))

**Practical cutover sequence:**
1. Complete initial full extraction (all tickets, customers, KB, attachments)
2. Load into target system in staging mode
3. Validate counts and sample records
4. Run delta extraction covering the extraction window
5. Load delta records
6. Switch DNS/routing to new helpdesk
7. Verify with a few live test tickets

## Common Pitfalls

1. **Forgetting to download attachments.** Attachment URLs point to S3 and become inaccessible after account deletion. Download every binary during extraction and verify file counts against attachment metadata before canceling.

2. **Relying on the `summary` field.** The summary is intentionally truncated. Always fetch the full message body from the `body` field in the messages endpoint response.

3. **Not extracting customers separately.** The built-in export embeds customer info in each ticket but does not produce a clean customer list. For deduplication, CRM import, or organization mapping, pull customers explicitly via `GET /v1/customers`.

4. **Ignoring KB articles.** The built-in export does not include knowledge base content. Export it separately through the API before closing your account. KB embedded images must also be downloaded — they will not resolve after account deactivation.

5. **Missing the `note` flag on messages.** Groove distinguishes between public replies and private notes via the `note` boolean. Map this field correctly and invert/rename it for target platforms that use the opposite convention (e.g., Zendesk's `public` field). Losing the public/private distinction exposes private agent notes to customers.

6. **Running extraction against a live account without throttling.** The rate limit is shared with your support team. An unthrottled extraction script at 200 calls/min on Standard plan will cause 429 errors for agents during peak hours. Run heavy extractions during off-hours.

7. **Ignoring the REST vs. GraphQL state mismatch.** REST includes `pending`; GraphQL includes `SNOOZED`, `DELETED`, `TRASH` but not `PENDING`. If you mix APIs or transform data without accounting for this, tickets silently land in the wrong state. Validate against real account data before writing transformation code.

8. **Using `unpaginated=true` for large KBs.** This parameter caps results at 100 with no truncation signal. A KB with 200 articles appears to fully export at 100 records. Paginate explicitly.

9. **No checkpoint/resume logic in extraction scripts.** A network failure or rate limit cascade at ticket 4,800 of 5,000 restarts extraction from zero. Build checkpoint files that record the last successfully processed ticket number or cursor position.

10. **Skipping the organization mapping step for B2B accounts.** Groove has no organization layer. Zendesk and Zammad do. Without explicit organization creation and user linking before ticket import, all tickets arrive as orphaned from any company context.

## Pre-Extraction Checklist

Before running any export or API script:

- **Audit tags:** Delete unused tags. Do not migrate garbage data.
- **Identify spam:** Decide whether to import spam tickets or filter them out during extraction.
- **Document non-exportable data:** Screenshot Instant Replies, automation rules, round-robin configs, and CSAT results.
- **Test rate limits:** Run a small extraction of ~1,000 tickets to calculate actual processing time and refine backoff logic before committing to a full run.
- **Provision attachment storage:** Set up an S3 bucket or equivalent to temporarily store downloaded attachments. Estimate storage: multiply average attachment size by `total_messages × average_attachments_per_message`.
- **Check KB access controls:** If your KB is password-protected or IP-restricted, the public API returns 404. Use admin endpoints or adjust restrictions before extraction.
- **Inspect custom fields:** Check if your account uses conversation-level custom fields and verify they appear in API responses before assuming the default export is complete.
- **Verify state vocabulary:** Pull a sample of tickets from both REST and GraphQL and compare state values. Document any discrepancies before writing transformation code.
- **Plan checkpoint/resume:** Decide on your checkpointing strategy before starting a large extraction — not after a failure.

## How Long Does a Full Groove Export Take?

| Account Size | Built-in Export | REST API v1 (Standard plan, 200 calls/min) | GraphQL v2 |
|---|---|---|---|
| < 1,000 tickets | Minutes to hours | 30–60 min | 15–30 min |
| 1,000–10,000 tickets | Hours | 2–6 hours | 1–3 hours |
| 10,000+ tickets | Up to 72 hours | 8–24 hours | 4–12 hours |

These estimates assume conservative throttling, average 6 messages per ticket, minimal attachments, and no extraction failures requiring restarts. REST v1 timing assumes Standard plan rate limits (200 calls/min). On Pro (800 calls/min), REST times compress by roughly 4×. GraphQL timing assumes nested message queries reduce per-ticket round trips to 1–2 vs. REST's 2–3.

Actual times depend on messages per ticket, attachment counts, KB size, and how aggressively you approach rate limits without triggering 429 cascades.

## When the Easy Export Stops Being Enough

Groove's built-in export handles archiving and simple backups. The complexity compounds when:

- You have 10,000+ tickets and need selective extraction (by mailbox, date range, or agent)
- You need KB articles, customer records, and conversations as a unified, deduplicated dataset
- Your target platform requires data transformations (timestamp normalization, HTML-to-markdown conversion, attachment re-hosting)
- You need zero-downtime cutover with delta sync for tickets created during migration
- You are hitting rate limits that stretch extraction across days
- You have B2B customers and need to reconstruct the organization layer before importing

At this point, you are doing ETL engineering, not clicking an export button. Build a staging environment, map fields to your target schema field-by-field (including edge cases like the `note` boolean inversion), test with a subset of 50–100 tickets including attachments, and verify attachment integrity and count before running a full migration.

For target-specific guidance, see [Groove to Zendesk Migration](https://clonepartner.com/blog/blog/groove-to-zendesk-migration-data-mapping-apis-rate-limits/) or [Groove to Zammad Migration](https://clonepartner.com/blog/blog/groove-to-zammad-migration-a-technical-guide/).

> Need a clean Groove export or a full migration to another helpdesk? ClonePartner handles the API orchestration, rate limit management, attachment downloading, data validation, and loading into your target platform — typically in days, not weeks. Book a free 30-minute call with our engineers.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### How do I export all my data from Groove?

Use the built-in export (Settings → Company → More → Exports) for conversation history as GZIP-compressed JSON. For customers, agents, KB articles, and other objects, use the REST API v1 or GraphQL API v2. There is no single button that exports everything.

### Does Groove support CSV export?

No. Groove's built-in export produces GZIP-compressed JSON only, following the v1 Tickets API format. For KB articles, Groove support can assist with a CSV export. If you need CSV for ticket data, you must extract via the API and convert the JSON yourself.

### What are Groove's API rate limits?

Groove's published API rate limits are plan-based: 200 calls per minute on Standard, 400 on Plus, and 800 on Pro. The API returns HTTP 429 when exceeded. Implement exponential backoff with jitter and avoid running extraction scripts during peak support hours.

### Can I export Groove knowledge base articles?

Not through the admin UI. There is no bulk KB export. Use the REST API (GET /v1/kb/:id/articles) or GraphQL API to retrieve articles programmatically, including HTML body, categories, tags, and metadata. Groove support can also assist with a CSV export of KB content.

### Should I use Groove's REST API or GraphQL API for data export?

REST v1 is deprecated but well-documented and has more complete coverage today. GraphQL v2 is actively developed and more efficient for large exports due to nested queries, but Groove's docs warn that Inbox and KB APIs are still being built. For a full, reliable extraction, REST v1 is the safer starting point.
