> ## 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.

# Supplier API

> Supplier management API for creating, retrieving, updating and deleting suppliers linked to optical stores

# Supplier API

The Supplier API provides endpoints for managing suppliers and their relations to stores, groups, addresses, and contacts in optical stores.

<Note>
  All Supplier API endpoints require authentication and a valid `x-store-id` header to identify the store context, unless stated otherwise.
</Note>

## Overview

Suppliers can be:

* Linked to one or more stores
* Assigned to one or more supplier groups
* Created with optional address and contact information
* Assigned or updated with a default price list
* Filtered and sorted by various criteria
* Retrieved in paginated lists
* Deleted individually or in bulk

## Endpoint Details

**Base URL:** `https://joptic.jethings.com`\
**Content-Type:** `application/json`

### Required Headers

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

<Warning>
  All routes require a valid `x-store-id` header. Users must have an active relation with the store to access its suppliers.
</Warning>

***

## Get All Suppliers

**GET** `/suppliers`

Retrieve a paginated list of suppliers linked to the store that the authenticated user has access to.

**Requires Authentication:** Bearer token in Authorization header

### Required Headers

| Key          | Value    | Required | Description                                       |
| ------------ | -------- | -------- | ------------------------------------------------- |
| `x-store-id` | `string` | Yes      | Store identifier (used by `@StoreId()` decorator) |

### Query Parameters

| Parameter   | Type      | Required | Description                                               | Default     |
| ----------- | --------- | -------- | --------------------------------------------------------- | ----------- |
| `search`    | `string`  | No       | Filter by name or description (partial match)             | —           |
| `name`      | `string`  | No       | Filter by supplier name (partial match)                   | —           |
| `isActive`  | `boolean` | No       | Filter by active/inactive status                          | —           |
| `page`      | `number`  | No       | Page number (1-based)                                     | `1`         |
| `limit`     | `number`  | No       | Number of items per page (1–100)                          | `10`        |
| `sortBy`    | `string`  | No       | Sort field (`name`, `isActive`, `updatedAt`, `createdAt`) | `createdAt` |
| `sortOrder` | `string`  | No       | Sort direction (`asc` or `desc`)                          | `desc`      |

### Success Response

**Status Code:** `200 OK`

```json theme={null}
{
  "data": [
    {
      "id": "sup_12345",
      "storeIds": ["str_1", "str_2"],
      "supplierGroups": [
        { "id": "grp_1", "name": "Preferred" }
      ],
      "name": "Global Traders Ltd.",
      "description": "Household goods wholesaler",
      "note": "Ships on Mondays",
      "defaultPriceListId": "pl_abc123",
      "address": {
        "id": "addr_1",
        "street": "123 Main St",
        "city": "Casablanca",
        "state": "CA",
        "postalCode": "20000",
        "country": "MA"
      },
      "contact": {
        "id": "cont_1",
        "phone": "+212600111222",
        "fax": null,
        "email": "sales@globaltraders.com",
        "website": "https://globaltraders.com"
      },
      "isActive": true,
      "createdAt": "2025-11-03T10:00:00.000Z",
      "updatedAt": "2025-11-03T10:30:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 12,
    "totalPages": 2,
    "hasNext": true,
    "hasPrev": false
  }
}
```

### Response Fields

#### Supplier Object

| Field                   | Type       | Description                          |
| ----------------------- | ---------- | ------------------------------------ |
| `id`                    | `string`   | Unique supplier ID                   |
| `storeIds`              | `string[]` | Store IDs this supplier is linked to |
| `supplierGroups`        | `object[]` | Groups assigned to the supplier      |
| `supplierGroups[].id`   | `string`   | Group ID                             |
| `supplierGroups[].name` | `string`   | Group name                           |
| `name`                  | `string`   | Supplier display name                |
| `description`           | `string?`  | Supplier description                 |
| `note`                  | `string?`  | Internal note                        |
| `defaultPriceListId`    | `string?`  | Default price list ID                |
| `address`               | `Address?` | Address information                  |
| `contact`               | `Contact?` | Contact information                  |
| `isActive`              | `boolean`  | Active status                        |
| `createdAt`             | `string`   | Creation timestamp (ISO 8601)        |
| `updatedAt`             | `string`   | Last update timestamp (ISO 8601)     |

