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

# List tickets

> GET /v1/tickets — list tickets with status filters, date ranges, and cursor pagination

Returns a paginated list of tickets in your organization, ordered by the `sort` field. A ticket is the case container: it always has one primary conversation with the requester, plus any side conversations. To export everything since a point in time, page this endpoint with an `updatedAt[gte]` filter, then fetch each changed ticket's threads.

```http theme={null}
GET /v1/tickets
```

## Authentication

Bearer token in the `Authorization` header. See [Authentication](/api-reference/authentication).

```http theme={null}
Authorization: Bearer rfl_live_...
```

## Query parameters

### Pagination

<ParamField query="limit" type="integer" default="20">
  Number of tickets per page. Range `1`–`100`.
</ParamField>

<ParamField query="startingAfter" type="string">
  Opaque cursor from the previous response's `nextCursor`. See [Pagination](/api-reference/pagination).
</ParamField>

### Sorting

<ParamField query="sort" type="string" default="-updatedAt">
  Field and direction. Prefix with `-` for descending. One of:

  * `createdAt`
  * `-createdAt`
  * `updatedAt`
  * `-updatedAt`
</ParamField>

### Filters

<ParamField query="status" type="string | string[]">
  Filter by ticket status. Repeat the parameter to match multiple values
  (e.g. `?status=open&status=snoozed`). Matching is exact — `resolved` does
  not include `assumed_resolved`. One of:

  * `open`
  * `snoozed`
  * `resolved`
  * `assumed_resolved`
  * `closed`
</ParamField>

### Date range

Use either `createdAt` or `updatedAt` ranges — not both. Mixing them returns `422 conflicting_date_range`.

<ParamField query="createdAt[gte]" type="string (ISO 8601)">
  Only return tickets created on or after this timestamp.
</ParamField>

<ParamField query="createdAt[lte]" type="string (ISO 8601)">
  Only return tickets created on or before this timestamp.
</ParamField>

<ParamField query="updatedAt[gte]" type="string (ISO 8601)">
  Only return tickets updated on or after this timestamp.
</ParamField>

<ParamField query="updatedAt[lte]" type="string (ISO 8601)">
  Only return tickets updated on or before this timestamp.
</ParamField>

## Response

