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

# GET /v1/crm/customers

> List the customers of your company, with free-text search and phone lookup.

Returns a paginated list of customers. Results cover the whole **company**, not just the project your token belongs to — see [Tenancy is implicit](/dev/crm/overview#tenancy-is-implicit).

## Endpoint

```
GET https://api.keebai.com/v1/crm/customers
```

## Required scope

`crm:customers:read`

## Headers

| Header          | Required | Value                    |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer kbai_pk_<token>` |

## Query parameters

| Field    | Type      | Required | Description                           |
| -------- | --------- | -------- | ------------------------------------- |
| `search` | `string`  | No       | Free-text match over name and email.  |
| `phone`  | `string`  | No       | Exact phone match.                    |
| `limit`  | `integer` | No       | Page size, `1`–`200`. Defaults to 50. |
| `offset` | `integer` | No       | Records to skip. Defaults to 0.       |

## Example request

<CodeGroup>
  ```bash curl theme={null}
  curl -G https://api.keebai.com/v1/crm/customers \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    --data-urlencode "search=perez" \
    --data-urlencode "limit=50"
  ```

  ```js JavaScript theme={null}
  const params = new URLSearchParams({ search: "perez", limit: "50" });

  const response = await fetch(
    `https://api.keebai.com/v1/crm/customers?${params}`,
    {
      headers: { Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}` },
    },
  );
  const { data, total } = await response.json();
  ```

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

  resp = requests.get(
      "https://api.keebai.com/v1/crm/customers",
      headers={"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"},
      params={"search": "perez", "limit": 50},
      timeout=10,
  )
  resp.raise_for_status()
  customers = resp.json()["data"]
  ```
</CodeGroup>

## Response

### 200 OK

```json theme={null}
{
  "data": [
    {
      "_id": "65a1f2b3c4d5e6f7a8b9c0d1",
      "name": "Juan",
      "last_name": "Pérez",
      "email": "juan@example.com",
      "phone": "+56912345678",
      "identification_number": "12345678-9",
      "identification_type": "rut",
      "identification_country": "CL",
      "external_ids": [],
      "custom_fields": {},
      "tags": [],
      "accepts_marketing": false,
      "created_at": "2026-05-02T14:31:07.221Z",
      "updated_at": "2026-05-02T14:31:07.221Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}
```

### 400 Bad Request

`limit` above 200, negative `offset`, or an unknown query parameter.

### 401 Unauthorized

Missing, invalid, revoked, or expired token.

### 403 Forbidden

The token does not have the `crm:customers:read` scope.

## Operational notes

* **Search is a contains match**, not fuzzy. `perez` matches `Pérez` and `Perezoso`; it does not match a typo like `peres`.
* **`phone` is exact**, including the `+` and country code. Store phones in E.164 if you plan to look them up.
* To page through the full set, increment `offset` by `limit` until `offset >= total`.


## OpenAPI

````yaml GET /v1/crm/customers
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/crm/customers:
    get:
      tags:
        - crm-customers
      summary: Listar clientes
      description: >-
        Devuelve los clientes de la company del token. El listado no filtra por
        project.
      operationId: PublicCustomersController_list
      parameters:
        - name: search
          required: false
          in: query
          description: Búsqueda libre por nombre o email
          schema:
            type: string
        - name: phone
          required: false
          in: query
          description: Filtrar por teléfono exacto
          schema:
            type: string
        - name: limit
          required: false
          in: query
          schema:
            minimum: 1
            maximum: 200
            default: 50
            type: number
        - name: offset
          required: false
          in: query
          schema:
            minimum: 0
            default: 0
            type: number
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicCustomerListResponseDto'
      security:
        - PAT: []
components:
  schemas:
    PublicCustomerListResponseDto:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/PublicCustomerResponseDto'
        total:
          type: number
        limit:
          type: number
        offset:
          type: number
      required:
        - data
        - total
        - limit
        - offset
    PublicCustomerResponseDto:
      type: object
      properties:
        _id:
          type: string
          description: ObjectId del cliente
        name:
          type: string
        last_name:
          type: string
        email:
          type: string
        phone:
          type: string
        identification_number:
          type: string
        identification_type:
          type: string
          enum:
            - rut
            - dni
            - passport
            - other
        identification_country:
          type: string
          description: Código de país ISO 3166-1 alpha-2
        external_ids:
          type: array
          items:
            $ref: '#/components/schemas/PublicCustomerExternalIdResponseDto'
        custom_fields:
          type: object
          description: Campos personalizados de la company
        tags:
          description: Ids de tags del CRM
          type: array
          items:
            type: string
        accepts_marketing:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - _id
        - name
        - external_ids
        - custom_fields
        - tags
        - accepts_marketing
        - created_at
        - updated_at
    PublicCustomerExternalIdResponseDto:
      type: object
      properties:
        provider:
          type: string
          description: Sistema externo dueño del identificador
        external_id:
          type: string
          description: Identificador del cliente en ese sistema
      required:
        - provider
        - external_id
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````