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

# Contacts Search API

> Advanced filtering, sorting, and pagination for searching contacts in the HoopAI platform.

<Note>
  Due to the complexity of advanced filtering, updates may take a few seconds to appear in search results.
</Note>

The Contacts Search API lets you search for contacts using extensive filtering, sorting, and pagination options.

## Endpoint

```
POST /contacts/search
```

***

## Request body

| Parameter     | Type   | Required | Description                                                                                                      |
| ------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------- |
| `locationId`  | string | Yes      | Location ID to search within.                                                                                    |
| `page`        | number | No       | Page number for standard pagination. Do not use with `searchAfter`.                                              |
| `pageLimit`   | number | Yes      | Results per page (max 500).                                                                                      |
| `searchAfter` | array  | No       | Cursor for pagination beyond 10,000 records. Value returned in each response.                                    |
| `filters`     | array  | No       | Filter criteria — see [Filters](#filters) below.                                                                 |
| `sort`        | array  | No       | Sort criteria — see [Sort](#sort) below.                                                                         |
| `query`       | string | No       | Full-text search string (max 75 characters). Runs against searchable fields configured in Custom Field Settings. |

### Sample request

```json theme={null}
{
  "locationId": "5DP41231LkQsiKESj6rh",
  "page": 1,
  "pageLimit": 20,
  "filters": [
    { "field": "dnd", "operator": "eq", "value": true },
    {
      "group": "OR",
      "filters": [
        { "field": "firstNameLowerCase", "operator": "eq", "value": "alex" },
        { "field": "lastNameLowerCase", "operator": "eq", "value": "peter" }
      ]
    }
  ],
  "sort": [{ "field": "firstNameLowerCase", "direction": "desc" }],
  "query": "tom"
}
```

***

## Pagination

### Standard pagination (`page` + `pageLimit`)

* Fetches up to **10,000 records** total.
* Maximum **500 records** per request.
* Use `page` and `pageLimit` to paginate through results.

### Cursor-based pagination (`searchAfter` + `pageLimit`)

* Required for more than 10,000 records.
* Use the `searchAfter` value returned in each response as the start point for the next page.
* Do **not** include `page` when using `searchAfter`.

***

## Filters

Filters can be applied individually or grouped with **AND** / **OR** logical operators. Each filter has three components:

* **Field** — the contact attribute to filter on.
* **Operator** — the comparison to apply.
* **Value** — the value to compare against.

<Info>
  By default, all filters in the `filters` array are combined with AND logic.
</Info>

### Sample filter payload

```json theme={null}
{
  "filters": [
    {
      "group": "AND",
      "filters": [
        { "field": "firstNameLowerCase", "operator": "contains", "value": "John" },
        { "field": "email", "operator": "exists" }
      ]
    },
    {
      "group": "OR",
      "filters": [
        { "field": "city", "operator": "eq", "value": "New York" },
        { "field": "state", "operator": "eq", "value": "California" }
      ]
    }
  ]
}
```

### Supported operators

| Operator       | Definition                           | Value type              | Character limit |
| -------------- | ------------------------------------ | ----------------------- | --------------- |
| `eq`           | Equals                               | Number, String, Boolean | 75              |
| `not_eq`       | Not equals                           | Number, String, Boolean | 75              |
| `contains`     | Contains (no special characters)     | String                  | 75              |
| `not_contains` | Not contains (no special characters) | String                  | 75              |
| `exists`       | Has a value                          | — (omit value)          | —               |
| `not_exists`   | Has no value                         | — (omit value)          | —               |
| `range`        | Range (`gte`/`lte`/`gt`/`lt`)        | Object                  | 75              |

### Supported filter fields

#### Contact information

| Display name           | Field name             | Supported operators                                                |
| ---------------------- | ---------------------- | ------------------------------------------------------------------ |
| Contact ID             | `id`                   | `eq`, `not_eq`                                                     |
| Contact Name           | `contactName`          | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Address                | `address`              | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Assigned To            | `assignedTo`           | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Business Name          | `businessName`         | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| City                   | `city`                 | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Country                | `country`              | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Company Name           | `companyName`          | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Created At             | `dateAdded`            | `range`, `exists`, `not_exists`                                    |
| Updated At             | `dateUpdated`          | `range`, `exists`, `not_exists`                                    |
| DND                    | `dnd`                  | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Email                  | `email`                | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Followers              | `followers`            | `eq`, `not_eq`, `exists`, `not_exists`                             |
| First Name (lowercase) | `firstNameLowerCase`   | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Last Name (lowercase)  | `lastNameLowerCase`    | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Is Valid WhatsApp      | `isValidWhatsapp`      | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Last Email Clicked     | `lastEmailClickedDate` | `range`, `exists`, `not_exists`                                    |
| Last Email Opened      | `lastEmailOpenedDate`  | `range`, `exists`, `not_exists`                                    |
| Phone                  | `phone`                | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Postal Code            | `postalCode`           | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Source                 | `source`               | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| State                  | `state`                | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Tags                   | `tags`                 | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Timezone               | `timezone`             | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Contact Type           | `type`                 | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Is Valid Email         | `validEmail`           | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Website                | `website`              | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Date of Birth          | `dateOfBirth`          | `range`, `exists`, `not_exists`                                    |

#### Contact activity

| Display name        | Field name          | Supported operators                    |
| ------------------- | ------------------- | -------------------------------------- |
| Last Appointment    | `lastAppointment`   | `range`, `exists`, `not_exists`        |
| Workflow (Active)   | `activeWorkflows`   | `eq`, `not_eq`, `exists`, `not_exists` |
| Workflow (Finished) | `finishedWorkflows` | `eq`, `not_eq`, `exists`, `not_exists` |

#### Opportunity (nested filter)

| Display name    | Field + Sub-field                   | Operators                              |
| --------------- | ----------------------------------- | -------------------------------------- |
| Pipeline        | `opportunities` + `pipelineId`      | `eq`, `not_eq`, `exists`, `not_exists` |
| Pipeline Stage  | `opportunities` + `pipelineStageId` | `eq`, `not_eq`, `exists`, `not_exists` |
| Pipeline Status | `opportunities` + `status`          | `eq`, `not_eq`, `exists`, `not_exists` |

#### Custom fields

| Field type                                    | Field name format                     | Operators                                                          |
| --------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------ |
| Text, Large Text, Single Option, Radio, Phone | `customFields.{custom_field_id}`      | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |
| Checkbox, Multiple Options                    | `customFields.{custom_field_id}`      | `eq`, `not_eq`, `exists`, `not_exists`                             |
| Numerical, Monetary                           | `customFields.{custom_field_id}`      | `range`, `eq`, `not_eq`, `exists`, `not_exists`                    |
| Date                                          | `customFields.{custom_field_id}`      | `range`, `exists`, `not_exists`                                    |
| Textbox List                                  | `customFields.{field_id}.{option_id}` | `eq`, `not_eq`, `contains`, `not_contains`, `exists`, `not_exists` |

***

## Sort

You can combine up to 2 sort criteria.

```json theme={null}
[
  { "field": "dateAdded", "direction": "desc" },
  { "field": "firstNameLowerCase", "direction": "asc" }
]
```

### Supported sort fields

| Display name           | Field name           |
| ---------------------- | -------------------- |
| First Name (lowercase) | `firstNameLowerCase` |
| Last Name (lowercase)  | `lastNameLowerCase`  |
| Business Name          | `businessName`       |
| Date Created           | `dateAdded`          |
| Date Updated           | `dateUpdated`        |
| Email                  | `email`              |
| DND                    | `dnd`                |
| Source                 | `source`             |
