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

# Customer API

> Create and manage optical customers with prescription data and file uploads

# Customer API

The Customer API allows you to create optical customers with their personal information, prescription data, and manage customer records. This endpoint supports file uploads for prescriptions and other documents.

<Note>
  This endpoint uses `multipart/form-data` format to support file uploads. Make
  sure to include the proper headers when making requests.
</Note>

## Endpoint Details

**Base URL:** `https://joptic.jethings.com`

### Required Headers

```
x-store-id: <store_id>
Authorization: Bearer <token>
```

***

## Create Customer

### Endpoint

**Endpoint:** `POST /customer`\
**Content-Type:** `multipart/form-data`

### Request Structure

The request uses `multipart/form-data` format with:

* **`jsonData`**: A JSON string containing the customer data
* **Files**: Optional prescription documents or attachments uploaded with specific field names

### Customer Data Structure

The `jsonData` field must contain a JSON string with the following structure:

| Field          | Type   | Required | Description                          |
| -------------- | ------ | -------- | ------------------------------------ |
| `customerData` | object | ✅ Yes    | Personal information of the customer |
| `address`      | object | No       | Customer address details             |
| `opticData`    | object | No       | Optical prescription measurements    |

#### CustomerData Object

| Field         | Type              | Required | Description                       |
| ------------- | ----------------- | -------- | --------------------------------- |
| `firstName`   | string            | ✅ Yes    | Customer's first name             |
| `lastName`    | string            | ✅ Yes    | Customer's last name              |
| `email`       | string            | ✅ Yes    | Customer's email address (unique) |
| `phoneNumber` | string            | ✅ Yes    | Customer's phone number           |
| `dateOfBirth` | string (ISO Date) | ✅ Yes    | Date of birth in ISO format       |
| `sex`         | string            | ✅ Yes    | Gender: "male" or "female"        |

#### Address Object

| Field     | Type   | Required | Description       |
| --------- | ------ | -------- | ----------------- |
| `street`  | string | No       | Street address    |
| `city`    | string | No       | City name         |
| `zipCode` | string | No       | Postal/ZIP code   |
| `state`   | string | No       | State or province |

#### OpticData Object

Contains vision prescription data for different viewing distances:

| Field                | Type   | Required | Description                      |
| -------------------- | ------ | -------- | -------------------------------- |
| `distanceVision`     | object | No       | Distance vision prescription     |
| `intermediateVision` | object | No       | Intermediate vision prescription |
| `nearVision`         | object | No       | Near vision prescription         |

Each vision object contains `rightEye` and `leftEye` with:

* `sph` (sphere): Spherical power
* `cyl` (cylinder): Cylindrical power
* `axis`: Axis measurement

***

## Create Customer Examples

