# Tham chiếu: Sản phẩm

Sản phẩm, mẫu mã và danh mục. Mọi đường dẫn dưới `https://danix.vn/api/open/v1`. Đặc tả máy đọc: [openapi.json](/openapi.json).

## Liệt kê sản phẩm {#listProducts}

`GET /products`

Phân trang theo con trỏ, sắp theo lần sửa tăng dần. Dòng danh sách gọn; lấy mẫu mã bằng `GET /products/{id}`.

**Quyền cần có:** khoá có quyền `pos.products.read`.

**Tham số query**

| Tên | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `limit` | integer | không | Số dòng mỗi trang, từ 1 đến 100. Mặc định 50. |
| `cursor` | string | không | Con trỏ lấy từ `nextCursor` của trang trước, để lật trang trong CÙNG một lượt đọc (giữ nguyên các tham số khác). Không tự dựng, không lưu để nối lượt đồng bộ sau. |
| `updatedSince` | string (date-time) | không | Chỉ lấy bản ghi có `updatedAt` từ thời điểm này (ISO 8601 UTC), tính cả mốc. Đây là cách đồng bộ tăng dần: có tham số này thì danh sách chỉ trả bản ghi đã qua khoảng trễ an toàn (trần thời gian tua lại kho của shop cộng 15 giây, mặc định 2 phút 15 giây). |
| `includeDeleted` | "true" \| "false" | không | `true` để lấy cả bản ghi đã xoá (có `deletedAt`). Mặc định `false`. |
| `search` | string | không | Tìm theo tên hoặc mã sản phẩm. |
| `categoryId` | string (uuid) | không | Chỉ lấy sản phẩm trong danh mục này. |

**Ví dụ yêu cầu**

```bash
curl -X GET "https://danix.vn/api/open/v1/products" \
  -H "Authorization: Bearer dnx_live_…"
```

**Phản hồi mẫu (200)**

```json
{
  "data": [
    {
      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
      "code": "SP0001",
      "name": "Áo thun cổ tròn",
      "type": "simple",
      "isActive": true,
      "categoryNames": [
        "Áo"
      ],
      "variantCount": 1,
      "imageUrl": null,
      "priceFrom": "225000",
      "priceTo": "225000",
      "updatedAt": "2026-10-02T03:15:00.000Z",
      "deletedAt": null
    }
  ],
  "nextCursor": null
}
```

**Trường của phản hồi**

| Trường | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `data` | array<object> | có | Các bản ghi của trang này. |
| `data[].id` | string (uuid) | có | Mã sản phẩm. |
| `data[].code` | string | có | Mã sản phẩm, duy nhất trong shop. |
| `data[].name` | string | có | Tên sản phẩm. |
| `data[].type` | "simple" \| "service" | có | Loại: `simple` (hàng hoá) hoặc `service` (dịch vụ). |
| `data[].isActive` | boolean | có | Sản phẩm còn bán. |
| `data[].categoryNames` | array<string> | có | Tên các danh mục chứa sản phẩm. |
| `data[].variantCount` | integer | có | Số mẫu mã. |
| `data[].imageUrl` | string hoặc null | có | Ảnh đại diện. |
| `data[].priceFrom` | string hoặc null | có | Giá bán thấp nhất trong các mẫu mã. Chuỗi thập phân hoặc `null`. |
| `data[].priceTo` | string hoặc null | có | Giá bán cao nhất trong các mẫu mã. Chuỗi thập phân hoặc `null`. |
| `data[].updatedAt` | string (date-time) | có | Lần sửa gần nhất. Danh sách sắp theo `(updatedAt, id)`; dùng làm mốc `updatedSince`. ISO 8601, múi giờ UTC. |
| `data[].deletedAt` | string (date-time) hoặc null | có | Thời điểm xoá mềm; `null` nếu chưa xoá. Chỉ khác `null` khi gọi với `includeDeleted=true`. ISO 8601 UTC hoặc `null`. |
| `nextCursor` | string hoặc null | có | Con trỏ của trang kế; `null` khi đã hết dữ liệu. |

