{
  "openapi": "3.0.3",
  "paths": {
    "/api/open/v1/shop": {
      "get": {
        "description": "Trả shop mà khoá API thuộc về, tên ứng dụng kết nối, các quyền được cấp, các trang chat được dùng và cờ chỉ đọc. Đây là cách nhanh nhất để kiểm khoá hoạt động.",
        "summary": "Thông tin shop của khoá",
        "tags": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Shop của khoá API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Shop"
                },
                "example": {
                  "id": "018f3b8e-0000-7000-8000-00000000a001",
                  "slug": "shop-mau",
                  "name": "Shop Mẫu",
                  "application": {
                    "name": "Đồng bộ kế toán",
                    "permissions": [
                      "pos.orders.read"
                    ],
                    "pageIds": []
                  },
                  "readOnly": false
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getShop",
        "x-required-permissions": {
          "anyOf": [],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          }
        ]
      }
    },
    "/api/open/v1/geo/units": {
      "get": {
        "description": "Duyệt cây tỉnh → (huyện) → xã của một hệ địa giới, mỗi lượt một tầng: vắng `parentId` là các tỉnh, có `parentId` là các đơn vị con của nó. Không phân trang (mỗi tầng tối đa vài trăm dòng), sắp theo tên. Dùng `id` của kết quả làm `provinceUnitId`, `districtUnitId`, `wardUnitId` khi tạo đơn hay thêm địa chỉ khách — có mã thì đơn đẩy được sang hãng vận chuyển. `parentId` không có hoặc khác hệ trả `not-found`.",
        "summary": "Tra danh mục đơn vị hành chính",
        "tags": [],
        "parameters": [
          {
            "name": "system",
            "in": "query",
            "description": "Hệ địa giới cần tra: `old` hoặc `new`. Bắt buộc.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "old",
                "new"
              ]
            }
          },
          {
            "name": "parentId",
            "in": "query",
            "description": "Mã đơn vị cha. Vắng thì trả các tỉnh, thành phố của hệ.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Lọc theo tên, không phân biệt hoa thường và dấu, trong tầng đang tra.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một tầng của danh mục.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GeoUnit"
                      },
                      "description": "Các đơn vị của tầng."
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "description": "Một tầng của danh mục."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000001501",
                      "system": "old",
                      "level": 1,
                      "parentId": null,
                      "name": "Đà Nẵng"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listGeoUnits",
        "x-required-permissions": {
          "anyOf": [],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/orders": {
      "get": {
        "description": "Phân trang theo con trỏ, sắp theo lần sửa tăng dần. Đồng bộ tăng dần bằng `updatedSince` (lật trang trong một lượt bằng `nextCursor`); dòng danh sách không có `lines`, `payments` và `shippingAddress` — lấy chi tiết bằng `GET /orders/{id}`.",
        "summary": "Liệt kê đơn hàng",
        "tags": [],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Số dòng mỗi trang, từ 1 đến 100. Mặc định 50.",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "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.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "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).",
            "schema": {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?!0000-)"
            }
          },
          {
            "name": "includeDeleted",
            "in": "query",
            "description": "`true` để lấy cả bản ghi đã xoá (có `deletedAt`). Mặc định `false`.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Chỉ lấy đơn ở trạng thái này.",
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "waiting_stock",
                "confirmed",
                "packing",
                "ready_to_ship",
                "shipped",
                "delivered",
                "paid",
                "returning",
                "partially_returned",
                "returned",
                "cancelled",
                "deleted"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "description": "Chỉ lấy đơn của khách này.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "code",
            "in": "query",
            "description": "Tìm đúng mã đơn.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một trang đơn hàng.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      },
                      "description": "Các bản ghi của trang này."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Con trỏ của trang kế; `null` khi đã hết dữ liệu.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "Một trang đơn hàng."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                      "code": "DH1024",
                      "status": "confirmed",
                      "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                      "warehouseName": "Kho chính",
                      "customerId": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                      "salesChannelId": "018f3b8e-1c2d-7a4b-9c3d-000000000301",
                      "salesChannelName": "Facebook",
                      "receivedAtShop": false,
                      "billFullName": "Nguyễn Văn An",
                      "billPhone": "0901234567",
                      "billEmail": null,
                      "totalPrice": "450000",
                      "discount": "0",
                      "shippingFee": "30000",
                      "freeShipping": false,
                      "surcharge": "0",
                      "tax": "0",
                      "totalAmount": "480000",
                      "returnedAmount": "0",
                      "exchangeReturn": false,
                      "paidAmount": "0",
                      "occurredAt": "2026-10-02T03:15:00.000Z",
                      "note": null,
                      "tags": [
                        {
                          "id": "018f3b8e-1c2d-7a4b-9c3d-000000000401",
                          "name": "Khách quen"
                        }
                      ],
                      "createdAt": "2026-10-02T03:15:00.000Z",
                      "updatedAt": "2026-10-02T03:15:00.000Z",
                      "deletedAt": null
                    }
                  ],
                  "nextCursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listOrders",
        "x-required-permissions": {
          "anyOf": [
            "pos.orders.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "invalid-cursor",
            "status": 400,
            "title": "Con trỏ phân trang không hợp lệ"
          }
        ]
      },
      "post": {
        "description": "Tạo đơn mới ở trạng thái `new`. Gửi kèm header `Idempotency-Key` để thử lại an toàn: cùng khoá trong 24 giờ trả đúng đơn đã tạo, không tạo đơn thứ hai. `unitPrice` khác giá niêm yết của mẫu mã, hay giảm giá (của dòng hoặc cả đơn) khác 0, cần thêm quyền `pos.orders.price.override`; gửi `payments` cần thêm `pos.orders.payment.record` — thiếu thì `insufficient-permission`.",
        "summary": "Tạo đơn hàng",
        "tags": [],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Khoá chống trùng do client đặt (khuyến nghị UUID). Gửi lại cùng khoá trong 24 giờ trả đúng kết quả lần đầu và header `Idempotent-Replayed: true`."
            }
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "warehouseId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Kho xuất hàng."
                  },
                  "customerId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Khách hàng có sẵn. Bỏ trống nếu bán lẻ."
                  },
                  "salesChannelId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Kênh bán."
                  },
                  "receivedAtShop": {
                    "type": "boolean",
                    "description": "Khách nhận hàng tại shop."
                  },
                  "billFullName": {
                    "type": "string",
                    "maxLength": 200,
                    "nullable": true,
                    "description": "Tên người mua."
                  },
                  "billPhone": {
                    "type": "string",
                    "maxLength": 32,
                    "nullable": true,
                    "description": "Số điện thoại người mua."
                  },
                  "billEmail": {
                    "type": "string",
                    "maxLength": 320,
                    "nullable": true,
                    "description": "Email người mua."
                  },
                  "shippingAddress": {
                    "type": "object",
                    "properties": {
                      "recipientName": {
                        "type": "string",
                        "maxLength": 200,
                        "nullable": true,
                        "description": "Tên người nhận."
                      },
                      "recipientPhone": {
                        "type": "string",
                        "maxLength": 32,
                        "nullable": true,
                        "description": "Số điện thoại người nhận."
                      },
                      "addressLine": {
                        "type": "string",
                        "maxLength": 500,
                        "nullable": true,
                        "description": "Số nhà, đường."
                      },
                      "provinceName": {
                        "type": "string",
                        "maxLength": 120,
                        "nullable": true,
                        "description": "Tỉnh hoặc thành phố."
                      },
                      "districtName": {
                        "type": "string",
                        "maxLength": 120,
                        "nullable": true,
                        "description": "Quận hoặc huyện."
                      },
                      "wardName": {
                        "type": "string",
                        "maxLength": 120,
                        "nullable": true,
                        "description": "Phường hoặc xã."
                      },
                      "geoSystem": {
                        "type": "string",
                        "enum": [
                          "old",
                          "new"
                        ],
                        "nullable": true,
                        "description": "Hệ địa giới của các mã bên dưới: `old` (tỉnh, huyện, xã) hoặc `new` (tỉnh, xã). Bỏ trống thì suy từ mã."
                      },
                      "provinceUnitId": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Mã (UUID) tỉnh hoặc thành phố trong danh mục hành chính của DANIX."
                      },
                      "districtUnitId": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Mã (UUID) quận hoặc huyện. Hệ `new` không có cấp này."
                      },
                      "wardUnitId": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Mã (UUID) phường hoặc xã."
                      }
                    },
                    "nullable": true,
                    "description": "Địa chỉ giao hàng."
                  },
                  "lines": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "variantId": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Mã mẫu mã cần bán."
                        },
                        "quantity": {
                          "type": "string",
                          "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
                          "description": "Số lượng, lớn hơn 0. Chuỗi thập phân, tối đa ba chữ số lẻ."
                        },
                        "unitPrice": {
                          "type": "string",
                          "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                          "description": "Đơn giá. Khác giá niêm yết của mẫu mã thì khoá cần thêm quyền `pos.orders.price.override`. Chuỗi thập phân, ví dụ \"150000\"."
                        },
                        "discount": {
                          "type": "string",
                          "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                          "description": "Giảm giá của dòng. Mặc định \"0\". Khác 0 thì khoá cần thêm quyền `pos.orders.price.override`."
                        }
                      },
                      "required": [
                        "variantId",
                        "quantity",
                        "unitPrice"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 200,
                    "description": "Các dòng hàng, 1 đến 200."
                  },
                  "discount": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Giảm giá cả đơn. Khác 0 thì khoá cần thêm quyền `pos.orders.price.override`."
                  },
                  "shippingFee": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Phí vận chuyển khách trả."
                  },
                  "freeShipping": {
                    "type": "boolean",
                    "description": "Shop chịu phí giao hàng."
                  },
                  "surcharge": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Phụ thu."
                  },
                  "tax": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Thuế."
                  },
                  "payments": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "methodId": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Mã phương thức thanh toán."
                        },
                        "amount": {
                          "type": "string",
                          "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                          "description": "Số tiền thu. Chuỗi thập phân, ví dụ \"150000\"."
                        }
                      },
                      "required": [
                        "methodId",
                        "amount"
                      ]
                    },
                    "description": "Các khoản thu. Gửi khoản thu thì khoá cần thêm quyền `pos.orders.payment.record`."
                  },
                  "occurredAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?!0000-)",
                    "description": "Thời điểm phát sinh đơn."
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 2000,
                    "nullable": true,
                    "description": "Ghi chú nội bộ."
                  },
                  "tagIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 50,
                    "description": "Các thẻ gắn vào đơn."
                  }
                },
                "required": [
                  "warehouseId",
                  "lines"
                ],
                "description": "Thân tạo đơn."
              },
              "example": {
                "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                "billFullName": "Nguyễn Văn An",
                "billPhone": "0901234567",
                "lines": [
                  {
                    "variantId": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
                    "quantity": "2",
                    "unitPrice": "225000"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một đơn hàng.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                  "code": "DH1024",
                  "status": "confirmed",
                  "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                  "warehouseName": "Kho chính",
                  "customerId": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                  "salesChannelId": "018f3b8e-1c2d-7a4b-9c3d-000000000301",
                  "salesChannelName": "Facebook",
                  "receivedAtShop": false,
                  "billFullName": "Nguyễn Văn An",
                  "billPhone": "0901234567",
                  "billEmail": null,
                  "shippingAddress": {
                    "recipientName": "Nguyễn Văn An",
                    "recipientPhone": "0901234567",
                    "addressLine": "12 Lê Lợi",
                    "geoSystem": "old",
                    "provinceUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001501",
                    "districtUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001502",
                    "wardUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001503",
                    "provinceName": "Đà Nẵng",
                    "districtName": "Hải Châu",
                    "wardName": "Thạch Thang"
                  },
                  "totalPrice": "450000",
                  "discount": "0",
                  "shippingFee": "30000",
                  "freeShipping": false,
                  "surcharge": "0",
                  "tax": "0",
                  "totalAmount": "480000",
                  "returnedAmount": "0",
                  "exchangeReturn": false,
                  "paidAmount": "0",
                  "occurredAt": "2026-10-02T03:15:00.000Z",
                  "note": null,
                  "tags": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000401",
                      "name": "Khách quen"
                    }
                  ],
                  "lines": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000501",
                      "variantId": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
                      "productId": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
                      "variantCode": "AO-THUN-M",
                      "variantName": "Áo thun - M",
                      "productName": "Áo thun cổ tròn",
                      "quantity": "2",
                      "unitPrice": "225000",
                      "discount": "0",
                      "lineTotal": "450000",
                      "returnedQuantity": "0"
                    }
                  ],
                  "payments": [],
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z",
                  "deletedAt": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "`true` khi đây là kết quả của lần gửi trước với cùng `Idempotency-Key`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createOrder",
        "x-required-permissions": {
          "anyOf": [
            "pos.orders.create"
          ],
          "allOf": [
            "pos.products.read"
          ]
        },
        "x-optional-permissions": [
          "pos.orders.payment.record",
          "pos.orders.price.override"
        ],
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          },
          {
            "code": "idempotency-key-in-progress",
            "status": 409,
            "title": "Yêu cầu với cùng Idempotency-Key đang được xử lý"
          },
          {
            "code": "idempotency-key-reused",
            "status": 422,
            "title": "Idempotency-Key đã dùng cho một yêu cầu có nội dung khác"
          },
          {
            "code": "variant-removed",
            "status": 422,
            "title": "Mẫu mã đã bị gỡ khỏi sản phẩm"
          },
          {
            "code": "customer-blocked",
            "status": 422,
            "title": "Khách hàng đang bị chặn"
          },
          {
            "code": "insufficient-stock",
            "status": 422,
            "title": "Không đủ tồn kho cho thao tác này"
          }
        ],
        "x-idempotent": true
      }
    },
    "/api/open/v1/orders/{id}": {
      "get": {
        "description": "Trả đơn kèm dòng hàng, khoản thu và địa chỉ giao hàng.",
        "summary": "Chi tiết một đơn hàng",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Một đơn hàng.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                  "code": "DH1024",
                  "status": "confirmed",
                  "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                  "warehouseName": "Kho chính",
                  "customerId": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                  "salesChannelId": "018f3b8e-1c2d-7a4b-9c3d-000000000301",
                  "salesChannelName": "Facebook",
                  "receivedAtShop": false,
                  "billFullName": "Nguyễn Văn An",
                  "billPhone": "0901234567",
                  "billEmail": null,
                  "shippingAddress": {
                    "recipientName": "Nguyễn Văn An",
                    "recipientPhone": "0901234567",
                    "addressLine": "12 Lê Lợi",
                    "geoSystem": "old",
                    "provinceUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001501",
                    "districtUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001502",
                    "wardUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001503",
                    "provinceName": "Đà Nẵng",
                    "districtName": "Hải Châu",
                    "wardName": "Thạch Thang"
                  },
                  "totalPrice": "450000",
                  "discount": "0",
                  "shippingFee": "30000",
                  "freeShipping": false,
                  "surcharge": "0",
                  "tax": "0",
                  "totalAmount": "480000",
                  "returnedAmount": "0",
                  "exchangeReturn": false,
                  "paidAmount": "0",
                  "occurredAt": "2026-10-02T03:15:00.000Z",
                  "note": null,
                  "tags": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000401",
                      "name": "Khách quen"
                    }
                  ],
                  "lines": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000501",
                      "variantId": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
                      "productId": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
                      "variantCode": "AO-THUN-M",
                      "variantName": "Áo thun - M",
                      "productName": "Áo thun cổ tròn",
                      "quantity": "2",
                      "unitPrice": "225000",
                      "discount": "0",
                      "lineTotal": "450000",
                      "returnedQuantity": "0"
                    }
                  ],
                  "payments": [],
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z",
                  "deletedAt": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getOrder",
        "x-required-permissions": {
          "anyOf": [
            "pos.orders.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          }
        ]
      },
      "patch": {
        "description": "Chỉ gửi trường cần đổi. Dòng hàng và kho không sửa được qua đường này. Đổi `discount` cần thêm quyền `pos.orders.price.override`.",
        "summary": "Sửa đơn hàng",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "customerId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Đổi khách hàng gắn với đơn."
                  },
                  "salesChannelId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Đổi kênh bán."
                  },
                  "billFullName": {
                    "type": "string",
                    "maxLength": 200,
                    "nullable": true,
                    "description": "Tên người mua."
                  },
                  "billPhone": {
                    "type": "string",
                    "maxLength": 32,
                    "nullable": true,
                    "description": "Số điện thoại người mua."
                  },
                  "billEmail": {
                    "type": "string",
                    "maxLength": 320,
                    "nullable": true,
                    "description": "Email người mua."
                  },
                  "shippingAddress": {
                    "type": "object",
                    "properties": {
                      "recipientName": {
                        "type": "string",
                        "maxLength": 200,
                        "nullable": true,
                        "description": "Tên người nhận."
                      },
                      "recipientPhone": {
                        "type": "string",
                        "maxLength": 32,
                        "nullable": true,
                        "description": "Số điện thoại người nhận."
                      },
                      "addressLine": {
                        "type": "string",
                        "maxLength": 500,
                        "nullable": true,
                        "description": "Số nhà, đường."
                      },
                      "provinceName": {
                        "type": "string",
                        "maxLength": 120,
                        "nullable": true,
                        "description": "Tỉnh hoặc thành phố."
                      },
                      "districtName": {
                        "type": "string",
                        "maxLength": 120,
                        "nullable": true,
                        "description": "Quận hoặc huyện."
                      },
                      "wardName": {
                        "type": "string",
                        "maxLength": 120,
                        "nullable": true,
                        "description": "Phường hoặc xã."
                      },
                      "geoSystem": {
                        "type": "string",
                        "enum": [
                          "old",
                          "new"
                        ],
                        "nullable": true,
                        "description": "Hệ địa giới của các mã bên dưới: `old` (tỉnh, huyện, xã) hoặc `new` (tỉnh, xã). Bỏ trống thì suy từ mã."
                      },
                      "provinceUnitId": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Mã (UUID) tỉnh hoặc thành phố trong danh mục hành chính của DANIX."
                      },
                      "districtUnitId": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Mã (UUID) quận hoặc huyện. Hệ `new` không có cấp này."
                      },
                      "wardUnitId": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Mã (UUID) phường hoặc xã."
                      }
                    },
                    "nullable": true,
                    "description": "Địa chỉ giao hàng."
                  },
                  "discount": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Giảm giá cả đơn. Đổi giảm giá thì khoá cần thêm quyền `pos.orders.price.override`."
                  },
                  "shippingFee": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Phí vận chuyển khách trả."
                  },
                  "surcharge": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Phụ thu."
                  },
                  "tax": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Thuế."
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 2000,
                    "nullable": true,
                    "description": "Ghi chú nội bộ."
                  },
                  "tagIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 50,
                    "description": "Thay toàn bộ thẻ của đơn."
                  }
                },
                "description": "Thân sửa đơn. Chỉ gửi trường cần đổi."
              },
              "example": {
                "note": "Khách dặn gọi trước khi giao"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Một đơn hàng.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                  "code": "DH1024",
                  "status": "confirmed",
                  "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                  "warehouseName": "Kho chính",
                  "customerId": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                  "salesChannelId": "018f3b8e-1c2d-7a4b-9c3d-000000000301",
                  "salesChannelName": "Facebook",
                  "receivedAtShop": false,
                  "billFullName": "Nguyễn Văn An",
                  "billPhone": "0901234567",
                  "billEmail": null,
                  "shippingAddress": {
                    "recipientName": "Nguyễn Văn An",
                    "recipientPhone": "0901234567",
                    "addressLine": "12 Lê Lợi",
                    "geoSystem": "old",
                    "provinceUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001501",
                    "districtUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001502",
                    "wardUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001503",
                    "provinceName": "Đà Nẵng",
                    "districtName": "Hải Châu",
                    "wardName": "Thạch Thang"
                  },
                  "totalPrice": "450000",
                  "discount": "0",
                  "shippingFee": "30000",
                  "freeShipping": false,
                  "surcharge": "0",
                  "tax": "0",
                  "totalAmount": "480000",
                  "returnedAmount": "0",
                  "exchangeReturn": false,
                  "paidAmount": "0",
                  "occurredAt": "2026-10-02T03:15:00.000Z",
                  "note": null,
                  "tags": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000401",
                      "name": "Khách quen"
                    }
                  ],
                  "lines": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000501",
                      "variantId": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
                      "productId": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
                      "variantCode": "AO-THUN-M",
                      "variantName": "Áo thun - M",
                      "productName": "Áo thun cổ tròn",
                      "quantity": "2",
                      "unitPrice": "225000",
                      "discount": "0",
                      "lineTotal": "450000",
                      "returnedQuantity": "0"
                    }
                  ],
                  "payments": [],
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z",
                  "deletedAt": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "updateOrder",
        "x-required-permissions": {
          "anyOf": [
            "pos.orders.update"
          ],
          "allOf": [
            "pos.products.read"
          ]
        },
        "x-optional-permissions": [
          "pos.orders.price.override"
        ],
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          },
          {
            "code": "customer-blocked",
            "status": 422,
            "title": "Khách hàng đang bị chặn"
          },
          {
            "code": "order-has-live-shipment",
            "status": 409,
            "title": "Đơn đang có vận đơn chưa kết thúc nên không thao tác được"
          }
        ]
      }
    },
    "/api/open/v1/orders/{id}/status": {
      "post": {
        "description": "Chuyển đơn sang trạng thái mới theo luồng của shop. Chuyển trạng thái có thể xuất hoặc nhập kho; từ chối bằng `order-status-not-allowed` hoặc `insufficient-stock`. Huỷ một đơn đã gửi hàng (`shipped`, `delivered`, `paid`, `returning`, `partially_returned`, `returned` sang `cancelled`; hàng khách đang giữ được nhập lại kho) cần thêm quyền `pos.orders.delete` — thiếu thì `insufficient-permission`. Đơn đã rời `new` thì không đưa về `new` được nữa (`order-status-not-allowed`).",
        "summary": "Đổi trạng thái đơn",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "new",
                      "waiting_stock",
                      "confirmed",
                      "packing",
                      "ready_to_ship",
                      "shipped",
                      "delivered",
                      "paid",
                      "returning",
                      "returned",
                      "cancelled"
                    ],
                    "description": "Trạng thái mới của đơn. `new` không bao giờ là một đích đi được: đơn đang ở `new` → `conflict`; đơn đã rời `new` → `order-status-not-allowed`."
                  }
                },
                "required": [
                  "status"
                ],
                "description": "Thân đổi trạng thái đơn."
              },
              "example": {
                "status": "confirmed"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Một đơn hàng.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                  "code": "DH1024",
                  "status": "confirmed",
                  "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                  "warehouseName": "Kho chính",
                  "customerId": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                  "salesChannelId": "018f3b8e-1c2d-7a4b-9c3d-000000000301",
                  "salesChannelName": "Facebook",
                  "receivedAtShop": false,
                  "billFullName": "Nguyễn Văn An",
                  "billPhone": "0901234567",
                  "billEmail": null,
                  "shippingAddress": {
                    "recipientName": "Nguyễn Văn An",
                    "recipientPhone": "0901234567",
                    "addressLine": "12 Lê Lợi",
                    "geoSystem": "old",
                    "provinceUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001501",
                    "districtUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001502",
                    "wardUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001503",
                    "provinceName": "Đà Nẵng",
                    "districtName": "Hải Châu",
                    "wardName": "Thạch Thang"
                  },
                  "totalPrice": "450000",
                  "discount": "0",
                  "shippingFee": "30000",
                  "freeShipping": false,
                  "surcharge": "0",
                  "tax": "0",
                  "totalAmount": "480000",
                  "returnedAmount": "0",
                  "exchangeReturn": false,
                  "paidAmount": "0",
                  "occurredAt": "2026-10-02T03:15:00.000Z",
                  "note": null,
                  "tags": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000401",
                      "name": "Khách quen"
                    }
                  ],
                  "lines": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000501",
                      "variantId": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
                      "productId": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
                      "variantCode": "AO-THUN-M",
                      "variantName": "Áo thun - M",
                      "productName": "Áo thun cổ tròn",
                      "quantity": "2",
                      "unitPrice": "225000",
                      "discount": "0",
                      "lineTotal": "450000",
                      "returnedQuantity": "0"
                    }
                  ],
                  "payments": [],
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z",
                  "deletedAt": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "changeOrderStatus",
        "x-required-permissions": {
          "anyOf": [
            "pos.orders.status"
          ],
          "allOf": [
            "pos.products.read"
          ]
        },
        "x-optional-permissions": [
          "pos.orders.delete"
        ],
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          },
          {
            "code": "order-status-not-allowed",
            "status": 422,
            "title": "Không chuyển được đơn sang trạng thái này từ trạng thái hiện tại"
          },
          {
            "code": "insufficient-stock",
            "status": 422,
            "title": "Không đủ tồn kho cho thao tác này"
          },
          {
            "code": "variant-removed",
            "status": 422,
            "title": "Mẫu mã đã bị gỡ khỏi sản phẩm"
          },
          {
            "code": "order-has-live-shipment",
            "status": 409,
            "title": "Đơn đang có vận đơn chưa kết thúc nên không thao tác được"
          }
        ]
      }
    },
    "/api/open/v1/orders/{id}/notes": {
      "get": {
        "description": "Danh sách ghi chú, mới nhất trước.",
        "summary": "Ghi chú của đơn",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Danh sách ghi chú.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Note"
                      },
                      "description": "Các ghi chú, mới nhất trước."
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "description": "Danh sách ghi chú."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000777",
                      "message": "Đã gọi khách xác nhận",
                      "createdByName": "Đồng bộ kế toán",
                      "createdByIsIntegration": true,
                      "createdAt": "2026-10-02T03:15:00.000Z",
                      "updatedAt": "2026-10-02T03:15:00.000Z"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listOrderNotes",
        "x-required-permissions": {
          "anyOf": [
            "pos.orders.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      },
      "post": {
        "description": "Ghi chú đứng tên ứng dụng kết nối.",
        "summary": "Thêm ghi chú vào đơn",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "description": "Nội dung ghi chú."
                  }
                },
                "required": [
                  "message"
                ],
                "description": "Thân thêm ghi chú."
              },
              "example": {
                "message": "Đã gọi khách xác nhận"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một ghi chú.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Note"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000777",
                  "message": "Đã gọi khách xác nhận",
                  "createdByName": "Đồng bộ kế toán",
                  "createdByIsIntegration": true,
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createOrderNote",
        "x-required-permissions": {
          "anyOf": [
            "pos.orders.update"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/products": {
      "get": {
        "description": "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}`.",
        "summary": "Liệt kê sản phẩm",
        "tags": [],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Số dòng mỗi trang, từ 1 đến 100. Mặc định 50.",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "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.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "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).",
            "schema": {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?!0000-)"
            }
          },
          {
            "name": "includeDeleted",
            "in": "query",
            "description": "`true` để lấy cả bản ghi đã xoá (có `deletedAt`). Mặc định `false`.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Tìm theo tên hoặc mã sản phẩm.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "categoryId",
            "in": "query",
            "description": "Chỉ lấy sản phẩm trong danh mục này.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một trang sản phẩm.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProductSummary"
                      },
                      "description": "Các bản ghi của trang này."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Con trỏ của trang kế; `null` khi đã hết dữ liệu.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "Một trang sản phẩm."
                },
                "example": {
                  "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
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listProducts",
        "x-required-permissions": {
          "anyOf": [
            "pos.products.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "invalid-cursor",
            "status": 400,
            "title": "Con trỏ phân trang không hợp lệ"
          }
        ]
      },
      "post": {
        "description": "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`).",
        "summary": "Tạo sản phẩm",
        "tags": [],
        "parameters": [],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64,
                    "description": "Mã sản phẩm. Bỏ trống thì hệ thống tự cấp."
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Tên sản phẩm."
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 5000,
                    "nullable": true,
                    "description": "Mô tả."
                  },
                  "categoryIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Các danh mục."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "simple",
                      "service"
                    ],
                    "description": "Loại sản phẩm. Mặc định `simple`."
                  },
                  "variants": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Mã mẫu mã cần sửa; bỏ trống để thêm mẫu mã mới."
                        },
                        "code": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64,
                          "description": "Mã mẫu mã (SKU). Bỏ trống thì hệ thống tự cấp."
                        },
                        "barcode": {
                          "type": "string",
                          "maxLength": 64,
                          "nullable": true,
                          "description": "Mã vạch."
                        },
                        "price": {
                          "type": "string",
                          "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                          "description": "Giá bán."
                        },
                        "weightGrams": {
                          "type": "string",
                          "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
                          "nullable": true,
                          "description": "Cân nặng, gram."
                        },
                        "isActive": {
                          "type": "boolean",
                          "description": "Mẫu mã còn bán."
                        },
                        "attributes": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 80
                          },
                          "description": "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."
                        }
                      }
                    },
                    "minItems": 1,
                    "description": "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."
                  }
                },
                "required": [
                  "name",
                  "variants"
                ],
                "description": "Thân tạo sản phẩm."
              },
              "example": {
                "name": "Áo thun cổ tròn",
                "variants": [
                  {
                    "code": "AO-THUN-M",
                    "price": "225000",
                    "attributes": {
                      "Size": "M"
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một sản phẩm kèm mẫu mã và ảnh.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                },
                "example": {
                  "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
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createProduct",
        "x-required-permissions": {
          "anyOf": [
            "pos.products.create"
          ],
          "allOf": [
            "pos.products.read"
          ]
        },
        "x-optional-permissions": [
          "pos.products.update"
        ],
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "duplicate-code",
            "status": 409,
            "title": "Mã đã tồn tại trong shop"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          }
        ]
      }
    },
    "/api/open/v1/products/{id}": {
      "get": {
        "description": "Trả sản phẩm kèm mẫu mã, ảnh và danh mục.",
        "summary": "Chi tiết một sản phẩm",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Một sản phẩm kèm mẫu mã và ảnh.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                },
                "example": {
                  "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
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getProduct",
        "x-required-permissions": {
          "anyOf": [
            "pos.products.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      },
      "patch": {
        "description": "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.",
        "summary": "Sửa sản phẩm",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Tên sản phẩm."
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 5000,
                    "nullable": true,
                    "description": "Mô tả."
                  },
                  "categoryIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Thay toàn bộ danh mục."
                  },
                  "variants": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Mã mẫu mã cần sửa; bỏ trống để thêm mẫu mã mới."
                        },
                        "code": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64,
                          "description": "Mã mẫu mã (SKU). Bỏ trống thì hệ thống tự cấp."
                        },
                        "barcode": {
                          "type": "string",
                          "maxLength": 64,
                          "nullable": true,
                          "description": "Mã vạch."
                        },
                        "price": {
                          "type": "string",
                          "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                          "description": "Giá bán."
                        },
                        "weightGrams": {
                          "type": "string",
                          "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
                          "nullable": true,
                          "description": "Cân nặng, gram."
                        },
                        "isActive": {
                          "type": "boolean",
                          "description": "Mẫu mã còn bán."
                        },
                        "attributes": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 80
                          },
                          "description": "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."
                        }
                      }
                    },
                    "minItems": 1,
                    "description": "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."
                  }
                },
                "description": "Thân sửa sản phẩm. Chỉ gửi trường cần đổi."
              },
              "example": {
                "name": "Áo thun cổ tròn cotton",
                "variants": [
                  {
                    "code": "AO-THUN-L",
                    "price": "235000",
                    "attributes": {
                      "Size": "L"
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Một sản phẩm kèm mẫu mã và ảnh.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                },
                "example": {
                  "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
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "updateProduct",
        "x-required-permissions": {
          "anyOf": [
            "pos.products.update"
          ],
          "allOf": [
            "pos.products.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "duplicate-code",
            "status": 409,
            "title": "Mã đã tồn tại trong shop"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          }
        ]
      }
    },
    "/api/open/v1/categories": {
      "get": {
        "description": "Toàn bộ danh mục của shop, dạng cây phẳng với `parentId`.",
        "summary": "Danh mục sản phẩm",
        "tags": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Danh sách danh mục.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Category"
                      },
                      "description": "Các danh mục."
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "description": "Danh sách danh mục."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000801",
                      "parentId": null,
                      "name": "Áo",
                      "slug": "ao"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listCategories",
        "x-required-permissions": {
          "anyOf": [
            "pos.products.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          }
        ]
      }
    },
    "/api/open/v1/warehouses": {
      "get": {
        "description": "Các kho hàng của shop.",
        "summary": "Danh sách kho",
        "tags": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Danh sách kho.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Warehouse"
                      },
                      "description": "Các kho."
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "description": "Danh sách kho."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                      "code": "KHO1",
                      "name": "Kho chính",
                      "phone": null,
                      "addressLine": null,
                      "provinceName": null,
                      "districtName": null,
                      "wardName": null,
                      "isDefault": true
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listWarehouses",
        "x-required-permissions": {
          "anyOf": [
            "pos.inventory.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          }
        ]
      }
    },
    "/api/open/v1/inventory/stock": {
      "get": {
        "description": "Mỗi dòng là tồn của một mẫu mã tại một kho. Không có giá vốn. Đồng bộ tăng dần bằng `updatedSince` (lật trang trong một lượt bằng `nextCursor`): có `updatedSince` thì chỉ trả dòng tồn đổi từ mốc ấy và đã qua khoảng trễ an toàn (bằng 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). Mốc của dòng tồn chỉ đổi khi số tồn hoặc `inTransit` đổi; giá, mã, mã vạch, tên và ảnh đi kèm lấy từ đồng bộ sản phẩm và danh sách kho.",
        "summary": "Tồn kho theo mẫu mã và kho",
        "tags": [],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Số dòng mỗi trang, từ 1 đến 100. Mặc định 50.",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "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.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "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).",
            "schema": {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?!0000-)"
            }
          },
          {
            "name": "warehouseId",
            "in": "query",
            "description": "Chỉ lấy tồn của kho này.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Tìm theo tên hoặc mã mẫu mã.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một trang tồn kho.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/StockRow"
                      },
                      "description": "Các bản ghi của trang này."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Con trỏ của trang kế; `null` khi đã hết dữ liệu.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "Một trang tồn kho."
                },
                "example": {
                  "data": [
                    {
                      "variantId": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
                      "productId": "018f3b8e-1c2d-7a4b-9c3d-000000000701",
                      "productName": "Áo thun cổ tròn",
                      "variantName": "Áo thun - M",
                      "code": "AO-THUN-M",
                      "barcode": null,
                      "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                      "warehouseName": "Kho chính",
                      "quantity": "18",
                      "inTransit": "0",
                      "price": "225000"
                    }
                  ],
                  "nextCursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listStock",
        "x-required-permissions": {
          "anyOf": [
            "pos.inventory.read"
          ],
          "allOf": [
            "pos.products.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "invalid-cursor",
            "status": 400,
            "title": "Con trỏ phân trang không hợp lệ"
          }
        ]
      }
    },
    "/api/open/v1/inventory/documents": {
      "post": {
        "description": "Tạo phiếu nhập, xuất hoặc kiểm kho ở trạng thái `draft`. Tồn kho chỉ đổi khi ghi sổ bằng `POST /inventory/documents/{id}/post`.",
        "summary": "Tạo phiếu kho nháp",
        "tags": [],
        "parameters": [],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "receipt",
                      "issue",
                      "stocktake"
                    ],
                    "description": "Loại phiếu: nhập, xuất hoặc kiểm kho."
                  },
                  "warehouseId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Kho của phiếu."
                  },
                  "supplierId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Nhà cung cấp (phiếu nhập)."
                  },
                  "documentDate": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                    "description": "Ngày của phiếu, dạng YYYY-MM-DD."
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true,
                    "description": "Ghi chú."
                  },
                  "lines": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "variantId": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Mã mẫu mã."
                        },
                        "quantity": {
                          "type": "string",
                          "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
                          "description": "Số lượng nhập hoặc xuất."
                        },
                        "countedQuantity": {
                          "type": "string",
                          "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
                          "description": "Số đếm thực tế (phiếu kiểm kho)."
                        },
                        "unitCost": {
                          "type": "string",
                          "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                          "description": "Giá nhập (phiếu nhập)."
                        },
                        "note": {
                          "type": "string",
                          "maxLength": 500,
                          "nullable": true,
                          "description": "Ghi chú dòng."
                        }
                      },
                      "required": [
                        "variantId"
                      ]
                    },
                    "maxItems": 500,
                    "description": "Các dòng của phiếu, tối đa 500."
                  }
                },
                "required": [
                  "kind",
                  "warehouseId"
                ],
                "description": "Thân tạo phiếu kho nháp. Ghi sổ bằng `POST /inventory/documents/{id}/post`."
              },
              "example": {
                "kind": "receipt",
                "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                "lines": [
                  {
                    "variantId": "018f3b8e-1c2d-7a4b-9c3d-000000000601",
                    "quantity": "10",
                    "unitCost": "120000"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một phiếu kho.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InventoryDocument"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001201",
                  "kind": "receipt",
                  "code": "PN0001",
                  "status": "draft",
                  "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                  "supplierId": null,
                  "occurredAt": "2026-10-02T03:15:00.000Z",
                  "postedAt": null,
                  "costingMethod": null,
                  "note": null,
                  "lines": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createInventoryDocument",
        "x-required-permissions": {
          "anyOf": [
            "pos.inventory.receipt",
            "pos.inventory.adjust"
          ],
          "allOf": [
            "pos.inventory.read",
            "pos.products.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "variant-removed",
            "status": 422,
            "title": "Mẫu mã đã bị gỡ khỏi sản phẩm"
          }
        ]
      }
    },
    "/api/open/v1/inventory/documents/{id}/post": {
      "post": {
        "description": "Ghi sổ phiếu nháp: tồn kho đổi ngay. Ghi sổ lần hai trả `document-already-posted`. Quyền theo LOẠI phiếu: phiếu kiểm kho cần `pos.inventory.adjust`, phiếu nhập hay xuất cần `pos.inventory.receipt` — có quyền còn lại thôi thì `insufficient-permission`.",
        "summary": "Ghi sổ phiếu kho",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Một phiếu kho.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InventoryDocument"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001201",
                  "kind": "receipt",
                  "code": "PN0001",
                  "status": "posted",
                  "warehouseId": "018f3b8e-1c2d-7a4b-9c3d-000000000001",
                  "supplierId": null,
                  "occurredAt": "2026-10-02T03:15:00.000Z",
                  "postedAt": "2026-10-02T03:16:00.000Z",
                  "costingMethod": "average",
                  "note": null,
                  "lines": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "postInventoryDocument",
        "x-required-permissions": {
          "anyOf": [
            "pos.inventory.receipt",
            "pos.inventory.adjust"
          ],
          "allOf": [
            "pos.inventory.read",
            "pos.products.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          },
          {
            "code": "document-already-posted",
            "status": 409,
            "title": "Phiếu kho đã ghi sổ"
          },
          {
            "code": "insufficient-stock",
            "status": 422,
            "title": "Không đủ tồn kho cho thao tác này"
          },
          {
            "code": "variant-removed",
            "status": 422,
            "title": "Mẫu mã đã bị gỡ khỏi sản phẩm"
          }
        ]
      }
    },
    "/api/open/v1/customers": {
      "get": {
        "description": "Phân trang theo con trỏ. Lọc theo số điện thoại bằng `phone`.",
        "summary": "Liệt kê khách hàng",
        "tags": [],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Số dòng mỗi trang, từ 1 đến 100. Mặc định 50.",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "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.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "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).",
            "schema": {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?!0000-)"
            }
          },
          {
            "name": "includeDeleted",
            "in": "query",
            "description": "`true` để lấy cả bản ghi đã xoá (có `deletedAt`). Mặc định `false`.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "phone",
            "in": "query",
            "description": "Tìm đúng số điện thoại (chấp nhận 0xxxxxxxxx hoặc +84…).",
            "schema": {
              "type": "string",
              "maxLength": 20
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Tìm theo tên.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một trang khách hàng.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomerSummary"
                      },
                      "description": "Các bản ghi của trang này."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Con trỏ của trang kế; `null` khi đã hết dữ liệu.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "Một trang khách hàng."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                      "name": "Nguyễn Văn An",
                      "gender": "male",
                      "dateOfBirth": null,
                      "source": "order",
                      "isBlocked": false,
                      "primaryPhone": "0901234567",
                      "primaryEmail": null,
                      "createdAt": "2026-10-02T03:15:00.000Z",
                      "updatedAt": "2026-10-02T03:15:00.000Z",
                      "deletedAt": null
                    }
                  ],
                  "nextCursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listCustomers",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "invalid-cursor",
            "status": 400,
            "title": "Con trỏ phân trang không hợp lệ"
          }
        ]
      },
      "post": {
        "description": "Tạo hồ sơ khách. Gửi `Idempotency-Key` để thử lại an toàn.",
        "summary": "Tạo khách hàng",
        "tags": [],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Khoá chống trùng do client đặt (khuyến nghị UUID). Gửi lại cùng khoá trong 24 giờ trả đúng kết quả lần đầu và header `Idempotent-Replayed: true`."
            }
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Tên khách."
                  },
                  "gender": {
                    "type": "string",
                    "enum": [
                      "male",
                      "female",
                      "other"
                    ],
                    "nullable": true,
                    "description": "Giới tính."
                  },
                  "dateOfBirth": {
                    "type": "string",
                    "nullable": true,
                    "description": "Ngày sinh, dạng YYYY-MM-DD."
                  },
                  "contacts": {
                    "type": "array",
                    "items": {
                      "discriminator": {
                        "propertyName": "kind"
                      },
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "kind": {
                              "type": "string",
                              "enum": [
                                "phone"
                              ]
                            },
                            "value": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 20,
                              "description": "Số điện thoại."
                            },
                            "isPrimary": {
                              "type": "boolean",
                              "description": "Đặt làm số chính."
                            }
                          },
                          "required": [
                            "kind",
                            "value"
                          ]
                        },
                        {
                          "type": "object",
                          "properties": {
                            "kind": {
                              "type": "string",
                              "enum": [
                                "email"
                              ]
                            },
                            "value": {
                              "type": "string",
                              "format": "email",
                              "description": "Email."
                            },
                            "isPrimary": {
                              "type": "boolean",
                              "description": "Đặt làm email chính."
                            }
                          },
                          "required": [
                            "kind",
                            "value"
                          ]
                        }
                      ]
                    },
                    "maxItems": 20,
                    "description": "Số điện thoại và email."
                  }
                },
                "required": [
                  "name"
                ],
                "description": "Thân tạo khách hàng."
              },
              "example": {
                "name": "Nguyễn Văn An",
                "contacts": [
                  {
                    "kind": "phone",
                    "value": "0901234567",
                    "isPrimary": true
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một khách hàng kèm liên hệ và địa chỉ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                  "name": "Nguyễn Văn An",
                  "gender": "male",
                  "dateOfBirth": null,
                  "source": "order",
                  "isBlocked": false,
                  "primaryPhone": "0901234567",
                  "primaryEmail": null,
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z",
                  "deletedAt": null,
                  "contacts": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000901",
                      "kind": "phone",
                      "value": "0901234567",
                      "isPrimary": true
                    }
                  ],
                  "addresses": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "`true` khi đây là kết quả của lần gửi trước với cùng `Idempotency-Key`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createCustomer",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.create"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          },
          {
            "code": "idempotency-key-in-progress",
            "status": 409,
            "title": "Yêu cầu với cùng Idempotency-Key đang được xử lý"
          },
          {
            "code": "idempotency-key-reused",
            "status": 422,
            "title": "Idempotency-Key đã dùng cho một yêu cầu có nội dung khác"
          }
        ],
        "x-idempotent": true
      }
    },
    "/api/open/v1/customers/{id}": {
      "get": {
        "description": "Trả khách kèm số điện thoại, email và địa chỉ giao hàng.",
        "summary": "Chi tiết một khách hàng",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Một khách hàng kèm liên hệ và địa chỉ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                  "name": "Nguyễn Văn An",
                  "gender": "male",
                  "dateOfBirth": null,
                  "source": "order",
                  "isBlocked": false,
                  "primaryPhone": "0901234567",
                  "primaryEmail": null,
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z",
                  "deletedAt": null,
                  "contacts": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000901",
                      "kind": "phone",
                      "value": "0901234567",
                      "isPrimary": true
                    }
                  ],
                  "addresses": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getCustomer",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      },
      "patch": {
        "description": "Chỉ gửi trường cần đổi. Số điện thoại và email không sửa được qua đường này.",
        "summary": "Sửa khách hàng",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Tên khách."
                  },
                  "gender": {
                    "type": "string",
                    "enum": [
                      "male",
                      "female",
                      "other"
                    ],
                    "nullable": true,
                    "description": "Giới tính."
                  },
                  "dateOfBirth": {
                    "type": "string",
                    "nullable": true,
                    "description": "Ngày sinh, dạng YYYY-MM-DD."
                  }
                },
                "description": "Thân sửa khách hàng. Chỉ gửi trường cần đổi."
              },
              "example": {
                "name": "Nguyễn Văn An (VIP)"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Một khách hàng kèm liên hệ và địa chỉ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000201",
                  "name": "Nguyễn Văn An",
                  "gender": "male",
                  "dateOfBirth": null,
                  "source": "order",
                  "isBlocked": false,
                  "primaryPhone": "0901234567",
                  "primaryEmail": null,
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z",
                  "deletedAt": null,
                  "contacts": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000000901",
                      "kind": "phone",
                      "value": "0901234567",
                      "isPrimary": true
                    }
                  ],
                  "addresses": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "updateCustomer",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.update"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/customers/{id}/addresses": {
      "post": {
        "description": "Thêm một địa chỉ giao hàng vào hồ sơ khách. Khách đã có một địa chỉ đúng như vậy thì trả `conflict`. Gửi mã đơn vị hành chính (tra bằng `GET /geo/units`) để đơn dùng địa chỉ này đẩy được sang hãng vận chuyển.",
        "summary": "Thêm địa chỉ cho khách",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "recipientName": {
                    "type": "string",
                    "maxLength": 200,
                    "nullable": true,
                    "description": "Tên người nhận."
                  },
                  "recipientPhone": {
                    "type": "string",
                    "maxLength": 32,
                    "nullable": true,
                    "description": "Số điện thoại người nhận."
                  },
                  "addressLine": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "Số nhà, đường."
                  },
                  "provinceName": {
                    "type": "string",
                    "maxLength": 120,
                    "nullable": true,
                    "description": "Tỉnh hoặc thành phố."
                  },
                  "districtName": {
                    "type": "string",
                    "maxLength": 120,
                    "nullable": true,
                    "description": "Quận hoặc huyện."
                  },
                  "wardName": {
                    "type": "string",
                    "maxLength": 120,
                    "nullable": true,
                    "description": "Phường hoặc xã."
                  },
                  "geoSystem": {
                    "type": "string",
                    "enum": [
                      "old",
                      "new"
                    ],
                    "nullable": true,
                    "description": "Hệ địa giới của các mã bên dưới: `old` (tỉnh, huyện, xã) hoặc `new` (tỉnh, xã). Bỏ trống thì suy từ mã."
                  },
                  "provinceUnitId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Mã (UUID) tỉnh hoặc thành phố trong danh mục hành chính của DANIX."
                  },
                  "districtUnitId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Mã (UUID) quận hoặc huyện. Hệ `new` không có cấp này."
                  },
                  "wardUnitId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Mã (UUID) phường hoặc xã."
                  }
                },
                "required": [
                  "addressLine"
                ],
                "description": "Thân thêm hoặc sửa địa chỉ giao hàng."
              },
              "example": {
                "addressLine": "12 Lê Lợi",
                "provinceName": "Đà Nẵng"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một địa chỉ giao hàng của khách.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerAddress"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000999",
                  "recipientName": null,
                  "recipientPhone": null,
                  "addressLine": "12 Lê Lợi, Đà Nẵng",
                  "geoSystem": null,
                  "provinceUnitId": null,
                  "districtUnitId": null,
                  "wardUnitId": null,
                  "provinceName": null,
                  "districtName": null,
                  "wardName": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createCustomerAddress",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.update"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          }
        ]
      }
    },
    "/api/open/v1/customers/{id}/addresses/{addressId}": {
      "patch": {
        "description": "Thay nội dung một địa chỉ giao hàng; trùng đúng một địa chỉ khác của khách thì trả `conflict`. Gửi tên địa danh (tỉnh, huyện, xã) mà không gửi mã thì mã đơn vị cũ bị XOÁ — địa chỉ không còn đẩy hãng được tới khi gửi lại mã; không gửi trường địa danh nào thì giữ nguyên.",
        "summary": "Sửa địa chỉ của khách",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          },
          {
            "name": "addressId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã địa chỉ."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "recipientName": {
                    "type": "string",
                    "maxLength": 200,
                    "nullable": true,
                    "description": "Tên người nhận."
                  },
                  "recipientPhone": {
                    "type": "string",
                    "maxLength": 32,
                    "nullable": true,
                    "description": "Số điện thoại người nhận."
                  },
                  "addressLine": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "Số nhà, đường."
                  },
                  "provinceName": {
                    "type": "string",
                    "maxLength": 120,
                    "nullable": true,
                    "description": "Tỉnh hoặc thành phố."
                  },
                  "districtName": {
                    "type": "string",
                    "maxLength": 120,
                    "nullable": true,
                    "description": "Quận hoặc huyện."
                  },
                  "wardName": {
                    "type": "string",
                    "maxLength": 120,
                    "nullable": true,
                    "description": "Phường hoặc xã."
                  },
                  "geoSystem": {
                    "type": "string",
                    "enum": [
                      "old",
                      "new"
                    ],
                    "nullable": true,
                    "description": "Hệ địa giới của các mã bên dưới: `old` (tỉnh, huyện, xã) hoặc `new` (tỉnh, xã). Bỏ trống thì suy từ mã."
                  },
                  "provinceUnitId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Mã (UUID) tỉnh hoặc thành phố trong danh mục hành chính của DANIX."
                  },
                  "districtUnitId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Mã (UUID) quận hoặc huyện. Hệ `new` không có cấp này."
                  },
                  "wardUnitId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Mã (UUID) phường hoặc xã."
                  }
                },
                "required": [
                  "addressLine"
                ],
                "description": "Thân thêm hoặc sửa địa chỉ giao hàng."
              },
              "example": {
                "addressLine": "34 Trần Phú"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Một địa chỉ giao hàng của khách.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerAddress"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000999",
                  "recipientName": null,
                  "recipientPhone": null,
                  "addressLine": "34 Trần Phú",
                  "geoSystem": "old",
                  "provinceUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001501",
                  "districtUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001502",
                  "wardUnitId": "018f3b8e-1c2d-7a4b-9c3d-000000001503",
                  "provinceName": "Đà Nẵng",
                  "districtName": "Hải Châu",
                  "wardName": "Thạch Thang"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "updateCustomerAddress",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.update"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          }
        ]
      },
      "delete": {
        "description": "Xoá một địa chỉ giao hàng khỏi hồ sơ khách. Đơn cũ vẫn giữ địa chỉ đã ghi.",
        "summary": "Xoá địa chỉ của khách",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          },
          {
            "name": "addressId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã địa chỉ."
          }
        ],
        "responses": {
          "200": {
            "description": "Thao tác thành công, không có dữ liệu trả về.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Luôn `true` khi thao tác thành công."
                    }
                  },
                  "required": [
                    "ok"
                  ],
                  "description": "Thao tác thành công, không có dữ liệu trả về."
                },
                "example": {
                  "ok": true
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "deleteCustomerAddress",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.delete"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/customers/{id}/notes": {
      "get": {
        "description": "Danh sách ghi chú, mới nhất trước.",
        "summary": "Ghi chú của khách",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Danh sách ghi chú.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Note"
                      },
                      "description": "Các ghi chú, mới nhất trước."
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "description": "Danh sách ghi chú."
                },
                "example": {
                  "data": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listCustomerNotes",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      },
      "post": {
        "description": "Ghi chú đứng tên ứng dụng kết nối.",
        "summary": "Thêm ghi chú vào khách",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "description": "Nội dung ghi chú."
                  }
                },
                "required": [
                  "message"
                ],
                "description": "Thân thêm ghi chú."
              },
              "example": {
                "message": "Khách thích giao buổi tối"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một ghi chú.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Note"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000000778",
                  "message": "Khách thích giao buổi tối",
                  "createdByName": "Đồng bộ kế toán",
                  "createdByIsIntegration": true,
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "updatedAt": "2026-10-02T03:15:00.000Z"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createCustomerNote",
        "x-required-permissions": {
          "anyOf": [
            "pos.customers.update"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/shipments": {
      "get": {
        "description": "Phân trang theo con trỏ. Dòng danh sách không có hành trình; lấy bằng `GET /shipments/{id}`.",
        "summary": "Liệt kê vận đơn",
        "tags": [],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Số dòng mỗi trang, từ 1 đến 100. Mặc định 50.",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "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.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "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).",
            "schema": {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?!0000-)"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Chỉ lấy vận đơn ở trạng thái này.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivering",
                "delivered",
                "delivery_failed",
                "returning",
                "returned",
                "partially_returned",
                "cancelled",
                "lost",
                "damaged",
                "exception",
                "scrapped"
              ]
            }
          },
          {
            "name": "orderId",
            "in": "query",
            "description": "Chỉ lấy vận đơn của đơn này.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một trang vận đơn.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ShipmentSummary"
                      },
                      "description": "Các bản ghi của trang này."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Con trỏ của trang kế; `null` khi đã hết dữ liệu.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "Một trang vận đơn."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000001001",
                      "carrier": "ghn",
                      "trackingCode": "GHNABC123",
                      "status": "delivering",
                      "orderId": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                      "orderCode": "DH1024",
                      "codAmount": "480000",
                      "feeTotal": "30000",
                      "createdAt": "2026-10-02T03:15:00.000Z",
                      "expectedDeliveryAt": null,
                      "failReason": null,
                      "updatedAt": "2026-10-02T03:15:00.000Z"
                    }
                  ],
                  "nextCursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listShipments",
        "x-required-permissions": {
          "anyOf": [
            "pos.shipping.read"
          ],
          "allOf": [
            "pos.orders.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "invalid-cursor",
            "status": 400,
            "title": "Con trỏ phân trang không hợp lệ"
          }
        ]
      },
      "post": {
        "description": "Đẩy đơn sang hãng vận chuyển qua một kết nối đã cấu hình trong shop.",
        "summary": "Tạo vận đơn cho một đơn",
        "tags": [],
        "parameters": [],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "orderId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Đơn hàng cần giao."
                  },
                  "connectionId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Kết nối hãng vận chuyển của shop dùng để giao."
                  },
                  "weightGram": {
                    "type": "integer",
                    "minimum": 0,
                    "exclusiveMinimum": 0,
                    "maximum": 50000,
                    "description": "Khối lượng khai với hãng, gram, từ 1 tới 50000."
                  },
                  "codAmount": {
                    "type": "string",
                    "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
                    "description": "Tiền thu hộ. Mặc định theo đơn."
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 5000,
                    "description": "Ghi chú cho hãng."
                  }
                },
                "required": [
                  "orderId",
                  "connectionId"
                ],
                "description": "Thân tạo vận đơn cho một đơn."
              },
              "example": {
                "orderId": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                "connectionId": "018f3b8e-1c2d-7a4b-9c3d-000000001301"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một vận đơn kèm hành trình.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Shipment"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001001",
                  "carrier": "ghn",
                  "trackingCode": "GHNABC123",
                  "status": "delivering",
                  "orderId": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                  "orderCode": "DH1024",
                  "codAmount": "480000",
                  "feeTotal": "30000",
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "expectedDeliveryAt": null,
                  "failReason": null,
                  "recipientName": "Nguyễn Văn An",
                  "recipientPhone": "0901234567",
                  "recipientAddress": "12 Lê Lợi, Thạch Thang, Hải Châu, Đà Nẵng",
                  "weightGram": 500,
                  "pickedUpAt": "2026-10-02T03:15:00.000Z",
                  "events": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Hãng vận chuyển hiện không với tới được: thử lại sau.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "createShipment",
        "x-required-permissions": {
          "anyOf": [
            "pos.shipping.read"
          ],
          "allOf": [
            "pos.orders.update",
            "pos.products.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          },
          {
            "code": "order-status-not-allowed",
            "status": 422,
            "title": "Không chuyển được đơn sang trạng thái này từ trạng thái hiện tại"
          },
          {
            "code": "insufficient-stock",
            "status": 422,
            "title": "Không đủ tồn kho cho thao tác này"
          },
          {
            "code": "carrier-unavailable",
            "status": 503,
            "title": "Hãng vận chuyển hiện không với tới được, hãy thử lại sau"
          }
        ]
      }
    },
    "/api/open/v1/shipments/{id}": {
      "get": {
        "description": "Trả vận đơn kèm hành trình theo thứ tự thời gian.",
        "summary": "Chi tiết một vận đơn",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Một vận đơn kèm hành trình.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Shipment"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001001",
                  "carrier": "ghn",
                  "trackingCode": "GHNABC123",
                  "status": "delivering",
                  "orderId": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                  "orderCode": "DH1024",
                  "codAmount": "480000",
                  "feeTotal": "30000",
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "expectedDeliveryAt": null,
                  "failReason": null,
                  "recipientName": "Nguyễn Văn An",
                  "recipientPhone": "0901234567",
                  "recipientAddress": "12 Lê Lợi, Thạch Thang, Hải Châu, Đà Nẵng",
                  "weightGram": 500,
                  "pickedUpAt": "2026-10-02T03:15:00.000Z",
                  "events": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getShipment",
        "x-required-permissions": {
          "anyOf": [
            "pos.shipping.read"
          ],
          "allOf": [
            "pos.orders.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/shipments/{id}/cancel": {
      "post": {
        "description": "Huỷ vận đơn chưa kết thúc. Vận đơn đã giao hay đã hoàn không huỷ được.",
        "summary": "Huỷ vận đơn",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Lý do huỷ, lưu vào nhật ký."
                  }
                },
                "description": "Thân huỷ vận đơn."
              },
              "example": {
                "reason": "Khách đổi ý"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Một vận đơn kèm hành trình.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Shipment"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001001",
                  "carrier": "ghn",
                  "trackingCode": "GHNABC123",
                  "status": "delivering",
                  "orderId": "018f3b8e-1c2d-7a4b-9c3d-000000000101",
                  "orderCode": "DH1024",
                  "codAmount": "480000",
                  "feeTotal": "30000",
                  "createdAt": "2026-10-02T03:15:00.000Z",
                  "expectedDeliveryAt": null,
                  "failReason": null,
                  "recipientName": "Nguyễn Văn An",
                  "recipientPhone": "0901234567",
                  "recipientAddress": "12 Lê Lợi, Thạch Thang, Hải Châu, Đà Nẵng",
                  "weightGram": 500,
                  "pickedUpAt": "2026-10-02T03:15:00.000Z",
                  "events": []
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Hãng vận chuyển hiện không với tới được: thử lại sau.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "cancelShipment",
        "x-required-permissions": {
          "anyOf": [
            "pos.shipping.read"
          ],
          "allOf": [
            "pos.orders.update"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "shipment-not-cancellable",
            "status": 409,
            "title": "Vận đơn không còn huỷ được"
          },
          {
            "code": "carrier-unavailable",
            "status": 503,
            "title": "Hãng vận chuyển hiện không với tới được, hãy thử lại sau"
          }
        ]
      }
    },
    "/api/open/v1/pages": {
      "get": {
        "description": "Chỉ liệt kê những trang mà ứng dụng kết nối được chọn lúc tạo.",
        "summary": "Các trang chat được dùng",
        "tags": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Danh sách trang.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Page"
                      },
                      "description": "Các trang."
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "description": "Danh sách trang."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000001301",
                      "providerPageId": "1090000000000001",
                      "provider": "facebook_page",
                      "pageName": "Shop Mẫu",
                      "avatarUrl": null,
                      "status": "active"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listPages",
        "x-required-permissions": {
          "anyOf": [
            "social.pages.read"
          ],
          "allOf": []
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          }
        ]
      }
    },
    "/api/open/v1/conversations": {
      "get": {
        "description": "Phân trang theo con trỏ, hội thoại có tin mới nhất trước. Lọc theo trang bằng `pageId` (mã trang trong DANIX, `id` của `GET /pages`); trang ngoài danh sách của ứng dụng bị từ chối `not-found`. Hội thoại chưa có tin nào không được liệt kê (vẫn đọc được theo id).",
        "summary": "Liệt kê hội thoại",
        "tags": [],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Số dòng mỗi trang, từ 1 đến 100. Mặc định 50.",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "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.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "Chỉ lấy hội thoại có `lastMessageAt` từ thời điểm này (ISO 8601 UTC), tính cả mốc. `lastMessageAt` là giờ của tin mới nhất theo kênh (Facebook, Zalo), không phải mốc sửa: tin tới muộn hay lịch sử đồng bộ về sau có thể mang giờ trước mốc đã đọc, còn đổi thẻ, đánh dấu đã đọc hay đổi tên khách không làm hội thoại hiện lại. Không dùng để đồng bộ tăng dần.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?!0000-)"
            }
          },
          {
            "name": "pageId",
            "in": "query",
            "description": "Chỉ lấy hội thoại của trang này: mã trang trong DANIX (`id` của `GET /pages`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "unread",
            "in": "query",
            "description": "`true` chỉ lấy hội thoại còn tin chưa đọc.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "name": "tagId",
            "in": "query",
            "description": "Chỉ lấy hội thoại có thẻ này.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một trang hội thoại.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      },
                      "description": "Các bản ghi của trang này."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Con trỏ của trang kế; `null` khi đã hết dữ liệu.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "Một trang hội thoại."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000001201",
                      "pageId": "018f3b8e-1c2d-7a4b-9c3d-000000001301",
                      "providerPageId": "1090000000000001",
                      "provider": "facebook_page",
                      "type": "INBOX",
                      "customerName": "Nguyễn Văn An",
                      "customerAvatarUrl": null,
                      "unreadCount": 1,
                      "lastMessageText": "Shop ơi áo này còn size M không?",
                      "lastMessageAt": "2026-10-02T03:15:00.000Z",
                      "lastMessageBy": "customer",
                      "tags": [],
                      "createdAt": "2026-10-01T03:15:00.000Z"
                    }
                  ],
                  "nextCursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listConversations",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.read",
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "invalid-cursor",
            "status": 400,
            "title": "Con trỏ phân trang không hợp lệ"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/conversations/{id}": {
      "get": {
        "description": "Trả hội thoại kèm thẻ và số tin chưa đọc.",
        "summary": "Chi tiết một hội thoại",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Một hội thoại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001201",
                  "pageId": "018f3b8e-1c2d-7a4b-9c3d-000000001301",
                  "providerPageId": "1090000000000001",
                  "provider": "facebook_page",
                  "type": "INBOX",
                  "customerName": "Nguyễn Văn An",
                  "customerAvatarUrl": null,
                  "unreadCount": 0,
                  "lastMessageText": "Dạ còn ạ",
                  "lastMessageAt": "2026-10-02T03:16:00.000Z",
                  "lastMessageBy": "page",
                  "tags": [],
                  "createdAt": "2026-10-01T03:15:00.000Z"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getConversation",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.read",
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/conversations/{id}/read": {
      "post": {
        "description": "Đặt số tin chưa đọc về 0.",
        "summary": "Đánh dấu hội thoại đã đọc",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "responses": {
          "200": {
            "description": "Thao tác thành công, không có dữ liệu trả về.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Luôn `true` khi thao tác thành công."
                    }
                  },
                  "required": [
                    "ok"
                  ],
                  "description": "Thao tác thành công, không có dữ liệu trả về."
                },
                "example": {
                  "ok": true
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "markConversationRead",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.read",
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/conversations/{id}/messages": {
      "get": {
        "description": "Phân trang theo con trỏ, tin mới nhất trước.",
        "summary": "Tin nhắn của hội thoại",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Con trỏ lấy từ `nextCursor` của trang trước.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Số tin mỗi trang, 1 đến 100.",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Một trang tin nhắn.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      },
                      "description": "Các bản ghi của trang này."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Con trỏ của trang kế; `null` khi đã hết dữ liệu.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "Một trang tin nhắn."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000001101",
                      "conversationId": "018f3b8e-1c2d-7a4b-9c3d-000000001201",
                      "pageId": "018f3b8e-1c2d-7a4b-9c3d-000000001301",
                      "providerPageId": "1090000000000001",
                      "provider": "facebook_page",
                      "direction": "outbound",
                      "type": "text",
                      "text": "Dạ còn ạ, anh chị cần mấy cái ạ?",
                      "status": "sent",
                      "isDeleted": false,
                      "sentByName": "Trần Thị Bình",
                      "sentByIsIntegration": false,
                      "customerName": "Nguyễn Văn An",
                      "attachments": [],
                      "createdAt": "2026-10-02T03:15:00.000Z"
                    }
                  ],
                  "nextCursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listMessages",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.read",
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "invalid-cursor",
            "status": 400,
            "title": "Con trỏ phân trang không hợp lệ"
          }
        ]
      },
      "post": {
        "description": "Gửi một tin nhắn chữ trong hội thoại. Gửi kèm `Idempotency-Key`: cùng khoá trong CÙNG hội thoại không gửi hai lần (cùng khoá ở hội thoại khác là một tin mới). Ngoài khung thời gian cho phép của kênh trả `messaging-window-closed`.",
        "summary": "Gửi tin nhắn",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Khoá chống trùng do client đặt (khuyến nghị UUID). Gửi lại cùng khoá trong 24 giờ trả đúng kết quả lần đầu và header `Idempotent-Replayed: true`."
            }
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "description": "Nội dung tin nhắn."
                  },
                  "replyToMessageId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Trả lời một tin có sẵn của hội thoại."
                  }
                },
                "required": [
                  "text"
                ],
                "description": "Thân gửi tin nhắn. Nên kèm header `Idempotency-Key` để gửi lại an toàn."
              },
              "example": {
                "text": "Dạ còn ạ, anh chị cần mấy cái ạ?"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một tin nhắn hoặc bình luận.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001101",
                  "conversationId": "018f3b8e-1c2d-7a4b-9c3d-000000001201",
                  "pageId": "018f3b8e-1c2d-7a4b-9c3d-000000001301",
                  "providerPageId": "1090000000000001",
                  "provider": "facebook_page",
                  "direction": "outbound",
                  "type": "text",
                  "text": "Dạ còn ạ, anh chị cần mấy cái ạ?",
                  "status": "sent",
                  "isDeleted": false,
                  "sentByName": "Trần Thị Bình",
                  "sentByIsIntegration": false,
                  "customerName": "Nguyễn Văn An",
                  "attachments": [],
                  "createdAt": "2026-10-02T03:15:00.000Z"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "`true` khi đây là kết quả của lần gửi trước với cùng `Idempotency-Key`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Hãng vận chuyển hiện không với tới được: thử lại sau.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "sendMessage",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "idempotency-key-in-progress",
            "status": 409,
            "title": "Yêu cầu với cùng Idempotency-Key đang được xử lý"
          },
          {
            "code": "idempotency-key-reused",
            "status": 422,
            "title": "Idempotency-Key đã dùng cho một yêu cầu có nội dung khác"
          },
          {
            "code": "messaging-window-closed",
            "status": 422,
            "title": "Đã quá khung thời gian được phép nhắn cho khách này"
          },
          {
            "code": "page-not-connected",
            "status": 422,
            "title": "Trang chat không còn kết nối"
          },
          {
            "code": "conversation-not-replyable",
            "status": 422,
            "title": "Hội thoại này không trả lời được"
          },
          {
            "code": "channel-unavailable",
            "status": 503,
            "title": "Kênh chat tạm thời không phản hồi, hãy thử lại sau"
          }
        ],
        "x-idempotent": true
      }
    },
    "/api/open/v1/tags": {
      "get": {
        "description": "Thẻ hội thoại của các trang ứng dụng được dùng. Thẻ là của từng trang (`pageId`): chỉ gắn được vào hội thoại cùng trang.",
        "summary": "Các thẻ hội thoại",
        "tags": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Danh sách thẻ.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChatTag"
                      },
                      "description": "Các thẻ."
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "description": "Danh sách thẻ."
                },
                "example": {
                  "data": [
                    {
                      "id": "018f3b8e-1c2d-7a4b-9c3d-000000001401",
                      "name": "Khách quen",
                      "color": "blue",
                      "pageId": "018f3b8e-1c2d-7a4b-9c3d-000000001301"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listChatTags",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.read",
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          }
        ]
      }
    },
    "/api/open/v1/conversations/{id}/tags": {
      "post": {
        "description": "Gắn một thẻ có sẵn vào hội thoại. Gắn lại thẻ đã có là thành công.",
        "summary": "Gắn thẻ vào hội thoại",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Mã thẻ cần gắn."
                  }
                },
                "required": [
                  "tagId"
                ],
                "description": "Thân gắn thẻ vào hội thoại."
              },
              "example": {
                "tagId": "018f3b8e-1c2d-7a4b-9c3d-000000001401"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Thao tác thành công, không có dữ liệu trả về.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Luôn `true` khi thao tác thành công."
                    }
                  },
                  "required": [
                    "ok"
                  ],
                  "description": "Thao tác thành công, không có dữ liệu trả về."
                },
                "example": {
                  "ok": true
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "addConversationTag",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/conversations/{id}/tags/{tagId}": {
      "delete": {
        "description": "Gỡ một thẻ khỏi hội thoại.",
        "summary": "Gỡ thẻ khỏi hội thoại",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          },
          {
            "name": "tagId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã thẻ."
          }
        ],
        "responses": {
          "200": {
            "description": "Thao tác thành công, không có dữ liệu trả về.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Luôn `true` khi thao tác thành công."
                    }
                  },
                  "required": [
                    "ok"
                  ],
                  "description": "Thao tác thành công, không có dữ liệu trả về."
                },
                "example": {
                  "ok": true
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "removeConversationTag",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          }
        ]
      }
    },
    "/api/open/v1/comments/{id}/hidden": {
      "post": {
        "description": "Chỉ áp dụng cho bình luận (`type = COMMENT`) của trang Facebook.",
        "summary": "Ẩn hoặc hiện một bình luận",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hidden": {
                    "type": "boolean",
                    "description": "`true` ẩn bình luận, `false` hiện lại."
                  }
                },
                "required": [
                  "hidden"
                ],
                "description": "Thân ẩn hoặc hiện bình luận."
              },
              "example": {
                "hidden": true
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Thao tác thành công, không có dữ liệu trả về.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Luôn `true` khi thao tác thành công."
                    }
                  },
                  "required": [
                    "ok"
                  ],
                  "description": "Thao tác thành công, không có dữ liệu trả về."
                },
                "example": {
                  "ok": true
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Hãng vận chuyển hiện không với tới được: thử lại sau.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "setCommentHidden",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "page-not-connected",
            "status": 422,
            "title": "Trang chat không còn kết nối"
          },
          {
            "code": "conversation-not-replyable",
            "status": 422,
            "title": "Hội thoại này không trả lời được"
          },
          {
            "code": "channel-unavailable",
            "status": 503,
            "title": "Kênh chat tạm thời không phản hồi, hãy thử lại sau"
          }
        ]
      }
    },
    "/api/open/v1/comments/{id}/private-reply": {
      "post": {
        "description": "Gửi một tin nhắn riêng đáp lại bình luận. Mỗi bình luận chỉ nhắn riêng được một lần.",
        "summary": "Nhắn riêng cho người bình luận",
        "tags": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mã định danh (UUID) của tài nguyên."
          }
        ],
        "requestBody": {
          "description": "Thân yêu cầu (JSON)",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "description": "Nội dung tin nhắn riêng gửi cho người bình luận."
                  }
                },
                "required": [
                  "text"
                ],
                "description": "Thân nhắn riêng cho người bình luận."
              },
              "example": {
                "text": "Cảm ơn bạn đã quan tâm, shop nhắn riêng nhé"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Một tin nhắn hoặc bình luận.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": "018f3b8e-1c2d-7a4b-9c3d-000000001101",
                  "conversationId": "018f3b8e-1c2d-7a4b-9c3d-000000001201",
                  "pageId": "018f3b8e-1c2d-7a4b-9c3d-000000001301",
                  "providerPageId": "1090000000000001",
                  "provider": "facebook_page",
                  "direction": "outbound",
                  "type": "text",
                  "text": "Dạ còn ạ, anh chị cần mấy cái ạ?",
                  "status": "sent",
                  "isDeleted": false,
                  "sentByName": "Trần Thị Bình",
                  "sentByIsIntegration": false,
                  "customerName": "Nguyễn Văn An",
                  "attachments": [],
                  "createdAt": "2026-10-02T03:15:00.000Z"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Hạn mức số yêu cầu trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Số yêu cầu còn lại trong cửa sổ hiện tại.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Số giây còn lại tới khi cửa sổ làm mới.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Dữ liệu gửi lên không hợp lệ (xem `code` và `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Thiếu khoá API, khoá sai hoặc đã bị thu hồi.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Gói cước của shop hết hạn: khoá chỉ còn quyền đọc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Khoá không có quyền thực hiện thao tác, shop bị đình chỉ hoặc chưa bật Tự động hoá.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Không tìm thấy tài nguyên (hoặc khoá không được thấy nó).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Xung đột với trạng thái hiện tại, hoặc Idempotency-Key đang được xử lý.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "Thân yêu cầu vượt quá dung lượng cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Thân yêu cầu không phải JSON: gửi `Content-Type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Dữ liệu hợp lệ nhưng nghiệp vụ không cho phép.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Vượt hạn mức gọi API: chờ theo header `Retry-After` rồi thử lại.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Số giây phải chờ trước khi thử lại.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Lỗi hệ thống.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Hãng vận chuyển hiện không với tới được: thử lại sau.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "sendPrivateReply",
        "x-required-permissions": {
          "anyOf": [
            "social.conversations.reply"
          ],
          "allOf": [
            "social.pages.read"
          ]
        },
        "x-error-codes": [
          {
            "code": "invalid-api-key",
            "status": 401,
            "title": "Khoá API thiếu, sai hoặc đã bị thu hồi"
          },
          {
            "code": "insufficient-permission",
            "status": 403,
            "title": "Khoá API không có quyền thực hiện thao tác này"
          },
          {
            "code": "shop-suspended",
            "status": 403,
            "title": "Shop đang bị đình chỉ"
          },
          {
            "code": "automation-not-active",
            "status": 403,
            "title": "Shop chưa bật tính năng Tự động hoá"
          },
          {
            "code": "rate-limited",
            "status": 429,
            "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại"
          },
          {
            "code": "internal-error",
            "status": 500,
            "title": "Lỗi hệ thống"
          },
          {
            "code": "subscription-expired",
            "status": 402,
            "title": "Gói cước của shop đã hết hạn, chỉ còn quyền đọc"
          },
          {
            "code": "validation-failed",
            "status": 400,
            "title": "Dữ liệu gửi lên không hợp lệ"
          },
          {
            "code": "payload-too-large",
            "status": 413,
            "title": "Thân yêu cầu vượt quá dung lượng cho phép"
          },
          {
            "code": "unsupported-media-type",
            "status": 415,
            "title": "Thân yêu cầu phải là JSON (`Content-Type: application/json`)"
          },
          {
            "code": "not-found",
            "status": 404,
            "title": "Không tìm thấy tài nguyên"
          },
          {
            "code": "conflict",
            "status": 409,
            "title": "Thao tác xung đột với trạng thái hiện tại"
          },
          {
            "code": "messaging-window-closed",
            "status": 422,
            "title": "Đã quá khung thời gian được phép nhắn cho khách này"
          },
          {
            "code": "page-not-connected",
            "status": 422,
            "title": "Trang chat không còn kết nối"
          },
          {
            "code": "conversation-not-replyable",
            "status": 422,
            "title": "Hội thoại này không trả lời được"
          },
          {
            "code": "channel-unavailable",
            "status": 503,
            "title": "Kênh chat tạm thời không phản hồi, hãy thử lại sau"
          }
        ]
      }
    }
  },
  "info": {
    "title": "DANIX Open API",
    "version": "1.0.0",
    "description": "API công khai của DANIX theo từng shop. Xác thực bằng khoá `dnx_live_…` trong header `Authorization: Bearer`. Tiền và số lượng là chuỗi thập phân; thời gian là ISO 8601 UTC; lỗi theo RFC 9457 kèm `code` ổn định."
  },
  "servers": [
    {
      "url": "https://danix.vn",
      "description": "Máy chủ chính"
    }
  ],
  "components": {
    "schemas": {
      "Shop": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã định danh của shop."
          },
          "slug": {
            "type": "string",
            "description": "Tên ngắn của shop trong đường dẫn."
          },
          "name": {
            "type": "string",
            "description": "Tên shop."
          },
          "application": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "description": "Tên ứng dụng kết nối đang gọi."
              },
              "permissions": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Các quyền khoá này được cấp, ví dụ `pos.orders.read`."
              },
              "pageIds": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid"
                },
                "description": "Mã (UUID) trong DANIX của các trang chat khoá này được dùng — cùng giá trị `id` của `GET /pages`. Rỗng nếu khoá không có quyền chat."
              }
            },
            "required": [
              "name",
              "permissions",
              "pageIds"
            ],
            "description": "Thông tin ứng dụng kết nối gắn với khoá."
          },
          "readOnly": {
            "type": "boolean",
            "description": "`true` khi gói cước hết hạn: khoá chỉ đọc, ghi trả 402."
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "application",
          "readOnly"
        ],
        "description": "Shop của khoá API.",
        "title": "Shop"
      },
      "Problem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Địa chỉ định danh loại lỗi (RFC 9457)."
          },
          "title": {
            "type": "string",
            "description": "Tiêu đề ngắn của lỗi, tiếng Việt."
          },
          "status": {
            "type": "integer",
            "description": "Mã trạng thái HTTP."
          },
          "detail": {
            "type": "string",
            "description": "Mô tả chi tiết của lần lỗi này."
          },
          "code": {
            "type": "string",
            "description": "Mã lỗi ổn định, kebab-case. Lập trình theo trường này."
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string",
                  "description": "Đường tới trường sai, ví dụ `lines.0.quantity`."
                },
                "message": {
                  "type": "string",
                  "description": "Vì sao trường đó sai."
                }
              },
              "required": [
                "path",
                "message"
              ]
            },
            "description": "Chỉ có khi `code` là `validation-failed`: danh sách trường sai."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "description": "Lỗi theo RFC 9457 kèm mã ổn định.",
        "title": "Problem"
      },
      "GeoUnit": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã (UUID) của đơn vị trong danh mục của DANIX."
          },
          "system": {
            "type": "string",
            "enum": [
              "old",
              "new"
            ],
            "description": "Hệ địa giới: `old` (tỉnh, huyện, xã — trước 01/07/2025) hoặc `new` (tỉnh, xã)."
          },
          "level": {
            "type": "integer",
            "description": "Cấp: 1 tỉnh hoặc thành phố, 2 quận hoặc huyện (chỉ hệ `old`), 3 phường hoặc xã."
          },
          "parentId": {
            "type": "string",
            "format": "uuid",
            "description": "Đơn vị cấp trên; `null` với cấp tỉnh.",
            "nullable": true
          },
          "name": {
            "type": "string",
            "description": "Tên đơn vị."
          }
        },
        "required": [
          "id",
          "system",
          "level",
          "parentId",
          "name"
        ],
        "description": "Một đơn vị hành chính.",
        "title": "GeoUnit"
      },
      "OrderAddress": {
        "type": "object",
        "properties": {
          "recipientName": {
            "type": "string",
            "description": "Tên người nhận.",
            "nullable": true
          },
          "recipientPhone": {
            "type": "string",
            "description": "Số điện thoại người nhận.",
            "nullable": true
          },
          "addressLine": {
            "type": "string",
            "description": "Số nhà, đường.",
            "nullable": true
          },
          "geoSystem": {
            "type": "string",
            "enum": [
              "old",
              "new"
            ],
            "description": "Hệ địa giới của các mã bên dưới: `old` (tỉnh, huyện, xã) hoặc `new` (tỉnh, xã). `null` khi địa chỉ chỉ có chữ.",
            "nullable": true
          },
          "provinceUnitId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã (UUID) tỉnh hoặc thành phố; `null` khi không có.",
            "nullable": true
          },
          "districtUnitId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã (UUID) quận hoặc huyện; hệ `new` luôn `null`.",
            "nullable": true
          },
          "wardUnitId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã (UUID) phường hoặc xã; `null` khi không có.",
            "nullable": true
          },
          "provinceName": {
            "type": "string",
            "description": "Tỉnh hoặc thành phố.",
            "nullable": true
          },
          "districtName": {
            "type": "string",
            "description": "Quận hoặc huyện.",
            "nullable": true
          },
          "wardName": {
            "type": "string",
            "description": "Phường hoặc xã.",
            "nullable": true
          }
        },
        "required": [
          "recipientName",
          "recipientPhone",
          "addressLine",
          "geoSystem",
          "provinceUnitId",
          "districtUnitId",
          "wardUnitId",
          "provinceName",
          "districtName",
          "wardName"
        ],
        "description": "Địa chỉ giao hàng. Chỉ có ở chi tiết đơn, không có trong danh sách.",
        "title": "OrderAddress"
      },
      "OrderTag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã thẻ."
          },
          "name": {
            "type": "string",
            "description": "Tên thẻ."
          }
        },
        "required": [
          "id",
          "name"
        ],
        "description": "Một thẻ gắn vào đơn.",
        "title": "OrderTag"
      },
      "OrderLine": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã dòng hàng."
          },
          "variantId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã mẫu mã."
          },
          "productId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã sản phẩm của mẫu mã.",
            "nullable": true
          },
          "variantCode": {
            "type": "string",
            "description": "Mã mẫu mã. Vắng khi khoá thiếu `pos.products.read`.",
            "nullable": true
          },
          "variantName": {
            "type": "string",
            "description": "Tên mẫu mã. Vắng khi khoá thiếu `pos.products.read`.",
            "nullable": true
          },
          "productName": {
            "type": "string",
            "description": "Tên sản phẩm. Vắng khi khoá thiếu `pos.products.read`.",
            "nullable": true
          },
          "quantity": {
            "type": "string",
            "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
            "description": "Số lượng. Chuỗi thập phân, tối đa ba chữ số lẻ."
          },
          "unitPrice": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Đơn giá. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "discount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Giảm giá của dòng. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "lineTotal": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Thành tiền của dòng, đã trừ giảm giá. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "returnedQuantity": {
            "type": "string",
            "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
            "description": "Số lượng khách đã hoàn. Chuỗi thập phân, tối đa ba chữ số lẻ."
          }
        },
        "required": [
          "id",
          "variantId",
          "productId",
          "quantity",
          "unitPrice",
          "discount",
          "lineTotal",
          "returnedQuantity"
        ],
        "description": "Một dòng hàng của đơn.",
        "title": "OrderLine"
      },
      "OrderPayment": {
        "type": "object",
        "properties": {
          "methodId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã phương thức thanh toán."
          },
          "methodName": {
            "type": "string",
            "description": "Tên phương thức thanh toán."
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Số tiền thu theo phương thức này. Chuỗi thập phân, ví dụ \"150000\"."
          }
        },
        "required": [
          "methodId",
          "methodName",
          "amount"
        ],
        "description": "Một khoản thu của đơn.",
        "title": "OrderPayment"
      },
      "Order": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã định danh của đơn."
          },
          "code": {
            "type": "string",
            "description": "Mã đơn, duy nhất trong shop."
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "waiting_stock",
              "confirmed",
              "packing",
              "ready_to_ship",
              "shipped",
              "delivered",
              "paid",
              "returning",
              "partially_returned",
              "returned",
              "cancelled",
              "deleted"
            ],
            "description": "Trạng thái đơn."
          },
          "warehouseId": {
            "type": "string",
            "format": "uuid",
            "description": "Kho xuất hàng."
          },
          "warehouseName": {
            "type": "string",
            "description": "Tên kho xuất hàng.",
            "nullable": true
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Khách hàng gắn với đơn.",
            "nullable": true
          },
          "salesChannelId": {
            "type": "string",
            "format": "uuid",
            "description": "Kênh bán.",
            "nullable": true
          },
          "salesChannelName": {
            "type": "string",
            "description": "Tên kênh bán.",
            "nullable": true
          },
          "receivedAtShop": {
            "type": "boolean",
            "description": "Khách nhận hàng tại shop."
          },
          "billFullName": {
            "type": "string",
            "description": "Tên người mua ghi trên đơn.",
            "nullable": true
          },
          "billPhone": {
            "type": "string",
            "description": "Số điện thoại người mua.",
            "nullable": true
          },
          "billEmail": {
            "type": "string",
            "description": "Email người mua.",
            "nullable": true
          },
          "shippingAddress": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/OrderAddress"
              }
            ]
          },
          "totalPrice": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tiền hàng. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "discount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Giảm giá cả đơn. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "shippingFee": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Phí vận chuyển khách trả. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "freeShipping": {
            "type": "boolean",
            "description": "Shop chịu phí giao hàng."
          },
          "surcharge": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Phụ thu. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "tax": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Thuế. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "totalAmount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tổng tiền đơn. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "returnedAmount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tiền hàng khách đã hoàn. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "paidAmount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Đã thanh toán. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "creditApplied": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tiền cấn trừ vào đơn này từ phiếu đổi trả (khách trả hàng của một đơn khác): khách đã trả cho đơn này bằng khoản ấy, nên số còn phải thu đã trừ nó. Chỉ có ở chi tiết đơn, không có trong danh sách. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "exchangeReturn": {
            "type": "boolean",
            "description": "Đơn trả để ĐỔI hàng: mọi lượt khách trả hàng của đơn đều là đổi (đổi size, đổi màu…), nên đơn không tính là hoàn — không vào tỉ lệ hoàn của số điện thoại, không gửi tin hoàn hay sự kiện hoàn sang Facebook. Chỉ có ở chi tiết đơn, không có trong danh sách."
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm phát sinh đơn. ISO 8601, múi giờ UTC."
          },
          "note": {
            "type": "string",
            "description": "Ghi chú nội bộ.",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderTag"
            },
            "description": "Các thẻ của đơn."
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderLine"
            },
            "description": "Các dòng hàng. Chỉ có ở chi tiết đơn, không có trong danh sách."
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderPayment"
            },
            "description": "Các khoản thu. Chỉ có ở chi tiết đơn, không có trong danh sách."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo. ISO 8601, múi giờ UTC."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm sửa gần nhất. ISO 8601, múi giờ UTC."
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm xoá mềm; `null` nếu chưa xoá. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "code",
          "status",
          "warehouseId",
          "warehouseName",
          "customerId",
          "salesChannelId",
          "salesChannelName",
          "receivedAtShop",
          "billFullName",
          "billPhone",
          "billEmail",
          "totalPrice",
          "discount",
          "shippingFee",
          "freeShipping",
          "surcharge",
          "tax",
          "totalAmount",
          "returnedAmount",
          "paidAmount",
          "occurredAt",
          "note",
          "tags",
          "createdAt",
          "updatedAt",
          "deletedAt"
        ],
        "description": "Một đơn hàng.",
        "title": "Order"
      },
      "Note": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã ghi chú."
          },
          "message": {
            "type": "string",
            "description": "Nội dung ghi chú."
          },
          "createdByName": {
            "type": "string",
            "description": "Tên người hay ứng dụng đã ghi.",
            "nullable": true
          },
          "createdByIsIntegration": {
            "type": "boolean",
            "description": "`true` khi ghi chú do một ứng dụng kết nối tạo."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo. ISO 8601, múi giờ UTC."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm sửa gần nhất. ISO 8601, múi giờ UTC."
          }
        },
        "required": [
          "id",
          "message",
          "createdByName",
          "createdByIsIntegration",
          "createdAt",
          "updatedAt"
        ],
        "description": "Một ghi chú.",
        "title": "Note"
      },
      "ProductSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã sản phẩm."
          },
          "code": {
            "type": "string",
            "description": "Mã sản phẩm, duy nhất trong shop."
          },
          "name": {
            "type": "string",
            "description": "Tên sản phẩm."
          },
          "type": {
            "type": "string",
            "enum": [
              "simple",
              "service"
            ],
            "description": "Loại: `simple` (hàng hoá) hoặc `service` (dịch vụ)."
          },
          "isActive": {
            "type": "boolean",
            "description": "Sản phẩm còn bán."
          },
          "categoryNames": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tên các danh mục chứa sản phẩm."
          },
          "variantCount": {
            "type": "integer",
            "description": "Số mẫu mã."
          },
          "imageUrl": {
            "type": "string",
            "description": "Ảnh đại diện.",
            "nullable": true
          },
          "priceFrom": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Giá bán thấp nhất trong các mẫu mã. Chuỗi thập phân hoặc `null`.",
            "nullable": true
          },
          "priceTo": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Giá bán cao nhất trong các mẫu mã. Chuỗi thập phân hoặc `null`.",
            "nullable": true
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "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."
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time",
            "description": "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`.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "type",
          "isActive",
          "categoryNames",
          "variantCount",
          "imageUrl",
          "priceFrom",
          "priceTo",
          "updatedAt",
          "deletedAt"
        ],
        "description": "Một dòng trong danh sách sản phẩm.",
        "title": "ProductSummary"
      },
      "Variant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã mẫu mã."
          },
          "code": {
            "type": "string",
            "description": "Mã mẫu mã (SKU), duy nhất trong shop."
          },
          "barcode": {
            "type": "string",
            "description": "Mã vạch.",
            "nullable": true
          },
          "price": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Giá bán. Chuỗi thập phân, ví dụ \"150000\"."
          },
          "weightGrams": {
            "type": "string",
            "description": "Cân nặng, gram. Chuỗi thập phân.",
            "nullable": true
          },
          "lengthCm": {
            "type": "string",
            "description": "Chiều dài, cm.",
            "nullable": true
          },
          "widthCm": {
            "type": "string",
            "description": "Chiều rộng, cm.",
            "nullable": true
          },
          "heightCm": {
            "type": "string",
            "description": "Chiều cao, cm.",
            "nullable": true
          },
          "isActive": {
            "type": "boolean",
            "description": "Mẫu mã còn bán."
          },
          "attributes": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "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."
          }
        },
        "required": [
          "id",
          "code",
          "barcode",
          "price",
          "weightGrams",
          "lengthCm",
          "widthCm",
          "heightCm",
          "isActive",
          "attributes"
        ],
        "description": "Một mẫu mã (biến thể) của sản phẩm.",
        "title": "Variant"
      },
      "ProductImage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã tệp."
          },
          "url": {
            "type": "string",
            "description": "Đường dẫn tuyệt đối tới tệp."
          },
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ],
            "description": "Loại tệp."
          },
          "variantIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Các mẫu mã dùng tệp này; rỗng là tệp chung."
          }
        },
        "required": [
          "id",
          "url",
          "kind",
          "variantIds"
        ],
        "description": "Ảnh hoặc video của sản phẩm.",
        "title": "ProductImage"
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã sản phẩm."
          },
          "code": {
            "type": "string",
            "description": "Mã sản phẩm, duy nhất trong shop."
          },
          "name": {
            "type": "string",
            "description": "Tên sản phẩm."
          },
          "description": {
            "type": "string",
            "description": "Mô tả.",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "simple",
              "service"
            ],
            "description": "Loại: `simple` (hàng hoá) hoặc `service` (dịch vụ)."
          },
          "isActive": {
            "type": "boolean",
            "description": "Sản phẩm còn bán."
          },
          "categoryIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Các danh mục chứa sản phẩm."
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variant"
            },
            "description": "Các mẫu mã."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductImage"
            },
            "description": "Ảnh và video."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo. ISO 8601, múi giờ UTC."
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm xoá mềm; `null` nếu chưa xoá. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "description",
          "type",
          "isActive",
          "categoryIds",
          "variants",
          "images",
          "createdAt",
          "deletedAt"
        ],
        "description": "Một sản phẩm kèm mẫu mã và ảnh.",
        "title": "Product"
      },
      "Category": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã danh mục."
          },
          "parentId": {
            "type": "string",
            "format": "uuid",
            "description": "Danh mục cha.",
            "nullable": true
          },
          "name": {
            "type": "string",
            "description": "Tên danh mục."
          },
          "slug": {
            "type": "string",
            "description": "Tên ngắn trong đường dẫn."
          }
        },
        "required": [
          "id",
          "parentId",
          "name",
          "slug"
        ],
        "description": "Một danh mục sản phẩm.",
        "title": "Category"
      },
      "Warehouse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã kho."
          },
          "code": {
            "type": "string",
            "description": "Mã kho."
          },
          "name": {
            "type": "string",
            "description": "Tên kho."
          },
          "phone": {
            "type": "string",
            "description": "Số điện thoại kho.",
            "nullable": true
          },
          "addressLine": {
            "type": "string",
            "description": "Địa chỉ kho.",
            "nullable": true
          },
          "provinceName": {
            "type": "string",
            "description": "Tỉnh hoặc thành phố.",
            "nullable": true
          },
          "districtName": {
            "type": "string",
            "description": "Quận hoặc huyện.",
            "nullable": true
          },
          "wardName": {
            "type": "string",
            "description": "Phường hoặc xã.",
            "nullable": true
          },
          "isDefault": {
            "type": "boolean",
            "description": "Kho mặc định của shop."
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "phone",
          "addressLine",
          "provinceName",
          "districtName",
          "wardName",
          "isDefault"
        ],
        "description": "Một kho hàng.",
        "title": "Warehouse"
      },
      "StockRow": {
        "type": "object",
        "properties": {
          "variantId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã mẫu mã."
          },
          "productId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã sản phẩm."
          },
          "productName": {
            "type": "string",
            "description": "Tên sản phẩm."
          },
          "variantName": {
            "type": "string",
            "description": "Tên mẫu mã."
          },
          "code": {
            "type": "string",
            "description": "Mã mẫu mã (SKU)."
          },
          "barcode": {
            "type": "string",
            "description": "Mã vạch.",
            "nullable": true
          },
          "warehouseId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã kho."
          },
          "warehouseName": {
            "type": "string",
            "description": "Tên kho."
          },
          "quantity": {
            "type": "string",
            "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
            "description": "Tồn thực trong kho. Chuỗi thập phân, tối đa ba chữ số lẻ."
          },
          "inTransit": {
            "type": "string",
            "pattern": "^\\d{1,9}(\\.\\d{1,3})?$",
            "description": "Số lượng đang trên đường về kho. Chuỗi thập phân, tối đa ba chữ số lẻ."
          },
          "price": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Giá bán. Chuỗi thập phân, ví dụ \"150000\"."
          }
        },
        "required": [
          "variantId",
          "productId",
          "productName",
          "variantName",
          "code",
          "barcode",
          "warehouseId",
          "warehouseName",
          "quantity",
          "inTransit",
          "price"
        ],
        "description": "Tồn kho của một mẫu mã tại một kho.",
        "title": "StockRow"
      },
      "InventoryDocumentLine": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã dòng phiếu."
          },
          "variantId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã mẫu mã."
          },
          "variantCode": {
            "type": "string",
            "description": "Mã mẫu mã."
          },
          "quantity": {
            "type": "string",
            "description": "Số lượng nhập hoặc xuất. Chuỗi thập phân.",
            "nullable": true
          },
          "countedQuantity": {
            "type": "string",
            "description": "Số đếm thực tế (phiếu kiểm kho).",
            "nullable": true
          },
          "unitCost": {
            "type": "string",
            "nullable": true,
            "description": "Giá nhập. Chỉ có khi khoá được xem giá vốn."
          },
          "note": {
            "type": "string",
            "description": "Ghi chú dòng.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "variantId",
          "variantCode",
          "quantity",
          "countedQuantity",
          "note"
        ],
        "description": "Một dòng của phiếu kho.",
        "title": "InventoryDocumentLine"
      },
      "InventoryDocument": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã phiếu."
          },
          "kind": {
            "type": "string",
            "description": "Loại phiếu: `receipt`, `issue`, `stocktake`…"
          },
          "code": {
            "type": "string",
            "description": "Mã phiếu."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "ordered",
              "posted",
              "in_transit",
              "completed",
              "cancelled"
            ],
            "description": "Trạng thái phiếu: `draft` là nháp, `posted` đã ghi sổ."
          },
          "warehouseId": {
            "type": "string",
            "format": "uuid",
            "description": "Kho của phiếu."
          },
          "supplierId": {
            "type": "string",
            "format": "uuid",
            "description": "Nhà cung cấp.",
            "nullable": true
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "Ngày của phiếu. ISO 8601, múi giờ UTC."
          },
          "postedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm ghi sổ. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          },
          "costingMethod": {
            "type": "string",
            "enum": [
              "average",
              "fifo"
            ],
            "description": "Phương pháp tính giá vốn khi ghi sổ.",
            "nullable": true
          },
          "note": {
            "type": "string",
            "description": "Ghi chú phiếu.",
            "nullable": true
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InventoryDocumentLine"
            },
            "description": "Các dòng của phiếu."
          }
        },
        "required": [
          "id",
          "kind",
          "code",
          "status",
          "warehouseId",
          "supplierId",
          "occurredAt",
          "postedAt",
          "costingMethod",
          "note",
          "lines"
        ],
        "description": "Một phiếu kho.",
        "title": "InventoryDocument"
      },
      "CustomerSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã khách hàng."
          },
          "name": {
            "type": "string",
            "description": "Tên khách."
          },
          "gender": {
            "type": "string",
            "enum": [
              "male",
              "female",
              "other"
            ],
            "description": "Giới tính.",
            "nullable": true
          },
          "dateOfBirth": {
            "type": "string",
            "description": "Ngày sinh, dạng YYYY-MM-DD.",
            "nullable": true
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "order",
              "import"
            ],
            "description": "Nguồn tạo hồ sơ: `manual`, `order` hoặc `import`."
          },
          "isBlocked": {
            "type": "boolean",
            "description": "Khách đang bị chặn."
          },
          "primaryPhone": {
            "type": "string",
            "description": "Số điện thoại chính.",
            "nullable": true
          },
          "primaryEmail": {
            "type": "string",
            "description": "Email chính.",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo. ISO 8601, múi giờ UTC."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm sửa gần nhất. ISO 8601, múi giờ UTC."
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm xoá mềm; `null` nếu chưa xoá. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "name",
          "gender",
          "dateOfBirth",
          "source",
          "isBlocked",
          "primaryPhone",
          "primaryEmail",
          "createdAt",
          "updatedAt",
          "deletedAt"
        ],
        "description": "Một dòng trong danh sách khách hàng.",
        "title": "CustomerSummary"
      },
      "CustomerContact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã liên hệ."
          },
          "kind": {
            "type": "string",
            "enum": [
              "phone",
              "email"
            ],
            "description": "Loại liên hệ."
          },
          "value": {
            "type": "string",
            "description": "Giá trị đã chuẩn hoá (số điện thoại 0xxxxxxxxx hoặc email)."
          },
          "isPrimary": {
            "type": "boolean",
            "description": "Liên hệ chính của loại này."
          }
        },
        "required": [
          "id",
          "kind",
          "value",
          "isPrimary"
        ],
        "description": "Một số điện thoại hoặc email của khách.",
        "title": "CustomerContact"
      },
      "CustomerAddress": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã địa chỉ."
          },
          "recipientName": {
            "type": "string",
            "description": "Tên người nhận.",
            "nullable": true
          },
          "recipientPhone": {
            "type": "string",
            "description": "Số điện thoại người nhận.",
            "nullable": true
          },
          "addressLine": {
            "type": "string",
            "description": "Số nhà, đường."
          },
          "geoSystem": {
            "type": "string",
            "enum": [
              "old",
              "new"
            ],
            "description": "Hệ địa giới của các mã bên dưới: `old` (tỉnh, huyện, xã) hoặc `new` (tỉnh, xã). `null` khi địa chỉ chỉ có chữ.",
            "nullable": true
          },
          "provinceUnitId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã (UUID) tỉnh hoặc thành phố; `null` khi không có.",
            "nullable": true
          },
          "districtUnitId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã (UUID) quận hoặc huyện; hệ `new` luôn `null`.",
            "nullable": true
          },
          "wardUnitId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã (UUID) phường hoặc xã; `null` khi không có.",
            "nullable": true
          },
          "provinceName": {
            "type": "string",
            "description": "Tỉnh hoặc thành phố.",
            "nullable": true
          },
          "districtName": {
            "type": "string",
            "description": "Quận hoặc huyện.",
            "nullable": true
          },
          "wardName": {
            "type": "string",
            "description": "Phường hoặc xã.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "recipientName",
          "recipientPhone",
          "addressLine",
          "geoSystem",
          "provinceUnitId",
          "districtUnitId",
          "wardUnitId",
          "provinceName",
          "districtName",
          "wardName"
        ],
        "description": "Một địa chỉ giao hàng của khách.",
        "title": "CustomerAddress"
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã khách hàng."
          },
          "name": {
            "type": "string",
            "description": "Tên khách."
          },
          "gender": {
            "type": "string",
            "enum": [
              "male",
              "female",
              "other"
            ],
            "description": "Giới tính.",
            "nullable": true
          },
          "dateOfBirth": {
            "type": "string",
            "description": "Ngày sinh, dạng YYYY-MM-DD.",
            "nullable": true
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "order",
              "import"
            ],
            "description": "Nguồn tạo hồ sơ: `manual`, `order` hoặc `import`."
          },
          "isBlocked": {
            "type": "boolean",
            "description": "Khách đang bị chặn."
          },
          "primaryPhone": {
            "type": "string",
            "description": "Số điện thoại chính.",
            "nullable": true
          },
          "primaryEmail": {
            "type": "string",
            "description": "Email chính.",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo. ISO 8601, múi giờ UTC."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm sửa gần nhất. ISO 8601, múi giờ UTC."
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm xoá mềm; `null` nếu chưa xoá. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          },
          "contacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerContact"
            },
            "description": "Số điện thoại và email."
          },
          "addresses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerAddress"
            },
            "description": "Địa chỉ giao hàng, địa chỉ dùng gần nhất đứng đầu."
          }
        },
        "required": [
          "id",
          "name",
          "gender",
          "dateOfBirth",
          "source",
          "isBlocked",
          "primaryPhone",
          "primaryEmail",
          "createdAt",
          "updatedAt",
          "deletedAt",
          "contacts",
          "addresses"
        ],
        "description": "Một khách hàng kèm liên hệ và địa chỉ.",
        "title": "Customer"
      },
      "ShipmentSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã vận đơn."
          },
          "carrier": {
            "type": "string",
            "enum": [
              "ghn",
              "vtp"
            ],
            "description": "Hãng vận chuyển."
          },
          "trackingCode": {
            "type": "string",
            "description": "Mã vận đơn của hãng. Rỗng khi chưa đẩy sang hãng.",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivering",
              "delivered",
              "delivery_failed",
              "returning",
              "returned",
              "partially_returned",
              "cancelled",
              "lost",
              "damaged",
              "exception",
              "scrapped"
            ],
            "description": "Trạng thái chuẩn của vận đơn."
          },
          "orderId": {
            "type": "string",
            "format": "uuid",
            "description": "Đơn hàng của vận đơn."
          },
          "orderCode": {
            "type": "string",
            "description": "Mã đơn hàng."
          },
          "codAmount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tiền thu hộ (COD). Chuỗi thập phân hoặc `null`.",
            "nullable": true
          },
          "feeTotal": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tổng phí vận chuyển. Chuỗi thập phân hoặc `null`.",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo. ISO 8601, múi giờ UTC."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "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."
          },
          "expectedDeliveryAt": {
            "type": "string",
            "format": "date-time",
            "description": "Dự kiến giao. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          },
          "failReason": {
            "type": "string",
            "description": "Lý do giao thất bại, nếu có.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "carrier",
          "trackingCode",
          "status",
          "orderId",
          "orderCode",
          "codAmount",
          "feeTotal",
          "createdAt",
          "updatedAt",
          "expectedDeliveryAt",
          "failReason"
        ],
        "description": "Một dòng trong danh sách vận đơn.",
        "title": "ShipmentSummary"
      },
      "ShipmentEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã sự kiện."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivering",
              "delivered",
              "delivery_failed",
              "returning",
              "returned",
              "partially_returned",
              "cancelled",
              "lost",
              "damaged",
              "exception",
              "scrapped"
            ],
            "description": "Trạng thái chuẩn sau sự kiện; `null` nếu sự kiện không đổi trạng thái.",
            "nullable": true
          },
          "carrierStatus": {
            "type": "string",
            "description": "Mã trạng thái gốc của hãng.",
            "nullable": true
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm hãng ghi nhận. ISO 8601, múi giờ UTC."
          },
          "location": {
            "type": "string",
            "description": "Nơi xảy ra.",
            "nullable": true
          },
          "description": {
            "type": "string",
            "description": "Mô tả của hãng.",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "description": "Lý do (giao thất bại, hoàn…).",
            "nullable": true
          }
        },
        "required": [
          "id",
          "status",
          "carrierStatus",
          "occurredAt",
          "location",
          "description",
          "reason"
        ],
        "description": "Một mốc trong hành trình vận đơn.",
        "title": "ShipmentEvent"
      },
      "Shipment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã vận đơn."
          },
          "carrier": {
            "type": "string",
            "enum": [
              "ghn",
              "vtp"
            ],
            "description": "Hãng vận chuyển."
          },
          "trackingCode": {
            "type": "string",
            "description": "Mã vận đơn của hãng. Rỗng khi chưa đẩy sang hãng.",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivering",
              "delivered",
              "delivery_failed",
              "returning",
              "returned",
              "partially_returned",
              "cancelled",
              "lost",
              "damaged",
              "exception",
              "scrapped"
            ],
            "description": "Trạng thái chuẩn của vận đơn."
          },
          "orderId": {
            "type": "string",
            "format": "uuid",
            "description": "Đơn hàng của vận đơn."
          },
          "orderCode": {
            "type": "string",
            "description": "Mã đơn hàng."
          },
          "codAmount": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tiền thu hộ (COD). Chuỗi thập phân hoặc `null`.",
            "nullable": true
          },
          "feeTotal": {
            "type": "string",
            "pattern": "^\\d{1,12}(\\.\\d{1,4})?$",
            "description": "Tổng phí vận chuyển. Chuỗi thập phân hoặc `null`.",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo. ISO 8601, múi giờ UTC."
          },
          "expectedDeliveryAt": {
            "type": "string",
            "format": "date-time",
            "description": "Dự kiến giao. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          },
          "failReason": {
            "type": "string",
            "description": "Lý do giao thất bại, nếu có.",
            "nullable": true
          },
          "recipientName": {
            "type": "string",
            "description": "Tên người nhận.",
            "nullable": true
          },
          "recipientPhone": {
            "type": "string",
            "description": "Số điện thoại người nhận.",
            "nullable": true
          },
          "recipientAddress": {
            "type": "string",
            "description": "Địa chỉ người nhận.",
            "nullable": true
          },
          "weightGram": {
            "type": "integer",
            "description": "Khối lượng khai với hãng, gram.",
            "nullable": true
          },
          "pickedUpAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm hãng lấy hàng. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShipmentEvent"
            },
            "description": "Hành trình, theo thứ tự thời gian."
          }
        },
        "required": [
          "id",
          "carrier",
          "trackingCode",
          "status",
          "orderId",
          "orderCode",
          "codAmount",
          "feeTotal",
          "createdAt",
          "expectedDeliveryAt",
          "failReason",
          "recipientName",
          "recipientPhone",
          "recipientAddress",
          "weightGram",
          "pickedUpAt",
          "events"
        ],
        "description": "Một vận đơn kèm hành trình.",
        "title": "Shipment"
      },
      "Page": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã trang trong DANIX. Đây là giá trị dùng làm `pageId` ở mọi chỗ khác của API."
          },
          "providerPageId": {
            "type": "string",
            "description": "Mã trang ở kênh gốc (Facebook, Zalo…). Chỉ để đối chiếu, không dùng làm tham số."
          },
          "provider": {
            "type": "string",
            "enum": [
              "facebook_page",
              "zalo_oa",
              "zalo_personal"
            ],
            "description": "Kênh: `facebook_page`, `zalo_oa` hoặc `zalo_personal`."
          },
          "pageName": {
            "type": "string",
            "description": "Tên trang."
          },
          "avatarUrl": {
            "type": "string",
            "description": "Ảnh đại diện của trang, lưu trên kho của DANIX. `null` khi chưa có bản lưu.",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "token_expired",
              "banned"
            ],
            "description": "Tình trạng kết nối: `active`, `token_expired` hoặc `banned`."
          }
        },
        "required": [
          "id",
          "providerPageId",
          "provider",
          "pageName",
          "avatarUrl",
          "status"
        ],
        "description": "Một trang chat mà khoá được dùng.",
        "title": "Page"
      },
      "Conversation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã hội thoại."
          },
          "pageId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã trang trong DANIX (`id` của `GET /pages`)."
          },
          "providerPageId": {
            "type": "string",
            "description": "Mã trang ở kênh gốc."
          },
          "provider": {
            "type": "string",
            "enum": [
              "facebook_page",
              "zalo_oa",
              "zalo_personal"
            ],
            "description": "Kênh của hội thoại."
          },
          "type": {
            "type": "string",
            "enum": [
              "INBOX",
              "COMMENT",
              "GROUP"
            ],
            "description": "Loại: `INBOX` tin nhắn, `COMMENT` bình luận, `GROUP` nhóm."
          },
          "customerName": {
            "type": "string",
            "description": "Tên khách ở kênh gốc.",
            "nullable": true
          },
          "customerAvatarUrl": {
            "type": "string",
            "description": "Ảnh đại diện của khách, lưu trên kho của DANIX. `null` khi chưa có bản lưu.",
            "nullable": true
          },
          "unreadCount": {
            "type": "integer",
            "description": "Số tin khách chưa được đọc."
          },
          "lastMessageText": {
            "type": "string",
            "description": "Nội dung tin gần nhất.",
            "nullable": true
          },
          "lastMessageAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tin gần nhất. ISO 8601 UTC hoặc `null`.",
            "nullable": true
          },
          "lastMessageBy": {
            "type": "string",
            "enum": [
              "customer",
              "page"
            ],
            "description": "Ai gửi tin gần nhất.",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "Mã thẻ."
                },
                "name": {
                  "type": "string",
                  "description": "Tên thẻ."
                }
              },
              "required": [
                "id",
                "name"
              ]
            },
            "description": "Các thẻ gắn vào hội thoại."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tạo hội thoại. ISO 8601, múi giờ UTC."
          }
        },
        "required": [
          "id",
          "pageId",
          "providerPageId",
          "provider",
          "type",
          "customerName",
          "customerAvatarUrl",
          "unreadCount",
          "lastMessageText",
          "lastMessageAt",
          "lastMessageBy",
          "tags",
          "createdAt"
        ],
        "description": "Một hội thoại.",
        "title": "Conversation"
      },
      "MessageAttachment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã tệp đính kèm."
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "file",
              "link",
              "button"
            ],
            "description": "Loại: bốn loại đầu là tệp; `link` và `button` là nút của tin mẫu."
          },
          "url": {
            "type": "string",
            "description": "Tệp: bản GỐC trên kho của DANIX, `null` khi chưa lưu về (xem `originalState`). Loại `link`: địa chỉ nút trỏ tới. Không bao giờ là đường dẫn của Facebook hay Zalo.",
            "nullable": true
          },
          "previewUrl": {
            "type": "string",
            "description": "Bản xem trước (ảnh thu nhỏ, ảnh bìa video) trên kho của DANIX; `null` khi không có.",
            "nullable": true
          },
          "originalState": {
            "type": "string",
            "enum": [
              "pending",
              "stored",
              "gone"
            ],
            "description": "`stored`: bản gốc có ở `url`. `pending`: chưa có bản gốc trên kho của DANIX. Tệp của hội thoại lâu không hoạt động đã được cất đi; lượt đọc trang ĐẦU tin nhắn (`GET /conversations/{id}/messages` không `cursor`) hay lượt nhân viên mở hội thoại kéo nó về ở nền trong ít phút — đọc lại sau để nhận `url`. Tệp chưa từng lưu bản gốc thì được lưu khi nhân viên mở xem. `gone`: nguồn không còn, sẽ không bao giờ có."
          },
          "fileName": {
            "type": "string",
            "description": "Tên tệp.",
            "nullable": true
          },
          "mimeType": {
            "type": "string",
            "description": "Loại nội dung (MIME).",
            "nullable": true
          }
        },
        "required": [
          "id",
          "type",
          "url",
          "previewUrl",
          "originalState",
          "fileName",
          "mimeType"
        ],
        "description": "Một tệp đính kèm của tin nhắn.",
        "title": "MessageAttachment"
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã tin nhắn."
          },
          "conversationId": {
            "type": "string",
            "format": "uuid",
            "description": "Hội thoại chứa tin."
          },
          "pageId": {
            "type": "string",
            "format": "uuid",
            "description": "Mã trang trong DANIX (`id` của `GET /pages`)."
          },
          "providerPageId": {
            "type": "string",
            "description": "Mã trang ở kênh gốc."
          },
          "provider": {
            "type": "string",
            "enum": [
              "facebook_page",
              "zalo_oa",
              "zalo_personal"
            ],
            "description": "Kênh của tin nhắn."
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ],
            "description": "`inbound` khách gửi, `outbound` shop gửi."
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "video",
              "audio",
              "file",
              "sticker",
              "location",
              "reel",
              "share",
              "like",
              "postback",
              "order",
              "referral"
            ],
            "description": "Loại tin nhắn."
          },
          "text": {
            "type": "string",
            "description": "Nội dung chữ.",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sent",
              "delivered",
              "read",
              "failed"
            ],
            "description": "Trạng thái gửi."
          },
          "isDeleted": {
            "type": "boolean",
            "description": "Tin đã bị thu hồi."
          },
          "sentByName": {
            "type": "string",
            "description": "Tên nhân viên hay ứng dụng đã gửi; rỗng với tin của khách.",
            "nullable": true
          },
          "sentByIsIntegration": {
            "type": "boolean",
            "description": "`true` khi tin do một ứng dụng kết nối gửi."
          },
          "customerName": {
            "type": "string",
            "description": "Tên khách của hội thoại; với nhóm (`GROUP`) là người gửi tin.",
            "nullable": true
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageAttachment"
            },
            "description": "Tệp đính kèm."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Thời điểm tin được ghi nhận. ISO 8601, múi giờ UTC."
          }
        },
        "required": [
          "id",
          "conversationId",
          "pageId",
          "providerPageId",
          "provider",
          "direction",
          "type",
          "text",
          "status",
          "isDeleted",
          "sentByName",
          "sentByIsIntegration",
          "customerName",
          "attachments",
          "createdAt"
        ],
        "description": "Một tin nhắn hoặc bình luận.",
        "title": "Message"
      },
      "ChatTag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mã thẻ."
          },
          "name": {
            "type": "string",
            "description": "Tên thẻ."
          },
          "color": {
            "type": "string",
            "enum": [
              "red",
              "orange",
              "amber",
              "green",
              "teal",
              "cyan",
              "blue",
              "indigo",
              "purple",
              "pink"
            ],
            "description": "Màu của thẻ."
          },
          "pageId": {
            "type": "string",
            "format": "uuid",
            "description": "Trang sở hữu thẻ. Thẻ là của TỪNG trang: chỉ gắn được vào hội thoại cùng trang, và hai trang có thể có thẻ trùng tên."
          }
        },
        "required": [
          "id",
          "name",
          "color",
          "pageId"
        ],
        "description": "Một thẻ hội thoại.",
        "title": "ChatTag"
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "dnx_live_…",
        "description": "Khoá API theo shop do quản trị viên tạo trong ứng dụng Tự động hoá. Gửi `Authorization: Bearer dnx_live_…`."
      }
    }
  },
  "security": [
    {
      "ApiKey": []
    }
  ]
}