### Example 1: Create Customer with Full Optical Data

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://joptic.jethings.com/customer \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -F 'jsonData={
      "customerData": {
        "firstName": "Layla",
        "lastName": "Hassan",
        "email": "layla@example.com",
        "phoneNumber": "+1-555-0199",
        "dateOfBirth": "1992-05-12T00:00:00.000Z",
        "sex": "female"
      },
      "address": {
        "street": "123 Vision Ave",
        "city": "Casablanca",
        "zipCode": "20000",
        "state": "MA"
      },
      "opticData": {
        "distanceVision": {
          "rightEye": { "sph": "-1.25", "cyl": "-0.25", "axis": "90" },
          "leftEye": { "sph": "-1.00", "cyl": "-0.25", "axis": "85" }
        },
        "intermediateVision": {
          "rightEye": { "sph": "-0.75", "cyl": "0", "axis": "0" },
          "leftEye": { "sph": "-0.75", "cyl": "0", "axis": "0" }
        },
        "nearVision": {
          "rightEye": { "sph": "-0.50", "cyl": "0", "axis": "0" },
          "leftEye": { "sph": "-0.50", "cyl": "0", "axis": "0" }
        }
      }
    }' \
    -F 'prescription=@prescription.pdf' \
    -F 'idCard=@id-card.jpg'
  ```

  ```javascript JavaScript theme={null}
  const formData = new FormData()

  const jsonData = {
    customerData: {
      firstName: "Layla",
      lastName: "Hassan",
      email: "layla@example.com",
      phoneNumber: "+1-555-0199",
      dateOfBirth: "1992-05-12T00:00:00.000Z",
      sex: "female",
    },
    address: {
      street: "123 Vision Ave",
      city: "Casablanca",
      zipCode: "20000",
      state: "MA",
    },
    opticData: {
      distanceVision: {
        rightEye: { sph: "-1.25", cyl: "-0.25", axis: "90" },
        leftEye: { sph: "-1.00", cyl: "-0.25", axis: "85" },
      },
      intermediateVision: {
        rightEye: { sph: "-0.75", cyl: "0", axis: "0" },
        leftEye: { sph: "-0.75", cyl: "0", axis: "0" },
      },
      nearVision: {
        rightEye: { sph: "-0.50", cyl: "0", axis: "0" },
        leftEye: { sph: "-0.50", cyl: "0", axis: "0" },
      },
    },
  }

  formData.append("jsonData", JSON.stringify(jsonData))
  formData.append("prescription", prescriptionFile)
  formData.append("idCard", idCardFile)

  const response = await fetch("https://joptic.jethings.com/customer", {
    method: "POST",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
    body: formData,
  })

  const result = await response.json()
  ```

  ```python Python theme={null}
  import requests
  import json

  url = "https://joptic.jethings.com/customer"

  json_data = {
      "customerData": {
          "firstName": "Layla",
          "lastName": "Hassan",
          "email": "layla@example.com",
          "phoneNumber": "+1-555-0199",
          "dateOfBirth": "1992-05-12T00:00:00.000Z",
          "sex": "female"
      },
      "address": {
          "street": "123 Vision Ave",
          "city": "Casablanca",
          "zipCode": "20000",
          "state": "MA"
      },
      "opticData": {
          "distanceVision": {
              "rightEye": {"sph": "-1.25", "cyl": "-0.25", "axis": "90"},
              "leftEye": {"sph": "-1.00", "cyl": "-0.25", "axis": "85"}
          },
          "intermediateVision": {
              "rightEye": {"sph": "-0.75", "cyl": "0", "axis": "0"},
              "leftEye": {"sph": "-0.75", "cyl": "0", "axis": "0"}
          },
          "nearVision": {
              "rightEye": {"sph": "-0.50", "cyl": "0", "axis": "0"},
              "leftEye": {"sph": "-0.50", "cyl": "0", "axis": "0"}
          }
      }
  }

  files = [
      ('prescription', ('prescription.pdf', open('prescription.pdf', 'rb'), 'application/pdf')),
      ('idCard', ('id-card.jpg', open('id-card.jpg', 'rb'), 'image/jpeg'))
  ]

  response = requests.post(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      },
      data={'jsonData': json.dumps(json_data)},
      files=files
  )

  print(response.json())
  ```
</CodeGroup>

**Success Response:**

```json theme={null}
{
  "customer": {
    "id": "customer-uuid",
    "isActive": true,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T10:00:00.000Z",
    "deletedAt": null
  },
  "localData": {
    "id": "local-data-uuid",
    "firstName": "Layla",
    "lastName": "Hassan",
    "email": "layla@example.com",
    "phoneNumber": "+1-555-0199",
    "dateOfBirth": "1992-05-12T00:00:00.000Z",
    "sex": "female",
    "customerId": "customer-uuid"
  }
}
```

***

### Example 2: Create Customer with Minimal Data

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://joptic.jethings.com/customer \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -F 'jsonData={
      "customerData": {
        "firstName": "Ahmed",
        "lastName": "Benali",
        "email": "ahmed.benali@example.com",
        "phoneNumber": "+212-6-12345678",
        "dateOfBirth": "1985-08-20T00:00:00.000Z",
        "sex": "male"
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const formData = new FormData()

  const jsonData = {
    customerData: {
      firstName: "Ahmed",
      lastName: "Benali",
      email: "ahmed.benali@example.com",
      phoneNumber: "+212-6-12345678",
      dateOfBirth: "1985-08-20T00:00:00.000Z",
      sex: "male",
    },
  }

  formData.append("jsonData", JSON.stringify(jsonData))

  const response = await fetch("https://joptic.jethings.com/customer", {
    method: "POST",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
    body: formData,
  })

  const result = await response.json()
  ```

  ```python Python theme={null}
  import requests
  import json

  url = "https://joptic.jethings.com/customer"

  json_data = {
      "customerData": {
          "firstName": "Ahmed",
          "lastName": "Benali",
          "email": "ahmed.benali@example.com",
          "phoneNumber": "+212-6-12345678",
          "dateOfBirth": "1985-08-20T00:00:00.000Z",
          "sex": "male"
      }
  }

  response = requests.post(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      },
      data={'jsonData': json.dumps(json_data)}
  )

  print(response.json())
  ```