Returns a [list envelope](/api-reference/pagination#response-shape) containing `Ticket` objects.

### The Ticket object

<ResponseField name="id" type="string">
  Unique identifier prefixed with `ti_`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `"ticket"`.
</ResponseField>

<ResponseField name="title" type="string">
  Ticket subject or summary.
</ResponseField>

<ResponseField name="status" type="string">
  Ticket lifecycle status. One of the values listed under the `status` query parameter.
</ResponseField>

<ResponseField name="requester" type="object">
  The contact the ticket is for.

  <Expandable title="properties">
    <ResponseField name="id" type="string">Contact ID prefixed with `con_`.</ResponseField>
    <ResponseField name="name" type="string | null">Contact display name.</ResponseField>

    <ResponseField name="email" type="object | null">
      The contact's email address, or `null` if none is known.

      <Expandable title="properties">
        <ResponseField name="email" type="string">The email address.</ResponseField>
        <ResponseField name="verified" type="boolean">`true` when ownership of the address is proven — verified by the contact or asserted by your own backend via [user identification](/user-identification). `false` for an address the contact merely typed in.</ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="tags" type="array">
  Tags applied to the ticket.

  <Expandable title="properties">
    <ResponseField name="id" type="string">Tag ID.</ResponseField>
    <ResponseField name="name" type="string">Tag name.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="topics" type="array">
  Topics applied to the ticket.

  <Expandable title="properties">
    <ResponseField name="id" type="string">Topic ID.</ResponseField>
    <ResponseField name="name" type="string">Topic name.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="estimatedScore" type="integer | null">
  AI-estimated customer-satisfaction score for the ticket, from `1` (poor) to
  `5` (great). `null` until the ticket has been analyzed — analysis runs after
  the ticket is resolved.
</ResponseField>

<ResponseField name="conversations" type="array">
  References to every conversation on the ticket, ordered by creation. Exactly
  one has `role: "primary"`. Fetch a thread via
  [List messages](/api-reference/conversations/messages) — no ticket detail
  call needed.

  <Expandable title="properties">
    <ResponseField name="id" type="string">Conversation ID prefixed with `conv_`.</ResponseField>
    <ResponseField name="role" type="string">`primary` (the requester's thread) or `side` (an isolated thread with another party).</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="createdAt" type="string">
  ISO 8601 UTC timestamp of ticket creation.
</ResponseField>

<ResponseField name="updatedAt" type="string">
  ISO 8601 UTC timestamp of the most recent update.
</ResponseField>

## Examples

### List the most recently updated tickets

<CodeGroup>
  ```bash curl theme={null}
  curl "https://api.replyful.com/v1/tickets?limit=5" \
    -H "Authorization: Bearer rfl_live_..."
  ```

  ```ts Node.js theme={null}
  const res = await fetch(
    "https://api.replyful.com/v1/tickets?limit=5",
    { headers: { Authorization: `Bearer ${process.env.REPLYFUL_API_KEY}` } },
  );
  const { data } = await res.json();
  ```

  ```python Python theme={null}
  import os, requests

  res = requests.get(
      "https://api.replyful.com/v1/tickets",
      headers={"Authorization": f"Bearer {os.environ['REPLYFUL_API_KEY']}"},
      params={"limit": 5},
  )
  res.raise_for_status()
  data = res.json()["data"]
  ```
</CodeGroup>

```json Response theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "ti_8kNwq2Tt7yGkX9wM4bDfP",
      "object": "ticket",
      "title": "Refund request",
      "status": "open",
      "requester": {
        "id": "con_9dKmT3xWq6bYrN8cJfA2eH4gp",
        "name": "Ada Lovelace",
        "email": { "email": "ada@example.com", "verified": true }
      },
      "tags": [
        { "id": "tag_7fQwXk3mB9tRc2Ns6", "name": "vip" }
      ],
      "topics": [
        { "id": "tpc_4mHbW8xKq2eYd7Tf3", "name": "Billing" }
      ],
      "estimatedScore": null,
      "conversations": [
        { "id": "conv_x7Kw2mQb9tRfN4c", "role": "primary" },
        { "id": "conv_p3Tf8xWq2mKb6rN", "role": "side" }
      ],
      "createdAt": "2026-04-29T14:30:00Z",
      "updatedAt": "2026-04-29T14:32:00Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "eyJzIjoiMjAyNi0wNC0yOVQxNDozMjowMC4wMDBaIiwiaSI6InRpXzhrTndxMlR0N3lHa1g5d000YkRmUCJ9",
  "url": "/v1/tickets"
}
```

### Export everything updated since a timestamp

```bash theme={null}
curl --get "https://api.replyful.com/v1/tickets" \
  -H "Authorization: Bearer rfl_live_..." \
  --data-urlencode "updatedAt[gte]=2026-04-01T00:00:00Z" \
  --data-urlencode "sort=updatedAt" \
  --data-urlencode "limit=100"
```

### Walk every page

See the [pagination guide](/api-reference/pagination#walking-through-every-page) for a copy-paste loop in Node and Python.

## Error responses

| Status | `error.code`              | When                                                                       |
| ------ | ------------------------- | -------------------------------------------------------------------------- |
| `401`  | `missing_api_key`         | The `Authorization` header is absent.                                      |
| `401`  | `invalid_api_key`         | The token is malformed, unknown, or archived.                              |
| `422`  | `invalid_query_parameter` | A query parameter failed validation. The `param` field names the offender. |
| `422`  | `invalid_cursor`          | The `startingAfter` cursor could not be decoded.                           |
| `422`  | `conflicting_date_range`  | Both `createdAt[...]` and `updatedAt[...]` were provided.                  |
| `429`  | `too_many_requests`       | Rate limit exceeded. Honor `Retry-After`.                                  |

See [Errors](/api-reference/errors) for the full error envelope reference.
