> ## Documentation Index
> Fetch the complete documentation index at: https://docs.armin.cx/llms.txt
> Use this file to discover all available pages before exploring further.

# Ticket exports

> Export tickets and their messages as CSV files in the background, by saved view, date range, or ticket IDs.

Use the Chatarmin CX Public API to load many tickets and their messages in one go. You start an export with one request, check its status until it is ready, and download the result through a time-limited link. You no longer read messages one ticket at a time.

## How it works

1. `POST /public/v1/ticket-exports` creates the export and returns it right away with status `queued`.
2. The export runs in the background, so it can cover as many tickets as you choose, up to 50,000.
3. `GET /public/v1/ticket-exports/{exportId}` returns the status. When it is `completed`, the response also holds a `download_url`.
4. You download the file from `download_url`.

Both endpoints require the `tickets:export` scope. Create a key in [Settings → API](https://armin.cx/app/_/settings/api). An agent-linked key can only export saved views that agent can see in the inbox, as for [`GET /views`](https://docs.armin.cx/api/filtering-and-saved-views). A key without a linked agent can export any view in the organization.

## Nightly sync

Export the tickets of a saved view, with their messages, once a night.

**1. Find the view**

Call `GET /public/v1/views` and keep the `id` of the view that holds the tickets you sync. See [Filtering and saved views](https://docs.armin.cx/api/filtering-and-saved-views.md).

**2. Create the export**

```bash theme={null}
curl -X POST 'https://api.armin.cx/public/v1/ticket-exports' \
  -H "cx-api-key: $CX_API_KEY" \
  -H 'content-type: application/json' \
  -d '{ "view_id": "00000000-0000-4000-8000-0000000000a1", "include_messages": true }'
```

The response has status `202` and the export in status `queued`.

**3. Poll until it is ready**

```bash theme={null}
curl "https://api.armin.cx/public/v1/ticket-exports/$EXPORT_ID" \
  -H "cx-api-key: $CX_API_KEY"
```

There are no webhooks. Poll every few seconds until `status` is `completed` or `failed`.

**4. Download the file**

Download `data.download_url` and unzip it. Each status call on a completed export returns a fresh link, so request the status again if a link has expired.

## Backfill

Export every ticket created in a date range. Dates are `YYYY-MM-DD` in UTC, both days are included, and they filter on the ticket creation date.

```bash theme={null}
curl -X POST 'https://api.armin.cx/public/v1/ticket-exports' \
  -H "cx-api-key: $CX_API_KEY" \
  -H 'content-type: application/json' \
  -d '{ "date_from": "2026-08-01", "date_to": "2026-08-14", "include_messages": true }'
```

One export holds at most 50,000 tickets. If a range matches more, the request fails with `EXPORT_TOO_LARGE`. Split the date range into smaller ranges and create one export for each.

## Request fields

Choose exactly one of the three selection modes. Unknown fields are rejected with `400 VALIDATION_ERROR`.

| Field | Contract |
| - | - |
| `view_id` | UUID of a saved view. Exports every ticket in the view. |
| `ticket_ids` | 1 to 500 ticket UUIDs. If any ID does not exist in your organization, the request fails with `400 VALIDATION_ERROR` and lists them in `details.invalid_ticket_ids`. |
| `date_from`, `date_to` | Both required together, `YYYY-MM-DD`, UTC, inclusive, `date_from` on or before `date_to`. |
| `include_messages` | Boolean, default `false`. Adds `messages.csv`. |
| `columns` | Optional list of ticket columns for `tickets.csv`. Defaults to the standard columns. |

Default columns: `ticket_number`, `subject`, `contact_first_name`, `contact_last_name`, `contact_email`, `contact_phone`, `status`, `assigned_agent_name`, `created_at`, `updated_at`.

Optional columns: `priority`, `channel`, `channel_name`, `tags`, `last_message_at`, `message_count`, `ticket_url`, `csat_score`, `csat_comment`, `csat_survey_sent`, `csat_submitted_at`, `created_date`, `created_time`, `language`, `social_thread_type`, `social_post_type`, `team_name`, `first_resolved_at`, `resolved_at`.

## Read the export

```json theme={null}
{
  "success": true,
  "data": {
    "id": "01a1adf2-20b3-72db-a67c-b1fc3cb46be2",
    "status": "completed",
    "created_at": "2026-11-05T23:01:28.884Z",
    "completed_at": "2026-11-05T23:01:38.519Z",
    "expires_at": "2026-11-12T23:01:38.515Z",
    "ticket_count": 144,
    "download_url": "https://…",
    "error": null
  }
}
```

| Field | Meaning |
| - | - |
| `id` | Export ID. Use it in `GET /ticket-exports/{exportId}`. |
| `status` | `queued`, `processing`, `completed`, `failed`, or `expired`. |
| `created_at`, `completed_at` | ISO-8601 timestamps. `completed_at` is `null` until the export finishes. |
| `expires_at` | When the file is deleted. `null` until the export completes. |
| `ticket_count` | Number of tickets in the file. `null` until the export completes. |
| `download_url` | Only set when `status` is `completed`. Valid for 1 hour. |
| `error` | Only set when `status` is `failed`: `Export failed. Please create a new export.` |

| Status | Meaning |
| - | - |
| `queued` | The export waits for a free worker. |
| `processing` | The file is being built. |
| `completed` | The file is ready. Download it from `download_url`. |
| `failed` | The export could not be built. Create a new export. |
| `expired` | The file is past its 7-day retention and is no longer available. Create a new export. |

## The file

When `include_messages` is `true`, the download is a ZIP with `tickets.csv` and `messages.csv`. Otherwise the download is a single `tickets.csv`. Files are UTF-8 without a byte order mark. A selection that matches no tickets still completes, with files that contain only the header row.

Tickets are ordered by creation date, oldest first. Messages are ordered by ticket, then by creation date, oldest first. Internal notes and deleted messages are not included.

`messages.csv` has these columns:

| Column | Value |
| - | - |
| `ticket_id`, `ticket_number` | The ticket the message belongs to. |
| `created_at` | ISO-8601 UTC timestamp. |
| `direction` | `inbound` for customer messages, `outbound` for all others. |
| `author_type` | `customer`, `agent`, or `ai`. |
| `author_name` | Name of the sender. |
| `ai_agent_name` | Name of the AI agent for `ai` messages, otherwise empty. |
| `body_text` | The message as plain text, the same as `text` in [`GET /tickets/:ticketNumber/messages`](https://docs.armin.cx/api/endpoints), without a length limit. |
| `message_id` | Message ID. |

All timestamps in both files are ISO-8601 in UTC. There is no timezone option.

## Limits

| Limit | Value |
| - | - |
| Tickets per export | 50,000. Above that the request fails with `EXPORT_TOO_LARGE`. |
| `ticket_ids` per request | 500 |
| Active exports per organization | 2 (`queued` or `processing`). A third request fails with `EXPORT_LIMIT_REACHED`. |
| File retention | 7 days. After that the status is `expired`. |
| Download link | A new link on every status call, valid for 1 hour. |
| Create requests | 30 requests / minute |
| Status requests | 120 requests / minute |

See [Rate limits](https://docs.armin.cx/api/rate-limits.md).

## Differences from the legacy export

`POST /public/tickets/export` stays available. If you move to the v1 export, these things change.

| | Legacy export | v1 export |
| - | - | - |
| Delivery | The file is the response of the request | Background job: create, poll, download |
| Largest export | 500 tickets | 50,000 tickets |
| Deleted messages | Included | Not included |
| `direction` | `in` / `out` | `inbound` / `outbound` |
| `author_type` | `customer` / `business` / `ai` | `customer` / `agent` / `ai` |
| Timestamps | `yyyy-MM-dd hh:mm:ss a zzz` in a requested timezone | ISO-8601 UTC, no `timezone` option |
| `body_text` | Cleaned text | The same plain text as v1 message `text`, without a length limit |
| No tickets matched | `404` | Completes with files that contain only the header row |
| Selection | `ticketIds` or `date_from` and `date_to` | `ticket_ids`, `date_from` and `date_to`, or `view_id` |
| Formats | `csv`, `excel`, `eml` | CSV only |

Not supported in v1: `excel`, `eml`, `order`, `timezone`, and `include_ticket_fields`. Column names in `tickets.csv` and `messages.csv` are the same as in the legacy export.

## Errors

| Response | When |
| - | - |
| `400 VALIDATION_ERROR` | Not exactly one selection mode, unknown fields or columns, `date_from` after `date_to`, or `ticket_ids` that do not exist (`details.invalid_ticket_ids`). |
| `400 EXPORT_TOO_LARGE` | The selection matches more than 50,000 tickets (`details.max_tickets`). Split the date range. |
| `403 FORBIDDEN_SCOPE` | The key does not have `tickets:export`. |
| `404 VIEW_NOT_FOUND` | The view does not exist or is not visible to the key's agent. |
| `404 EXPORT_NOT_FOUND` | The export does not exist in your organization. |
| `429 EXPORT_LIMIT_REACHED` | Two exports are already running, or another export is being created. Wait and try again. |
| `429 RATE_LIMIT_EXCEEDED` | You sent too many requests. Wait for `Retry-After`. |

See [Errors](https://docs.armin.cx/api/errors.md) for the error format. Match on `code`, not on `message`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.