</CodeGroup>

***

## Get Single Customer

Retrieve a specific customer by ID with all their details including personal information, address, and optical prescription data.

### Endpoint

**Endpoint:** `GET /customer/:id`

### Path Parameters

| Parameter | Type   | Required | Description            |
| --------- | ------ | -------- | ---------------------- |
| `id`      | string | ✅ Yes    | The ID of the customer |

### Response (Success)

```json theme={null}
{
  "id": "customer-uuid",
  "isActive": true,
  "createdAt": "2024-01-15T10:00:00.000Z",
  "updatedAt": "2024-01-15T10:00:00.000Z",
  "deletedAt": null,
  "localData": {
    "id": "local-data-uuid",
    "firstName": "Layla",
    "lastName": "Hassan",
    "email": "layla@example.com",
    "phoneNumber": "+1-555-0199",
    "dateOfBirth": "1992-05-12T00:00:00.000Z",
    "sex": "female",
    "customerId": "customer-uuid"
  },
  "address": {
    "id": "address-uuid",
    "street": "123 Vision Ave",
    "city": "Casablanca",
    "zipCode": "20000",
    "state": "MA",
    "customerId": "customer-uuid"
  },
  "opticData": {
    "id": "optic-data-uuid",
    "distanceVision": {
      "rightEye": { "sph": "-1.25", "cyl": "-0.25", "axis": "90" },
      "leftEye": { "sph": "-1.00", "cyl": "-0.25", "axis": "85" }
    },
    "intermediateVision": {
      "rightEye": { "sph": "-0.75", "cyl": "0", "axis": "0" },
      "leftEye": { "sph": "-0.75", "cyl": "0", "axis": "0" }
    },
    "nearVision": {
      "rightEye": { "sph": "-0.50", "cyl": "0", "axis": "0" },
      "leftEye": { "sph": "-0.50", "cyl": "0", "axis": "0" }
    },
    "customerId": "customer-uuid"
  }
}
```

### Response Schema

* `id` (string): The customer ID
* `isActive` (boolean): Whether the customer is active
* `createdAt` (Date): Creation timestamp
* `updatedAt` (Date): Last update timestamp
* `deletedAt` (Date, nullable): Deletion timestamp (null if not deleted)
* `localData` (object): Customer personal information
* `address` (object, optional): Customer address details
* `opticData` (object, optional): Optical prescription data

### Error Responses

**404 Not Found** - Customer not found or doesn't belong to store

```json theme={null}
{
  "statusCode": 404,
  "message": "Customer not found or does not belong to this store"
}
```

### Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://joptic.jethings.com/customer/customer-uuid" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://joptic.jethings.com/customer/customer-uuid", {
    method: "GET",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
  })

  const result = await response.json()
  ```

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

  url = "https://joptic.jethings.com/customer/customer-uuid"

  response = requests.get(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      }
  )

  print(response.json())
  ```
</CodeGroup>

***

## Get All Customers

### Endpoint

**Endpoint:** `GET /customer`

### Query Parameters

