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

# Esquema de filtro de clientes

> Referencia completa del parámetro `filter` de `GET /v1/customers` en formato `qs`.

Este documento describe el esquema del parámetro `filter` para `GET /v1/customers`.

`filter` no se envía como JSON literal: se envía como query string codificado con `qs`.

## Formato general

Ejemplo mínimo:

```http theme={null}
GET /v1/customers?filter[name][contains]=juan
```

Ejemplo con composición lógica:

```http theme={null}
GET /v1/customers?filter[AND][0][email][contains]=@empresa.com&filter[OR][0][noPayments]=true
```

## Esquema base

Conceptualmente, el esquema es:

```json theme={null}
{
  "id": "StringFilter",
  "createdAt": "DateTimeFilter",
  "updatedAt": "DateTimeFilter",
  "name": "StringFilter",
  "archivedAt": "NullableDateTimeFilter",
  "email": "StringFilter",
  "phoneNumber": "StringFilter",
  "secondaryPhoneNumber": "StringFilter",
  "username": "StringFilter",
  "tags": "TagListFilter",
  "invoices": "InvoiceListFilter",
  "receivedAnnouncements": "AnnouncementRecipientListFilter",
  "metadataItem": "MetadataItemFilter",
  "metadata": "MetadataListFilter",
  "assignee": "MemberFilter",
  "noPayments": "BooleanLikeFilter",
  "hasTaxProfiles": "BooleanLikeFilter",
  "AND": ["CustomerFilter"],
  "OR": ["CustomerFilter"]
}
```

## Campos soportados

Puedes usar estos campos de primer nivel en `filter`:

* `id`
* `createdAt`
* `updatedAt`
* `name`
* `archivedAt`
* `email`
* `phoneNumber`
* `secondaryPhoneNumber`
* `username`
* `tags`
* `invoices`
* `receivedAnnouncements`
* `metadataItem`
* `metadata`
* `assignee`
* `noPayments`
* `hasTaxProfiles`
* `AND`
* `OR`

## Operadores comunes

Los operadores disponibles dependen del tipo de campo:

* String (`name`, `email`, `username`, etc.): `equals`, `contains`, `startsWith`, `endsWith`, `in`, `notIn`
* Fecha (`createdAt`, `updatedAt`, `archivedAt`): `equals`, `gt`, `gte`, `lt`, `lte`
* Booleano (`noPayments`, `hasTaxProfiles`): `equals`

Para filtros relacionales (`tags`, `invoices`, `metadata`, `assignee`) usa operadores de lista/relación como `some`, `every`, `none`, según el objeto anidado.

## Ejemplos

### Filtrar por nombre

```http theme={null}
GET /v1/customers?filter[name][contains]=Alicia
```

### Filtrar por correo exacto

```http theme={null}
GET /v1/customers?filter[email][equals]=alicia@ejemplo.com
```

### Filtrar por clientes sin pagos

```http theme={null}
GET /v1/customers?filter[noPayments]=true
```

### Combinar filtros con AND

```http theme={null}
GET /v1/customers?filter[AND][0][name][contains]=Alicia&filter[AND][1][hasTaxProfiles]=true
```

### Combinar `AND` y `OR`

```http theme={null}
GET /v1/customers?filter[AND][0][name][contains]=Alicia&filter[AND][1][OR][0][email][contains]=@empresa.com&filter[AND][1][OR][1][noPayments]=true
```

### Orden y paginacion junto con filtros

```http theme={null}
GET /v1/customers?filter[name][contains]=Alicia&orderBy=createdAt=desc&take=20&skip=0
```

## Construccion recomendada con `qs`

```ts theme={null}
import qs from 'qs';

const filter = {
  AND: [{ name: { contains: 'Alicia' } }, { hasTaxProfiles: { equals: true } }],
};

const query = qs.stringify({ filter, take: 20, skip: 0 }, { encodeValuesOnly: true });
// filter[AND][0][name][contains]=Alicia&filter[AND][1][hasTaxProfiles][equals]=true&take=20&skip=0
```

## Recomendaciones

* Usa un serializador `qs` para evitar errores de encoding y de arrays anidados.
* Empieza con un filtro simple y agrega `AND`/`OR` solo cuando sea necesario.
* Combina filtros con `take`, `skip` y `orderBy` para paginacion consistente.