#### Address

| Field        | Type     | Description     |
| ------------ | -------- | --------------- |
| `id`         | `string` | Address ID      |
| `street`     | `string` | Street          |
| `city`       | `string` | City            |
| `state`      | `string` | State/Province  |
| `postalCode` | `string` | Postal/ZIP code |
| `country`    | `string` | Country         |

#### Contact

| Field     | Type     | Description   |
| --------- | -------- | ------------- |
| `id`      | `string` | Contact ID    |
| `phone`   | `string` | Phone number  |
| `fax`     | `string` | Fax number    |
| `email`   | `string` | Email address |
| `website` | `string` | Website URL   |

#### Pagination Object

| Field        | Type      | Description                    |
| ------------ | --------- | ------------------------------ |
| `page`       | `number`  | Current page number            |
| `limit`      | `number`  | Number of items per page       |
| `total`      | `number`  | Total number of records        |
| `totalPages` | `number`  | Total pages available          |
| `hasNext`    | `boolean` | Whether a next page exists     |
| `hasPrev`    | `boolean` | Whether a previous page exists |

### Possible Errors

| Code | Message                                        |
| ---- | ---------------------------------------------- |
| 403  | Forbidden - User does not have access to store |
| 500  | Internal Server Error                          |

### Example Requests

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

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

  const response = await fetch(`https://joptic.jethings.com/suppliers?${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/suppliers"

  params = {
      "page": 1,
      "limit": 10,
      "sortBy": "name",
      "sortOrder": "asc"
  }

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

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

Search suppliers by name/description:

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

Filter by active status:

```bash theme={null}
curl -X GET "https://joptic.jethings.com/suppliers?isActive=true&page=1&limit=20" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"
```

***

## Get Supplier by ID

**GET** `/suppliers/:id`

Retrieve a single supplier by ID. The supplier must be linked to the current store.

**Requires Authentication:** Bearer token in Authorization header

### Required Headers

| Key          | Value    | Required | Description                                       |
| ------------ | -------- | -------- | ------------------------------------------------- |
| `x-store-id` | `string` | Yes      | Store identifier (used by `@StoreId()` decorator) |

### Path Parameters

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

### Success Response

**Status Code:** `200 OK`

```json theme={null}
{
  "id": "sup_12345",
  "storeIds": ["str_1"],
  "supplierGroups": [{ "id": "grp_1", "name": "Preferred" }],
  "name": "Global Traders Ltd.",
  "description": "Household goods wholesaler",
  "note": "Ships on Mondays",
  "defaultPriceListId": "pl_abc123",
  "address": {
    "id": "addr_1",
    "street": "123 Main St",
    "city": "Casablanca",
    "state": "CA",
    "postalCode": "20000",
    "country": "MA"
  },
  "contact": {
    "id": "cont_1",
    "phone": "+212600111222",
    "fax": null,
    "email": "sales@globaltraders.com",
    "website": "https://globaltraders.com"
  },
  "isActive": true,
  "createdAt": "2025-11-03T10:00:00.000Z",
  "updatedAt": "2025-11-03T10:30:00.000Z"
}
```

### Possible Errors

| Code | Message                                                    |
| ---- | ---------------------------------------------------------- |
| 403  | Forbidden - User does not have access to store or supplier |
| 404  | Not Found - Supplier not found or inaccessible             |
| 500  | Internal Server Error                                      |

### Example Requests

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

  ```javascript JavaScript theme={null}
  const response = await fetch("https://joptic.jethings.com/suppliers/sup_12345", {
    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/suppliers/sup_12345"

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

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

***

## Create Supplier

**POST** `/suppliers`

Create a new supplier and link it to the current store. You can optionally assign supplier groups, a default price list, address, and contact information.

**Requires Authentication:** Bearer token in Authorization header

### Required Headers

| Key          | Value    | Required | Description                                       |
| ------------ | -------- | -------- | ------------------------------------------------- |
| `x-store-id` | `string` | Yes      | Store identifier (used by `@StoreId()` decorator) |

### Request Body

| Field                | Type           | Required | Description                         |
| -------------------- | -------------- | -------- | ----------------------------------- |
| `supplierGroupIds`   | `string[]`     | No       | Supplier groups to associate        |
| `name`               | `string`       | ✅ Yes    | Supplier name (max 255 characters)  |
| `description`        | `string`       | No       | Description (max 1000 characters)   |
| `note`               | `string`       | No       | Internal note (max 1000 characters) |
| `defaultPriceListId` | `string`       | No       | Default price list to assign        |
| `address`            | `AddressInput` | No       | Address to create                   |
| `contact`            | `ContactInput` | No       | Contact to create                   |

#### AddressInput

| Field        | Type     | Required | Description                         |
| ------------ | -------- | -------- | ----------------------------------- |
| `street`     | `string` | ✅ Yes    | Street (max 255 characters)         |
| `city`       | `string` | ✅ Yes    | City (max 100 characters)           |
| `state`      | `string` | ✅ Yes    | State/Province (max 100 characters) |
| `postalCode` | `string` | ✅ Yes    | Postal/ZIP code (max 20 characters) |
| `country`    | `string` | ✅ Yes    | Country (max 100 characters)        |

#### ContactInput

| Field     | Type     | Required | Description                                             |
| --------- | -------- | -------- | ------------------------------------------------------- |
| `phone`   | `string` | No       | Phone number (max 20 characters)                        |
| `fax`     | `string` | No       | Fax number (max 20 characters)                          |
| `email`   | `string` | No       | Email address (max 255 characters, must be valid email) |
| `website` | `string` | No       | Website URL (max 255 characters)                        |

### Request Body Example

```json theme={null}
{
  "supplierGroupIds": ["grp_1", "grp_2"],
  "name": "Global Traders Ltd.",
  "description": "Household goods wholesaler",
  "note": "Ships on Mondays",
  "defaultPriceListId": "pl_abc123",
  "address": {
    "street": "123 Main St",
    "city": "Casablanca",
    "state": "CA",
    "postalCode": "20000",
    "country": "MA"
  },
  "contact": {
    "phone": "+212600111222",
    "fax": null,
    "email": "sales@globaltraders.com",
    "website": "https://globaltraders.com"
  }
}
```

### Success Response

**Status Code:** `200 OK`

```json theme={null}
{
  "id": "sup_abc123",
  "storeIds": ["str_12345"],
  "supplierGroups": [
    { "id": "grp_1", "name": "Preferred" },
    { "id": "grp_2", "name": "Local" }
  ],
  "name": "Global Traders Ltd.",
  "description": "Household goods wholesaler",
  "note": "Ships on Mondays",
  "defaultPriceListId": "pl_abc123",
  "address": {
    "id": "addr_1",
    "street": "123 Main St",
    "city": "Casablanca",
    "state": "CA",
    "postalCode": "20000",
    "country": "MA"
  },
  "contact": {
    "id": "cont_1",
    "phone": "+212600111222",
    "fax": null,
    "email": "sales@globaltraders.com",
    "website": "https://globaltraders.com"
  },
  "isActive": true,
  "createdAt": "2025-11-03T10:00:00.000Z",
  "updatedAt": "2025-11-03T10:00:00.000Z"
}
```

### Possible Errors

| Code | Message                                             |
| ---- | --------------------------------------------------- |
| 400  | Bad Request - Invalid request data                  |
| 403  | Forbidden - User does not have access to this store |
| 404  | Not Found - Price list or supplier group not found  |
| 500  | Internal Server Error - Failed to create supplier   |

### Example Requests

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://joptic.jethings.com/suppliers" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "supplierGroupIds": ["grp_1"],
      "name": "Global Traders Ltd.",
      "description": "Household goods wholesaler",
      "note": "Ships on Mondays",
      "defaultPriceListId": "pl_abc123",
      "address": {
        "street": "123 Main St",
        "city": "Casablanca",
        "state": "CA",
        "postalCode": "20000",
        "country": "MA"
      },
      "contact": {
        "phone": "+212600111222",
        "email": "sales@globaltraders.com",
        "website": "https://globaltraders.com"
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const supplierData = {
    supplierGroupIds: ["grp_1"],
    name: "Global Traders Ltd.",
    description: "Household goods wholesaler",
    note: "Ships on Mondays",
    defaultPriceListId: "pl_abc123",
    address: {
      street: "123 Main St",
      city: "Casablanca",
      state: "CA",
      postalCode: "20000",
      country: "MA"
    },
    contact: {
      phone: "+212600111222",
      email: "sales@globaltraders.com",
      website: "https://globaltraders.com"
    }
  }

  const response = await fetch("https://joptic.jethings.com/suppliers", {
    method: "POST",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(supplierData),
  })

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

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

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

  supplier_data = {
      "supplierGroupIds": ["grp_1"],
      "name": "Global Traders Ltd.",
      "description": "Household goods wholesaler",
      "note": "Ships on Mondays",
      "defaultPriceListId": "pl_abc123",
      "address": {
          "street": "123 Main St",
          "city": "Casablanca",
          "state": "CA",
          "postalCode": "20000",
          "country": "MA"
      },
      "contact": {
          "phone": "+212600111222",
          "email": "sales@globaltraders.com",
          "website": "https://globaltraders.com"
      }
  }

  response = requests.post(
      url,
      headers={
          "x-store-id": "your-store-id",
          "Authorization": "Bearer <token>",
          "Content-Type": "application/json"
      },
      json=supplier_data
  )

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

<Note>
  When creating a supplier, the system automatically:

  1. Creates an address record if address information is provided
  2. Creates a contact record if contact information is provided
  3. Links the supplier to the authenticated user's store
  4. Assigns the supplier to specified supplier groups if `supplierGroupIds` is provided
  5. Validates that all supplier groups and price list exist before creating
</Note>

***

## Update Supplier

**PUT** `/suppliers/:id`

Update an existing supplier. You may update core fields, default price list, address, and contact. If address/contact do not exist, they will be created.

**Requires Authentication:** Bearer token in Authorization header

### Required Headers

| Key          | Value    | Required | Description                                       |
| ------------ | -------- | -------- | ------------------------------------------------- |
| `x-store-id` | `string` | Yes      | Store identifier (used by `@StoreId()` decorator) |

### Path Parameters

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

### Request Body

All fields are optional (partial updates supported):

| Field                | Type           | Required | Description                         |
| -------------------- | -------------- | -------- | ----------------------------------- |
| `name`               | `string`       | No       | Supplier name (max 255 characters)  |
| `description`        | `string`       | No       | Description (max 1000 characters)   |
| `note`               | `string`       | No       | Internal note (max 1000 characters) |
| `defaultPriceListId` | `string`       | No       | Default price list to assign        |
| `isActive`           | `boolean`      | No       | Active status                       |
| `address`            | `AddressInput` | No       | Address to create/update            |
| `contact`            | `ContactInput` | No       | Contact to create/update            |

### Request Body Example

```json theme={null}
{
  "name": "Global Traders Ltd. (Updated)",
  "description": "Updated description",
  "note": "Priority handling",
  "defaultPriceListId": "pl_789xyz",
  "isActive": true,
  "address": {
    "street": "456 New Ave",
    "city": "Rabat",
    "state": "RA",
    "postalCode": "10000",
    "country": "MA"
  },
  "contact": {
    "phone": "+212600999888",
    "fax": null,
    "email": "ops@globaltraders.com",
    "website": "https://globaltraders.com/contact"
  }
}
```

### Success Response

**Status Code:** `200 OK`

Returns the updated Supplier object (same shape as in "Get Supplier by ID").

### Possible Errors

| Code | Message                                                         |
| ---- | --------------------------------------------------------------- |
| 400  | Bad Request - Validation errors                                 |
| 403  | Forbidden - User does not have access to this store or supplier |
| 404  | Not Found - Supplier or price list not found                    |
| 500  | Internal Server Error - Failed to update supplier               |

### Example Requests

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT "https://joptic.jethings.com/suppliers/sup_abc123" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Updated Supplier Name",
      "description": "Updated description",
      "isActive": true
    }'
  ```

  ```javascript JavaScript theme={null}
  const updateData = {
    name: "Updated Supplier Name",
    description: "Updated description",
    isActive: true
  }

  const response = await fetch("https://joptic.jethings.com/suppliers/sup_abc123", {
    method: "PUT",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(updateData),
  })

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

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

  url = "https://joptic.jethings.com/suppliers/sup_abc123"

  update_data = {
      "name": "Updated Supplier Name",
      "description": "Updated description",
      "isActive": True
  }

  response = requests.put(
      url,
      headers={
          "x-store-id": "your-store-id",
          "Authorization": "Bearer <token>",
          "Content-Type": "application/json"
      },
      json=update_data
  )

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

<Note>
  When updating a supplier:

  * Only provided fields are updated (partial updates supported)
  * Address fields are updated in the linked address record, or a new address record is created if one doesn't exist
  * Contact fields are updated in the linked contact record, or a new contact record is created if one doesn't exist
  * The `updatedAt` timestamp is automatically updated
  * Price list is validated if provided
</Note>

***

## Delete Single Supplier

**DELETE** `/suppliers/:id`

Delete a single supplier by its ID. This is a permanent deletion.

**Requires Authentication:** Bearer token in Authorization header

**Rate Limited:** 5 requests per minute

### Path Parameters

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

### Success Response

**Status Code:** `200 OK`

```json theme={null}
{
  "message": "Supplier deleted successfully"
}
```

### Possible Errors

| Code | Message                                 |
| ---- | --------------------------------------- |
| 404  | Not Found - Supplier not found          |
| 429  | Too Many Requests - Rate limit exceeded |
| 500  | Internal Server Error                   |

### Example Requests

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

  ```javascript JavaScript theme={null}
  const response = await fetch("https://joptic.jethings.com/suppliers/sup_12345", {
    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/suppliers/sup_12345"

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

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

<Warning>
  This operation permanently deletes the supplier. Make sure to verify the supplier ID before deletion. Consider checking for dependent records (like purchase orders or invoices) before deleting.
</Warning>

***

## Bulk Delete Suppliers

**DELETE** `/suppliers`

Delete multiple suppliers by their IDs. This is a permanent deletion.

**Requires Authentication:** Bearer token in Authorization header

**Rate Limited:** 3 requests per minute

### Request Body

| Field | Type            | Required | Description            |
| ----- | --------------- | -------- | ---------------------- |
| `ids` | `string[]` UUID | ✅ Yes    | Supplier IDs to delete |

### Request Body Example

```json theme={null}
{
  "ids": ["sup_123", "sup_456", "sup_789"]
}
```

### Success Response

**Status Code:** `200 OK`

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

### Possible Errors

| Code | Message                                        |
| ---- | ---------------------------------------------- |
| 400  | Bad Request - No supplier IDs provided         |
| 404  | Not Found - No valid suppliers found to delete |
| 429  | Too Many Requests - Rate limit exceeded        |
| 500  | Internal Server Error                          |

### Example Requests

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

  ```javascript JavaScript theme={null}
  const deleteData = {
    ids: ["sup_123", "sup_456", "sup_789"]
  }

  const response = await fetch("https://joptic.jethings.com/suppliers", {
    method: "DELETE",
    headers: {
      "x-store-id": "your-store-id",
      Authorization: "Bearer <token>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(deleteData),
  })

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

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

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

  delete_data = {
      "ids": ["sup_123", "sup_456", "sup_789"]
  }

  response = requests.delete(
      url,
      headers={
          "x-store-id": "your-store-id",
          "Authorization": "Bearer <token>",
          "Content-Type": "application/json"
      },
      json=delete_data
  )

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

<Warning>
  This operation permanently deletes multiple suppliers. Only valid supplier IDs are deleted (invalid IDs are ignored). Returns the count of successfully deleted suppliers. Rate limited to 3 requests per minute for safety.
</Warning>

***

## Business Logic

### Supplier Creation Flow

<AccordionGroup>
  <Accordion title="Store Linking" icon="store">
    **Store Linking**

    When creating a supplier:

    1. The supplier is automatically linked to the authenticated user's store
    2. Store access is validated before creation
    3. The supplier can be linked to multiple stores via `storeIds` array
    4. Store linking is managed through `supplierStoreRelation` table
  </Accordion>

  <Accordion title="Supplier Group Assignment" icon="users">
    **Supplier Group Assignment**

    Suppliers can be assigned to groups:

    1. Provide `supplierGroupIds` array in the request
    2. All supplier group IDs are validated before creation
    3. If any group doesn't exist, creation fails with 404
    4. Groups are linked through `supplierGroupRelation` table
    5. A supplier can belong to multiple groups
  </Accordion>

  <Accordion title="Address Management" icon="map-pin">
    **Address Management**

    Address information is optional:

    1. If address is provided, a new address record is created
    2. All address fields are required if address object is provided
    3. The address is automatically linked to the supplier via `addressId`
    4. Address can be updated independently when updating the supplier
    5. If address doesn't exist and is provided in update, a new address is created
  </Accordion>

  <Accordion title="Contact Management" icon="phone">
    **Contact Management**

    Contact information is optional:

    1. If contact is provided, a new contact record is created
    2. Contact fields (phone, fax, email, website) are all optional
    3. The contact is automatically linked to the supplier via `contactId`
    4. Contact can be updated independently when updating the supplier
    5. If contact doesn't exist and is provided in update, a new contact is created
  </Accordion>

  <Accordion title="Default Price List" icon="tag">
    **Default Price List**

    Suppliers can have a default price list:

    1. Provide `defaultPriceListId` in the request
    2. Price list is validated before creation/update
    3. If price list doesn't exist, operation fails with 404
    4. Default price list can be changed or removed (set to null) on update
  </Accordion>

  <Accordion title="Store Access Validation" icon="shield">
    **Store Access Validation**

    Access control is enforced:

    1. User must have an active relation with the store to access its suppliers
    2. Store access is validated on every request
    3. Users can only create/update/delete suppliers for stores they have access to
    4. Supplier operations are scoped to the authenticated user's accessible stores
  </Accordion>

  <Accordion title="Partial Updates" icon="edit">
    **Partial Updates**

    The update endpoint supports flexible partial updates:

    1. Only provided fields are updated
    2. Address fields can be updated independently
    3. Contact fields can be updated independently
    4. Supplier fields (name, description, note, defaultPriceListId, isActive) can be updated independently
    5. This allows fine-grained control over what gets updated
  </Accordion>
</AccordionGroup>

### Supplier States

<CardGroup cols={2}>
  <Card title="Active Supplier" icon="check-circle">
    **Active** (`isActive: true`)

    <br />

    Supplier is currently in use and available for operations
  </Card>

  <Card title="Grouped Supplier" icon="users">
    **Grouped** (has supplierGroups)

    <br />

    Supplier belongs to one or more supplier groups
  </Card>

  <Card title="With Address" icon="map-pin">
    **Address Information**

    <br />

    Supplier has associated address details
  </Card>

  <Card title="With Contact" icon="phone">
    **Contact Information**

    <br />

    Supplier has associated contact details (phone, email, website)
  </Card>
</CardGroup>

***

## Error Responses

All endpoints may return standard HTTP error codes:

* `400 Bad Request` - Invalid request parameters or body
* `403 Forbidden` - User does not have access to the store or supplier
* `404 Not Found` - Resource not found (supplier, store, price list, supplier group, etc.)
* `429 Too Many Requests` - Rate limit exceeded
* `500 Internal Server Error` - Server error

### Error Response Format

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

### Common Error Scenarios

**403 Forbidden - Store Access**

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

Occurs when the user doesn't have an active relation with the specified store.

**403 Forbidden - Supplier Access**

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

Occurs when the supplier is not linked to the user's accessible store.

**404 Not Found - Supplier**

```json theme={null}
{
  "statusCode": 404,
  "message": "Supplier not found"
}
```

Occurs when the supplier ID doesn't exist.

**404 Not Found - Price List or Group**

```json theme={null}
{
  "statusCode": 404,
  "message": "Price list not found"
}
```

Occurs when a referenced price list or supplier group doesn't exist.

**400 Bad Request - Validation Error**

```json theme={null}
{
  "statusCode": 400,
  "message": ["name must be a string", "address.street must be a string"],
  "error": "Bad Request"
}
```

Occurs when required fields are missing or data types are incorrect.

***

## Usage Examples

### Example 1: Create a supplier with all information

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://joptic.jethings.com/suppliers" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Lens Supplier Inc",
      "description": "Premium lens supplier",
      "supplierGroupIds": ["grp_1"],
      "defaultPriceListId": "pl_123",
      "address": {
        "street": "123 Optical Street",
        "city": "New York",
        "state": "NY",
        "postalCode": "10001",
        "country": "United States"
      },
      "contact": {
        "phone": "+1-555-123-4567",
        "email": "contact@lenssupplier.com"
      }
    }'
  ```
</CodeGroup>

### Example 2: Create a supplier with minimal information

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://joptic.jethings.com/suppliers" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Basic Supplier"
    }'
  ```
</CodeGroup>

### Example 3: Update supplier contact only

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT "https://joptic.jethings.com/suppliers/sup_123" \
    -H "x-store-id: your-store-id" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "contact": {
        "phone": "+1-555-999-8888",
        "email": "newemail@supplier.com"
      }
    }'
  ```
</CodeGroup>

### Example 4: Bulk delete suppliers

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

***

## Best Practices

<AccordionGroup>
  <Accordion title="Supplier Organization">
    * Use supplier groups to organize suppliers by category or type
    * Assign default price lists for easier price management
    * Keep supplier names descriptive and consistent
    * Use notes field for important supplier-specific information
  </Accordion>

  <Accordion title="Address and Contact">
    * Provide complete address information for accurate location tracking
    * Keep contact information up to date for easy communication
    * Use consistent address formats across suppliers
    * Update contact information when personnel changes
  </Accordion>

  <Accordion title="Pagination">
    * Use pagination when fetching large lists of suppliers
    * Default limit of 10 is suitable for most use cases
    * Maximum limit of 100 should be used sparingly
    * Always check `hasNext` and `hasPrev` for navigation
  </Accordion>

  <Accordion title="Filtering and Search">
    * Use `search` for general name/description searches
    * Use `name` for specific name filtering
    * Combine filters for precise results
    * Use `isActive` to filter active/inactive suppliers
  </Accordion>

  <Accordion title="Partial Updates">
    * Only send fields that need to be updated
    * Update address and contact information independently when possible
    * Use partial updates to avoid overwriting unchanged data
    * Verify supplier ID before updating
  </Accordion>

  <Accordion title="Deletion">
    * Verify supplier ID before deletion
    * Check for dependent records (purchase orders, invoices) before deleting
    * Use bulk delete for multiple suppliers (rate limited to 3 per minute)
    * Single delete is rate limited to 5 per minute
    * Deletion is permanent and cannot be undone
  </Accordion>

  <Accordion title="Rate Limiting">
    * DELETE endpoints are rate limited for safety
    * Single delete: 5 requests per minute
    * Bulk delete: 3 requests per minute
    * Implement retry logic with exponential backoff for rate limit errors
  </Accordion>
</AccordionGroup>

***

## Related Endpoints

Suppliers work in conjunction with other inventory management endpoints:

* **Supplier Groups API** (`/supplier-groups`) - Manage supplier groups and assign suppliers
* **Price List API** (`/price-lists`) - Manage price lists that can be assigned to suppliers
* **Purchase Invoice API** (`/purchase-invoices`) - Create purchase invoices from suppliers
* **Purchase Order API** (`/purchase-orders`) - Create purchase orders with suppliers

<Info>
  Suppliers are essential for managing vendor relationships in optical stores. They can be organized into groups, assigned default price lists, and linked to multiple stores. Use suppliers to track vendor information and manage purchasing relationships.
</Info>

***

## Summary

| Endpoint         | Method   | Description                                    |
| ---------------- | -------- | ---------------------------------------------- |
| `/suppliers`     | `GET`    | Retrieve paginated list of suppliers           |
| `/suppliers/:id` | `GET`    | Retrieve a single supplier by ID               |
| `/suppliers`     | `POST`   | Create a new supplier                          |
| `/suppliers/:id` | `PUT`    | Update an existing supplier (partial updates)  |
| `/suppliers/:id` | `DELETE` | Delete a single supplier (rate limited: 5/min) |
| `/suppliers`     | `DELETE` | Bulk delete suppliers (rate limited: 3/min)    |

***

<CardGroup cols={2}>
  <Card title="Supplier Groups API" icon="users" href="/j-optic/supplier-groups-api">
    Manage supplier groups and assignments
  </Card>

  <Card title="Price List API" icon="tag" href="/j-optic/price-list-api">
    Manage price lists for suppliers
  </Card>
</CardGroup>