| Parameter   | Type    | Required | Default   | Description                                            |
| ----------- | ------- | -------- | --------- | ------------------------------------------------------ |
| `search`    | string  | No       | -         | Search by first name, last name, or username           |
| `name`      | string  | No       | -         | Filter by specific name (first or last)                |
| `isActive`  | boolean | No       | -         | Filter by active status                                |
| `page`      | number  | No       | 1         | Page number for pagination                             |
| `limit`     | number  | No       | 10        | Number of records per page                             |
| `sortBy`    | string  | No       | createdAt | Sort field: "name", "status", "createdAt", "updatedAt" |
| `sortOrder` | string  | No       | desc      | Sort order: "asc" or "desc"                            |

***

## Get Customers Examples

### Example 1: Get All Customers (Default Pagination)

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://joptic.jethings.com/customer" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://joptic.jethings.com/customer", {
    method: "GET",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
  })

  const result = await response.json()
  ```

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

  url = "https://joptic.jethings.com/customer"

  response = requests.get(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      }
  )

  print(response.json())
  ```
</CodeGroup>

***

### Example 2: Search Customers with Filters

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://joptic.jethings.com/customer?search=Hassan&isActive=true&page=1&limit=20&sortBy=name&sortOrder=asc" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    search: "Hassan",
    isActive: "true",
    page: "1",
    limit: "20",
    sortBy: "name",
    sortOrder: "asc",
  })

  const response = await fetch(`https://joptic.jethings.com/customer?${params}`, {
    method: "GET",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
  })

  const result = await response.json()
  ```

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

  url = "https://joptic.jethings.com/customer"

  params = {
      'search': 'Hassan',
      'isActive': True,
      'page': 1,
      'limit': 20,
      'sortBy': 'name',
      'sortOrder': 'asc'
  }

  response = requests.get(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      },
      params=params
  )

  print(response.json())
  ```
</CodeGroup>

**Success Response:**

```json theme={null}
{
  "data": [
    {
      "id": "customer-uuid",
      "name": "Layla Hassan",
      "userName": "layla@example.com",
      "groups": [
        {
          "id": "group-uuid",
          "name": "VIP Customers"
        }
      ],
      "isLocal": true,
      "status": "active",
      "createdAt": "2024-01-15T10:00:00.000Z",
      "updatedAt": "2024-01-15T10:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 45,
    "totalPages": 3
  }
}
```

***

### Example 3: Get Active Customers Only

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://joptic.jethings.com/customer?isActive=true&sortBy=createdAt&sortOrder=desc" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    isActive: "true",
    sortBy: "createdAt",
    sortOrder: "desc",
  })

  const response = await fetch(`https://joptic.jethings.com/customer?${params}`, {
    method: "GET",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
  })

  const result = await response.json()
  ```

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

  url = "https://joptic.jethings.com/customer"

  params = {
      'isActive': True,
      'sortBy': 'createdAt',
      'sortOrder': 'desc'
  }

  response = requests.get(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      },
      params=params
  )

  print(response.json())
  ```
</CodeGroup>

***

## Update Customer

Update an existing customer's information. You can update personal data, address, and optical prescription information. This endpoint also supports file uploads for updating prescription documents.

### Endpoint

**Endpoint:** `PUT /customer/:id`\
**Content-Type:** `multipart/form-data`

### Path Parameters

| Parameter | Type   | Required | Description            |
| --------- | ------ | -------- | ---------------------- |
| `id`      | string | ✅ Yes    | The ID of the customer |

### Request Structure

The request uses `multipart/form-data` format with:

* **`jsonData`**: A JSON string containing the customer data to update
* **Files**: Optional prescription documents or attachments to update

### Request Body Schema

All fields in the `jsonData` are optional. Only include the fields you want to update:

| Field          | Type   | Required | Description                          |
| -------------- | ------ | -------- | ------------------------------------ |
| `customerData` | object | No       | Personal information of the customer |
| `address`      | object | No       | Customer address details             |
| `opticData`    | object | No       | Optical prescription measurements    |

#### CustomerData Object (all fields optional)