**Lỗi**

| Trạng thái | Mã `code` | Ý nghĩa |
| --- | --- | --- |
| 401 | [`invalid-api-key`](/developers/errors#invalid-api-key) | Khoá API thiếu, sai hoặc đã bị thu hồi |
| 403 | [`insufficient-permission`](/developers/errors#insufficient-permission) | Khoá API không có quyền thực hiện thao tác này |
| 403 | [`shop-suspended`](/developers/errors#shop-suspended) | Shop đang bị đình chỉ |
| 403 | [`automation-not-active`](/developers/errors#automation-not-active) | Shop chưa bật tính năng Tự động hoá |
| 429 | [`rate-limited`](/developers/errors#rate-limited) | Vượt hạn mức gọi API, hãy chờ rồi thử lại |
| 500 | [`internal-error`](/developers/errors#internal-error) | Lỗi hệ thống |
| 400 | [`validation-failed`](/developers/errors#validation-failed) | Dữ liệu gửi lên không hợp lệ |
| 400 | [`invalid-cursor`](/developers/errors#invalid-cursor) | Con trỏ phân trang không hợp lệ |

Định dạng lỗi và bảng mọi mã `code`: [Lỗi](/developers/errors).

## Tạo sản phẩm {#createProduct}

`POST /products`

Tạo sản phẩm kèm ít nhất một mẫu mã. Sản phẩm nhiều mẫu mã: mỗi mẫu mang `attributes` theo tên (`{ "Size": "M" }`), mọi mẫu cùng một bộ thuộc tính; thuộc tính hay giá trị chưa có thì được tạo (cần thêm quyền `pos.products.update`).

**Quyền cần có:** khoá có quyền `pos.products.create` và quyền `pos.products.read`.

**Quyền thêm theo trường:** `pos.products.update` — chỉ cần khi thân dùng trường tương ứng (xem mô tả ở trên).

**Thân yêu cầu (JSON)**

| Trường | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `code` | string | không | Mã sản phẩm. Bỏ trống thì hệ thống tự cấp. |
| `name` | string | có | Tên sản phẩm. |
| `description` | string hoặc null | không | Mô tả. |
| `categoryIds` | array<string (uuid)> | không | Các danh mục. |
| `type` | "simple" \| "service" | không | Loại sản phẩm. Mặc định `simple`. |
| `variants` | array<object> | có | Các mẫu mã, ít nhất một. Nhiều mẫu mã thì mỗi mẫu mang `attributes` để phân biệt; thứ tự thuộc tính của sản phẩm theo thứ tự khoá ở mẫu mã đầu tiên. |
| `variants[].id` | string (uuid) | không | Mã mẫu mã cần sửa; bỏ trống để thêm mẫu mã mới. |
| `variants[].code` | string | không | Mã mẫu mã (SKU). Bỏ trống thì hệ thống tự cấp. |
| `variants[].barcode` | string hoặc null | không | Mã vạch. |
| `variants[].price` | string | không | Giá bán. |
| `variants[].weightGrams` | string hoặc null | không | Cân nặng, gram. |
| `variants[].isActive` | boolean | không | Mẫu mã còn bán. |
| `variants[].attributes` | object | không | Thuộc tính theo TÊN, ví dụ `{ "Size": "M", "Màu": "Đỏ" }` (tên và giá trị không phân biệt hoa thường; chưa có thì được tạo, cần thêm quyền `pos.products.update`). Mọi mẫu mã của một sản phẩm mang đúng cùng một bộ thuộc tính, không hai mẫu trùng tổ hợp. Gửi kèm `id` là đổi tổ hợp của mẫu mã ấy. |

**Ví dụ yêu cầu**

```bash
curl -X POST "https://danix.vn/api/open/v1/products" \
  -H "Authorization: Bearer dnx_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Áo thun cổ tròn",
  "variants": [
    {
      "code": "AO-THUN-M",
      "price": "225000",
      "attributes": {
        "Size": "M"
      }
    }
  ]
}'
```

**Phản hồi mẫu (201)**

```json
{
  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
  "code": "SP0001",
  "name": "Áo thun cổ tròn",
  "description": null,
  "type": "simple",
  "isActive": true,
  "categoryIds": [
    "018f3b8e-1c2d-7a4b-9c3d-000000000801"
  ],
  "variants": [
    {
      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
      "code": "AO-THUN-M",
      "barcode": null,
      "price": "225000",
      "weightGrams": "200",
      "lengthCm": null,
      "widthCm": null,
      "heightCm": null,
      "isActive": true,
      "attributes": {
        "Size": "M"
      }
    }
  ],
  "images": [],
  "createdAt": "2026-10-02T03:15:00.000Z",
  "deletedAt": null
}
```

**Trường của phản hồi**

| Trường | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `id` | string (uuid) | có | Mã sản phẩm. |
| `code` | string | có | Mã sản phẩm, duy nhất trong shop. |
| `name` | string | có | Tên sản phẩm. |
| `description` | string hoặc null | có | Mô tả. |
| `type` | "simple" \| "service" | có | Loại: `simple` (hàng hoá) hoặc `service` (dịch vụ). |
| `isActive` | boolean | có | Sản phẩm còn bán. |
| `categoryIds` | array<string (uuid)> | có | Các danh mục chứa sản phẩm. |
| `variants` | array<object> | có | Các mẫu mã. |
| `variants[].id` | string (uuid) | có | Mã mẫu mã. |
| `variants[].code` | string | có | Mã mẫu mã (SKU), duy nhất trong shop. |
| `variants[].barcode` | string hoặc null | có | Mã vạch. |
| `variants[].price` | string | có | Giá bán. Chuỗi thập phân, ví dụ "150000". |
| `variants[].weightGrams` | string hoặc null | có | Cân nặng, gram. Chuỗi thập phân. |
| `variants[].lengthCm` | string hoặc null | có | Chiều dài, cm. |
| `variants[].widthCm` | string hoặc null | có | Chiều rộng, cm. |
| `variants[].heightCm` | string hoặc null | có | Chiều cao, cm. |
| `variants[].isActive` | boolean | có | Mẫu mã còn bán. |
| `variants[].attributes` | object | có | Thuộc tính của mẫu mã theo TÊN, ví dụ `{ "Size": "M", "Màu": "Đỏ" }`, theo thứ tự thuộc tính của sản phẩm. Rỗng với sản phẩm không có thuộc tính. |
| `images` | array<object> | có | Ảnh và video. |
| `images[].id` | string (uuid) | có | Mã tệp. |
| `images[].url` | string | có | Đường dẫn tuyệt đối tới tệp. |
| `images[].kind` | "image" \| "video" | có | Loại tệp. |
| `images[].variantIds` | array<string (uuid)> | có | Các mẫu mã dùng tệp này; rỗng là tệp chung. |
| `createdAt` | string (date-time) | có | Thời điểm tạo. ISO 8601, múi giờ UTC. |
| `deletedAt` | string (date-time) hoặc null | có | Thời điểm xoá mềm; `null` nếu chưa xoá. ISO 8601 UTC hoặc `null`. |

**Lỗi**

| Trạng thái | Mã `code` | Ý nghĩa |
| --- | --- | --- |
| 401 | [`invalid-api-key`](/developers/errors#invalid-api-key) | Khoá API thiếu, sai hoặc đã bị thu hồi |
| 403 | [`insufficient-permission`](/developers/errors#insufficient-permission) | Khoá API không có quyền thực hiện thao tác này |
| 403 | [`shop-suspended`](/developers/errors#shop-suspended) | Shop đang bị đình chỉ |
| 403 | [`automation-not-active`](/developers/errors#automation-not-active) | Shop chưa bật tính năng Tự động hoá |
| 429 | [`rate-limited`](/developers/errors#rate-limited) | Vượt hạn mức gọi API, hãy chờ rồi thử lại |
| 500 | [`internal-error`](/developers/errors#internal-error) | Lỗi hệ thống |
| 402 | [`subscription-expired`](/developers/errors#subscription-expired) | Gói cước của shop đã hết hạn, chỉ còn quyền đọc |
| 400 | [`validation-failed`](/developers/errors#validation-failed) | Dữ liệu gửi lên không hợp lệ |
| 413 | [`payload-too-large`](/developers/errors#payload-too-large) | Thân yêu cầu vượt quá dung lượng cho phép |
| 415 | [`unsupported-media-type`](/developers/errors#unsupported-media-type) | Thân yêu cầu phải là JSON (`Content-Type: application/json`) |
| 409 | [`duplicate-code`](/developers/errors#duplicate-code) | Mã đã tồn tại trong shop |
| 409 | [`conflict`](/developers/errors#conflict) | Thao tác xung đột với trạng thái hiện tại |

Định dạng lỗi và bảng mọi mã `code`: [Lỗi](/developers/errors).

## Chi tiết một sản phẩm {#getProduct}

`GET /products/{id}`

Trả sản phẩm kèm mẫu mã, ảnh và danh mục.

**Quyền cần có:** khoá có quyền `pos.products.read`.

**Tham số đường dẫn**

| Tên | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `id` | string (uuid) | có | Mã định danh (UUID) của tài nguyên. |

**Ví dụ yêu cầu**

```bash
curl -X GET "https://danix.vn/api/open/v1/products/{id}" \
  -H "Authorization: Bearer dnx_live_…"
```

Thay `{id}` trong địa chỉ bằng mã thật của bản ghi, và `dnx_live_…` bằng khoá của bạn.

**Phản hồi mẫu (200)**

```json
{
  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
  "code": "SP0001",
  "name": "Áo thun cổ tròn",
  "description": null,
  "type": "simple",
  "isActive": true,
  "categoryIds": [
    "018f3b8e-1c2d-7a4b-9c3d-000000000801"
  ],
  "variants": [
    {
      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
      "code": "AO-THUN-M",
      "barcode": null,
      "price": "225000",
      "weightGrams": "200",
      "lengthCm": null,
      "widthCm": null,
      "heightCm": null,
      "isActive": true,
      "attributes": {
        "Size": "M"
      }
    }
  ],
  "images": [],
  "createdAt": "2026-10-02T03:15:00.000Z",
  "deletedAt": null
}
```

**Trường của phản hồi**

| Trường | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `id` | string (uuid) | có | Mã sản phẩm. |
| `code` | string | có | Mã sản phẩm, duy nhất trong shop. |
| `name` | string | có | Tên sản phẩm. |
| `description` | string hoặc null | có | Mô tả. |
| `type` | "simple" \| "service" | có | Loại: `simple` (hàng hoá) hoặc `service` (dịch vụ). |
| `isActive` | boolean | có | Sản phẩm còn bán. |
| `categoryIds` | array<string (uuid)> | có | Các danh mục chứa sản phẩm. |
| `variants` | array<object> | có | Các mẫu mã. |
| `variants[].id` | string (uuid) | có | Mã mẫu mã. |
| `variants[].code` | string | có | Mã mẫu mã (SKU), duy nhất trong shop. |
| `variants[].barcode` | string hoặc null | có | Mã vạch. |
| `variants[].price` | string | có | Giá bán. Chuỗi thập phân, ví dụ "150000". |
| `variants[].weightGrams` | string hoặc null | có | Cân nặng, gram. Chuỗi thập phân. |
| `variants[].lengthCm` | string hoặc null | có | Chiều dài, cm. |
| `variants[].widthCm` | string hoặc null | có | Chiều rộng, cm. |
| `variants[].heightCm` | string hoặc null | có | Chiều cao, cm. |
| `variants[].isActive` | boolean | có | Mẫu mã còn bán. |
| `variants[].attributes` | object | có | Thuộc tính của mẫu mã theo TÊN, ví dụ `{ "Size": "M", "Màu": "Đỏ" }`, theo thứ tự thuộc tính của sản phẩm. Rỗng với sản phẩm không có thuộc tính. |
| `images` | array<object> | có | Ảnh và video. |
| `images[].id` | string (uuid) | có | Mã tệp. |
| `images[].url` | string | có | Đường dẫn tuyệt đối tới tệp. |
| `images[].kind` | "image" \| "video" | có | Loại tệp. |
| `images[].variantIds` | array<string (uuid)> | có | Các mẫu mã dùng tệp này; rỗng là tệp chung. |
| `createdAt` | string (date-time) | có | Thời điểm tạo. ISO 8601, múi giờ UTC. |
| `deletedAt` | string (date-time) hoặc null | có | Thời điểm xoá mềm; `null` nếu chưa xoá. ISO 8601 UTC hoặc `null`. |

**Lỗi**

| Trạng thái | Mã `code` | Ý nghĩa |
| --- | --- | --- |
| 401 | [`invalid-api-key`](/developers/errors#invalid-api-key) | Khoá API thiếu, sai hoặc đã bị thu hồi |
| 403 | [`insufficient-permission`](/developers/errors#insufficient-permission) | Khoá API không có quyền thực hiện thao tác này |
| 403 | [`shop-suspended`](/developers/errors#shop-suspended) | Shop đang bị đình chỉ |
| 403 | [`automation-not-active`](/developers/errors#automation-not-active) | Shop chưa bật tính năng Tự động hoá |
| 429 | [`rate-limited`](/developers/errors#rate-limited) | Vượt hạn mức gọi API, hãy chờ rồi thử lại |
| 500 | [`internal-error`](/developers/errors#internal-error) | Lỗi hệ thống |
| 404 | [`not-found`](/developers/errors#not-found) | Không tìm thấy tài nguyên |

Định dạng lỗi và bảng mọi mã `code`: [Lỗi](/developers/errors).

## Sửa sản phẩm {#updateProduct}

`PATCH /products/{id}`

Chỉ gửi trường cần đổi. `variants` vá TỪNG PHẦN: mẫu mã có `id` chỉ đổi các trường gửi lên, mẫu mã không có `id` được thêm vào cuối (kèm `attributes` theo tên), mẫu mã đã có mà không nhắc tới giữ nguyên. Gỡ mẫu mã chưa mở ở phiên bản này.

**Quyền cần có:** khoá có quyền `pos.products.update` và quyền `pos.products.read`.

**Tham số đường dẫn**

| Tên | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `id` | string (uuid) | có | Mã định danh (UUID) của tài nguyên. |

**Thân yêu cầu (JSON)**

| Trường | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `name` | string | không | Tên sản phẩm. |
| `description` | string hoặc null | không | Mô tả. |
| `categoryIds` | array<string (uuid)> | không | Thay toàn bộ danh mục. |
| `variants` | array<object> | không | Vá TỪNG PHẦN: mẫu mã có `id` được sửa (chỉ các trường gửi lên), mẫu mã không có `id` được thêm vào cuối; mẫu mã đã có mà không nhắc tới thì GIỮ NGUYÊN. Gỡ mẫu mã chưa mở ở phiên bản này. |
| `variants[].id` | string (uuid) | không | Mã mẫu mã cần sửa; bỏ trống để thêm mẫu mã mới. |
| `variants[].code` | string | không | Mã mẫu mã (SKU). Bỏ trống thì hệ thống tự cấp. |
| `variants[].barcode` | string hoặc null | không | Mã vạch. |
| `variants[].price` | string | không | Giá bán. |
| `variants[].weightGrams` | string hoặc null | không | Cân nặng, gram. |
| `variants[].isActive` | boolean | không | Mẫu mã còn bán. |
| `variants[].attributes` | object | không | Thuộc tính theo TÊN, ví dụ `{ "Size": "M", "Màu": "Đỏ" }` (tên và giá trị không phân biệt hoa thường; chưa có thì được tạo, cần thêm quyền `pos.products.update`). Mọi mẫu mã của một sản phẩm mang đúng cùng một bộ thuộc tính, không hai mẫu trùng tổ hợp. Gửi kèm `id` là đổi tổ hợp của mẫu mã ấy. |

**Ví dụ yêu cầu**

```bash
curl -X PATCH "https://danix.vn/api/open/v1/products/{id}" \
  -H "Authorization: Bearer dnx_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Áo thun cổ tròn cotton",
  "variants": [
    {
      "code": "AO-THUN-L",
      "price": "235000",
      "attributes": {
        "Size": "L"
      }
    }
  ]
}'
```

Thay `{id}` trong địa chỉ bằng mã thật của bản ghi, và `dnx_live_…` bằng khoá của bạn.

**Phản hồi mẫu (200)**

```json
{
  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
  "code": "SP0001",
  "name": "Áo thun cổ tròn",
  "description": null,
  "type": "simple",
  "isActive": true,
  "categoryIds": [
    "018f3b8e-1c2d-7a4b-9c3d-000000000801"
  ],
  "variants": [
    {
      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
      "code": "AO-THUN-M",
      "barcode": null,
      "price": "225000",
      "weightGrams": "200",
      "lengthCm": null,
      "widthCm": null,
      "heightCm": null,
      "isActive": true,
      "attributes": {
        "Size": "M"
      }
    }
  ],
  "images": [],
  "createdAt": "2026-10-02T03:15:00.000Z",
  "deletedAt": null
}
```

**Trường của phản hồi**

| Trường | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `id` | string (uuid) | có | Mã sản phẩm. |
| `code` | string | có | Mã sản phẩm, duy nhất trong shop. |
| `name` | string | có | Tên sản phẩm. |
| `description` | string hoặc null | có | Mô tả. |
| `type` | "simple" \| "service" | có | Loại: `simple` (hàng hoá) hoặc `service` (dịch vụ). |
| `isActive` | boolean | có | Sản phẩm còn bán. |
| `categoryIds` | array<string (uuid)> | có | Các danh mục chứa sản phẩm. |
| `variants` | array<object> | có | Các mẫu mã. |
| `variants[].id` | string (uuid) | có | Mã mẫu mã. |
| `variants[].code` | string | có | Mã mẫu mã (SKU), duy nhất trong shop. |
| `variants[].barcode` | string hoặc null | có | Mã vạch. |
| `variants[].price` | string | có | Giá bán. Chuỗi thập phân, ví dụ "150000". |
| `variants[].weightGrams` | string hoặc null | có | Cân nặng, gram. Chuỗi thập phân. |
| `variants[].lengthCm` | string hoặc null | có | Chiều dài, cm. |
| `variants[].widthCm` | string hoặc null | có | Chiều rộng, cm. |
| `variants[].heightCm` | string hoặc null | có | Chiều cao, cm. |
| `variants[].isActive` | boolean | có | Mẫu mã còn bán. |
| `variants[].attributes` | object | có | Thuộc tính của mẫu mã theo TÊN, ví dụ `{ "Size": "M", "Màu": "Đỏ" }`, theo thứ tự thuộc tính của sản phẩm. Rỗng với sản phẩm không có thuộc tính. |
| `images` | array<object> | có | Ảnh và video. |
| `images[].id` | string (uuid) | có | Mã tệp. |
| `images[].url` | string | có | Đường dẫn tuyệt đối tới tệp. |
| `images[].kind` | "image" \| "video" | có | Loại tệp. |
| `images[].variantIds` | array<string (uuid)> | có | Các mẫu mã dùng tệp này; rỗng là tệp chung. |
| `createdAt` | string (date-time) | có | Thời điểm tạo. ISO 8601, múi giờ UTC. |
| `deletedAt` | string (date-time) hoặc null | có | Thời điểm xoá mềm; `null` nếu chưa xoá. ISO 8601 UTC hoặc `null`. |

**Lỗi**

| Trạng thái | Mã `code` | Ý nghĩa |
| --- | --- | --- |
| 401 | [`invalid-api-key`](/developers/errors#invalid-api-key) | Khoá API thiếu, sai hoặc đã bị thu hồi |
| 403 | [`insufficient-permission`](/developers/errors#insufficient-permission) | Khoá API không có quyền thực hiện thao tác này |
| 403 | [`shop-suspended`](/developers/errors#shop-suspended) | Shop đang bị đình chỉ |
| 403 | [`automation-not-active`](/developers/errors#automation-not-active) | Shop chưa bật tính năng Tự động hoá |
| 429 | [`rate-limited`](/developers/errors#rate-limited) | Vượt hạn mức gọi API, hãy chờ rồi thử lại |
| 500 | [`internal-error`](/developers/errors#internal-error) | Lỗi hệ thống |
| 402 | [`subscription-expired`](/developers/errors#subscription-expired) | Gói cước của shop đã hết hạn, chỉ còn quyền đọc |
| 400 | [`validation-failed`](/developers/errors#validation-failed) | Dữ liệu gửi lên không hợp lệ |
| 413 | [`payload-too-large`](/developers/errors#payload-too-large) | Thân yêu cầu vượt quá dung lượng cho phép |
| 415 | [`unsupported-media-type`](/developers/errors#unsupported-media-type) | Thân yêu cầu phải là JSON (`Content-Type: application/json`) |
| 404 | [`not-found`](/developers/errors#not-found) | Không tìm thấy tài nguyên |
| 409 | [`duplicate-code`](/developers/errors#duplicate-code) | Mã đã tồn tại trong shop |
| 409 | [`conflict`](/developers/errors#conflict) | Thao tác xung đột với trạng thái hiện tại |

Định dạng lỗi và bảng mọi mã `code`: [Lỗi](/developers/errors).

## Danh mục sản phẩm {#listCategories}

`GET /categories`

Toàn bộ danh mục của shop, dạng cây phẳng với `parentId`.

**Quyền cần có:** khoá có quyền `pos.products.read`.

**Ví dụ yêu cầu**

```bash
curl -X GET "https://danix.vn/api/open/v1/categories" \
  -H "Authorization: Bearer dnx_live_…"
```

**Phản hồi mẫu (200)**

```json
{
  "data": [
    {
      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000801",
      "parentId": null,
      "name": "Áo",
      "slug": "ao"
    }
  ]
}
```

**Trường của phản hồi**

| Trường | Kiểu | Bắt buộc | Mô tả |
| --- | --- | --- | --- |
| `data` | array<object> | có | Các danh mục. |
| `data[].id` | string (uuid) | có | Mã danh mục. |
| `data[].parentId` | string (uuid) hoặc null | có | Danh mục cha. |
| `data[].name` | string | có | Tên danh mục. |
| `data[].slug` | string | có | Tên ngắn trong đường dẫn. |

**Lỗi**

| Trạng thái | Mã `code` | Ý nghĩa |
| --- | --- | --- |
| 401 | [`invalid-api-key`](/developers/errors#invalid-api-key) | Khoá API thiếu, sai hoặc đã bị thu hồi |
| 403 | [`insufficient-permission`](/developers/errors#insufficient-permission) | Khoá API không có quyền thực hiện thao tác này |
| 403 | [`shop-suspended`](/developers/errors#shop-suspended) | Shop đang bị đình chỉ |
| 403 | [`automation-not-active`](/developers/errors#automation-not-active) | Shop chưa bật tính năng Tự động hoá |
| 429 | [`rate-limited`](/developers/errors#rate-limited) | Vượt hạn mức gọi API, hãy chờ rồi thử lại |
| 500 | [`internal-error`](/developers/errors#internal-error) | Lỗi hệ thống |

Định dạng lỗi và bảng mọi mã `code`: [Lỗi](/developers/errors).