| Field         | Type              | Description                       |
| ------------- | ----------------- | --------------------------------- |
| `firstName`   | string            | Customer's first name             |
| `lastName`    | string            | Customer's last name              |
| `email`       | string            | Customer's email address (unique) |
| `phoneNumber` | string            | Customer's phone number           |
| `dateOfBirth` | string (ISO Date) | Date of birth in ISO format       |
| `sex`         | string            | Gender: "male" or "female"        |

### Request Body Examples

**Update only customer name:**

```json theme={null}
{
  "customerData": {
    "firstName": "Layla",
    "lastName": "Hassan"
  }
}
```

**Update multiple fields:**

```json theme={null}
{
  "customerData": {
    "firstName": "Layla",
    "lastName": "Hassan",
    "phoneNumber": "+1-555-9999"
  },
  "address": {
    "street": "456 New Street",
    "city": "Rabat",
    "zipCode": "10000",
    "state": "RB"
  },
  "opticData": {
    "distanceVision": {
      "rightEye": { "sph": "-1.50", "cyl": "-0.50", "axis": "90" },
      "leftEye": { "sph": "-1.25", "cyl": "-0.50", "axis": "85" }
    }
  }
}
```

### Response (Success)

```json theme={null}
{
  "customer": {
    "id": "customer-uuid",
    "isActive": true,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T11:45:00.000Z",
    "deletedAt": null
  },
  "localData": {
    "id": "local-data-uuid",
    "firstName": "Layla",
    "lastName": "Hassan",
    "email": "layla@example.com",
    "phoneNumber": "+1-555-9999",
    "dateOfBirth": "1992-05-12T00:00:00.000Z",
    "sex": "female",
    "customerId": "customer-uuid"
  }
}
```

### Error Responses

**404 Not Found** - Customer not found or doesn't belong to store

```json theme={null}
{
  "statusCode": 404,
  "message": "Customer not found or does not belong to this store"
}
```

**400 Bad Request** - Validation errors

```json theme={null}
{
  "statusCode": 400,
  "message": [
    "firstName must be a string",
    "email must be a valid email address"
  ],
  "error": "Bad Request"
}
```

**409 Conflict** - Email already exists

```json theme={null}
{
  "statusCode": 409,
  "message": "Customer with this email already exists",
  "error": "Conflict"
}
```

### Example Requests

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT "https://joptic.jethings.com/customer/customer-uuid" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -F 'jsonData={
      "customerData": {
        "firstName": "Layla",
        "lastName": "Hassan",
        "phoneNumber": "+1-555-9999"
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const formData = new FormData()

  const jsonData = {
    customerData: {
      firstName: "Layla",
      lastName: "Hassan",
      phoneNumber: "+1-555-9999",
    },
  }

  formData.append("jsonData", JSON.stringify(jsonData))

  const response = await fetch("https://joptic.jethings.com/customer/customer-uuid", {
    method: "PUT",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
    body: formData,
  })

  const result = await response.json()
  ```

  ```python Python theme={null}
  import requests
  import json

  url = "https://joptic.jethings.com/customer/customer-uuid"

  json_data = {
      "customerData": {
          "firstName": "Layla",
          "lastName": "Hassan",
          "phoneNumber": "+1-555-9999"
      }
  }

  response = requests.put(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      },
      data={'jsonData': json.dumps(json_data)}
  )

  print(response.json())
  ```
</CodeGroup>

### Notes

* Only customers belonging to the authenticated user's store can be updated
* Email addresses must be unique per store
* All updates are performed within a database transaction
* Files can be updated by including new files with the same field names
* If a field is not included in the request, it will remain unchanged

***

## Delete Customer

Delete a single customer. This performs a soft delete, setting the `deletedAt` timestamp.

### Endpoint

**Endpoint:** `DELETE /customer/:id`

### Path Parameters

| Parameter | Type   | Required | Description            |
| --------- | ------ | -------- | ---------------------- |
| `id`      | string | ✅ Yes    | The ID of the customer |

### Response (Success)

```json theme={null}
{
  "message": "Customer successfully deleted",
  "customerId": "customer-uuid"
}
```

### Response Schema

* `message` (string): Success message
* `customerId` (string): The ID of the deleted customer

### Error Responses

**404 Not Found** - Customer not found or doesn't belong to store

```json theme={null}
{
  "statusCode": 404,
  "message": "Customer not found or does not belong to this store"
}
```

### Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE "https://joptic.jethings.com/customer/customer-uuid" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://joptic.jethings.com/customer/customer-uuid", {
    method: "DELETE",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
  })

  const result = await response.json()
  ```

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

  url = "https://joptic.jethings.com/customer/customer-uuid"

  response = requests.delete(
      url,
      headers={
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      }
  )

  print(response.json())
  ```
</CodeGroup>

### Notes

* Only customers belonging to the authenticated user's store can be deleted
* This is a soft delete operation (sets `deletedAt` timestamp)
* Deleted customers are automatically excluded from GET requests
* Related data (address, opticData) may be handled based on cascade rules

***

## Bulk Delete Customers

Delete multiple customers in a single request. Only customers belonging to the authenticated user's store can be deleted.

### Endpoint

**Endpoint:** `DELETE /customer/bulk`

### Request Body

```json theme={null}
{
  "ids": [
    "customer-uuid-1",
    "customer-uuid-2",
    "customer-uuid-3"
  ]
}
```

### Request Body Schema

* `ids` (array of strings, required, min 1 item): Array of customer IDs to delete
  * Each ID must be a non-empty string

### Response (Success)

```json theme={null}
{
  "message": "Successfully deleted 3 customer(s)",
  "deletedCount": 3
}
```

### Response Schema

* `message` (string): Success message indicating how many customers were deleted
* `deletedCount` (number): Number of customers successfully deleted

### Error Responses

**400 Bad Request** - No customer IDs provided

```json theme={null}
{
  "statusCode": 400,
  "message": "No customer IDs provided"
}
```

**404 Not Found** - No valid customers found to delete

```json theme={null}
{
  "statusCode": 404,
  "message": "No valid customers found to delete"
}
```

### Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE "https://joptic.jethings.com/customer/bulk" \
    -H "Content-Type: application/json" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -d '{
      "ids": [
        "customer-uuid-1",
        "customer-uuid-2"
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://joptic.jethings.com/customer/bulk", {
    method: "DELETE",
    headers: {
      "Content-Type": "application/json",
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
    },
    body: JSON.stringify({
      ids: ["customer-uuid-1", "customer-uuid-2"],
    }),
  })

  const result = await response.json()
  ```

  ```python Python theme={null}
  import requests
  import json

  url = "https://joptic.jethings.com/customer/bulk"

  response = requests.delete(
      url,
      headers={
          'Content-Type': 'application/json',
          'x-store-id': 'your-store-id',
          'Authorization': 'Bearer <token>'
      },
      data=json.dumps({
          'ids': ['customer-uuid-1', 'customer-uuid-2']
      })
  )

  print(response.json())
  ```
</CodeGroup>

### Notes

* Only customers that belong to the authenticated user's store will be deleted
* Already deleted customers (soft-deleted) are automatically excluded
* This is a soft delete operation (sets `deletedAt` timestamp)
* All operations are performed within a database transaction

***

## Response Structures

### Create Customer Response

```json theme={null}
{
  "customer": {
    "id": "customer-uuid",
    "isActive": true,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T10:00:00.000Z",
    "deletedAt": null
  },
  "localData": {
    "id": "local-data-uuid",
    "firstName": "string",
    "lastName": "string",
    "email": "string",
    "phoneNumber": "string",
    "dateOfBirth": "ISO date string",
    "sex": "male | female",
    "customerId": "customer-uuid"
  }
}
```

### Get Customers Response

```json theme={null}
{
  "data": [
    {
      "id": "customer-uuid",
      "name": "Full Name",
      "userName": "email@example.com",
      "groups": [
        {
          "id": "group-uuid",
          "name": "Group Name"
        }
      ],
      "isLocal": true,
      "status": "active | unactive",
      "createdAt": "ISO date string",
      "updatedAt": "ISO date string"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 100,
    "totalPages": 10
  }
}
```

***

## Error Responses

### 400 Bad Request

```json theme={null}
{
  "statusCode": 400,
  "message": "Invalid input data or missing required fields",
  "error": "Bad Request"
}
```

### 403 Forbidden

```json theme={null}
{
  "statusCode": 403,
  "message": "You do not have access to this store",
  "error": "Forbidden"
}
```

### 409 Conflict

```json theme={null}
{
  "statusCode": 409,
  "message": "Customer with this email already exists",
  "error": "Conflict"
}
```

### 500 Internal Server Error

```json theme={null}
{
  "statusCode": 500,
  "message": "Failed to create customer",
  "error": "Internal Server Error"
}
```

***

## Important Notes

### Store Access Requirements

<AccordionGroup>
  <Accordion title="Create Customer">
    * Requires user to be an **owner** of the store
    * Validates `userStoreRelation` for active owner relationship
    * Missing ownership raises `ForbiddenException`
  </Accordion>

  <Accordion title="Get Customers">
    * Requires user to have **access** to the store
    * Can be owner or have assigned role/permissions
    * Store access is validated via `userStoreRelation`
  </Accordion>

  <Accordion title="Update Customer">
    * Requires user to be an **owner** of the store
    * Only customers belonging to the store can be updated
    * Email uniqueness is validated per store
  </Accordion>

  <Accordion title="Delete Customer">
    * Requires user to be an **owner** of the store
    * Only customers belonging to the store can be deleted
    * Performs soft delete (sets deletedAt timestamp)
  </Accordion>
</AccordionGroup>

### Email Handling

* Email addresses are **trimmed** and stored as provided
* Email must be **unique** per store
* Duplicate emails result in `ConflictException` (409)

### File Uploads

<CardGroup cols={2}>
  <Card title="Supported Files" icon="file">
    Upload prescription documents, ID cards, or related files

    <br />

    <code>Grouped by fieldname</code>
  </Card>

  <Card title="Optional Upload" icon="check">
    Files are optional for customer creation

    <br />

    <code>Basic customer data sufficient</code>
  </Card>
</CardGroup>

### Customer Status

* Customers are created with `isActive: true` by default
* Status can be filtered in GET requests using `isActive` parameter
* Deleted customers have `deletedAt` timestamp (soft delete)

### Name Display Logic

The customer name follows this fallback order:

1. `firstName + lastName` from local data
2. `userName` from user table
3. Placeholder value if neither available

### Pagination

<Tabs>
  <Tab title="Default Behavior">
    ```javascript theme={null}
    {
      page: 1,
      limit: 10,
      sortBy: "createdAt",
      sortOrder: "desc"
    }
    ```

    Returns 10 most recent customers by default
  </Tab>

  <Tab title="Custom Pagination">
    ```javascript theme={null}
    {
      page: 2,
      limit: 50,
      sortBy: "name",
      sortOrder: "asc"
    }
    ```

    Customize page size and sorting
  </Tab>
</Tabs>

***

## Summary

<Steps>
  <Step title="Authenticate">Obtain JWT token and ensure store access</Step>

  <Step title="Prepare Customer Data">
    Structure JSON with required customer information
  </Step>

  <Step title="Create Customer">POST request with multipart form data</Step>

  <Step title="Retrieve Customers">
    GET request with optional filters and pagination, or GET single customer by ID
  </Step>

  <Step title="Update Customer">
    PUT request with customer ID and updated data (multipart/form-data)
  </Step>

  <Step title="Delete Customer">
    DELETE request with customer ID, or bulk delete with array of IDs
  </Step>

  <Step title="Handle Response">
    Process customer data, pagination metadata, or deletion confirmation
  </Step>
</Steps>

This API provides comprehensive customer management for optical stores, including prescription data storage, file uploads, flexible querying with pagination and filtering, and full CRUD operations for customer records.

***

<CardGroup cols={2}>
  <Card title="Purchase Invoice API" icon="file-invoice" href="/api-reference/purchase-invoice-api">
    Create purchase invoices and manage inventory
  </Card>

  <Card title="Store Management" icon="store" href="/api-reference/stores-api">
    Manage stores and permissions
  </Card>
</CardGroup>
