{
  "openapi": "3.0.0",
  "paths": {
    "/items": {
      "get": {
        "operationId": "listItems",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by item name, barcode or item code",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (1 = active, 0 = inactive)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "groupId",
            "required": false,
            "in": "query",
            "description": "Filter by group/category id(s) — array or CSV",
            "schema": {
              "type": "array",
              "items": {
                "type": "number"
              }
            }
          },
          {
            "name": "brandId",
            "required": false,
            "in": "query",
            "description": "Filter by brand ID",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "taxId",
            "required": false,
            "in": "query",
            "description": "Filter by tax ID",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "stockStatus",
            "required": false,
            "in": "query",
            "description": "Filter by stock status: inStock, low, out",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "weighingScale",
            "required": false,
            "in": "query",
            "description": "Filter items flagged for weighing scale (1 = yes, 0 = no)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Filter by item type (0 = standard, 1 = variant, 2 = combo)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "storeId",
            "required": false,
            "in": "query",
            "description": "Scope to one store: only items enabled at that store, with price / stock / cost read from that store",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List items",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/items/{id}/full": {
      "get": {
        "operationId": "getItemFull",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get an item definition: per-store prices, variants (SKUs), composite components, modifiers, extras",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/items/{id}": {
      "get": {
        "operationId": "getItem",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "storeId",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get an item",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/categories": {
      "get": {
        "operationId": "listCategories",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by group name",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (1 = active, 0 = inactive)",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List categories",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/categories/{id}": {
      "get": {
        "operationId": "getCategory",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get a category",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/menus": {
      "get": {
        "operationId": "listMenus",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by menu name",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (1 = active, 0 = inactive)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "delivery",
            "required": false,
            "in": "query",
            "description": "Delivery filter (1 = yes, 0 = no, 2/undefined = any)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "takeaway",
            "required": false,
            "in": "query",
            "description": "Takeaway filter (1 = yes, 0 = no, 2/undefined = any)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "online",
            "required": false,
            "in": "query",
            "description": "Online filter (1 = yes, 0 = no, 2/undefined = any)",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List menus",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/menus/{id}/full": {
      "get": {
        "operationId": "getMenuFull",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get a menu with its items and store assignments",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/modifiers": {
      "get": {
        "operationId": "listModifiers",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by modifier name",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "itemSearch",
            "required": false,
            "in": "query",
            "description": "Filter to modifiers applied to an item whose name matches (searches ItemSelectedModifier, Item.modifierId and the Item.Modifiers JSON column)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (1 = active, 0 = inactive)",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List modifier groups",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/modifiers/{id}/full": {
      "get": {
        "operationId": "getModifierFull",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get a modifier group with its options",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/customers": {
      "get": {
        "operationId": "listCustomers",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by name, email, mobile, or loyalty number",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (1 = active, 0 = inactive)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "email",
            "required": false,
            "in": "query",
            "description": "Filter by email",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mobile",
            "required": false,
            "in": "query",
            "description": "Filter by mobile",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List customers",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:RESERVATION",
          "Partner:TABLE_QR_PAYMENT"
        ]
      },
      "post": {
        "operationId": "createCustomer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PartnerCreateCustomerDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Create a customer",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:RESERVATION",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/customers/{id}": {
      "get": {
        "operationId": "getCustomer",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get a customer",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:RESERVATION",
          "Partner:TABLE_QR_PAYMENT"
        ]
      },
      "patch": {
        "operationId": "updateCustomer",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PartnerUpdateCustomerDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Update a customer",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:RESERVATION",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/stores": {
      "get": {
        "operationId": "listStores",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by name, code, or email",
            "schema": {
              "example": "Main Store",
              "type": "string"
            }
          },
          {
            "name": "isActive",
            "required": false,
            "in": "query",
            "description": "Filter by active status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "1 = active only, 0 = inactive only",
            "schema": {
              "example": 1,
              "type": "number",
              "enum": [
                0,
                1
              ]
            }
          },
          {
            "name": "archived",
            "required": false,
            "in": "query",
            "description": "Restrict to archived (true) or non-archived (false) stores",
            "schema": {
              "example": false,
              "type": "boolean"
            }
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "0 = stores only, 1 = warehouses only; omit for both",
            "schema": {
              "example": 0,
              "type": "number",
              "enum": [
                0,
                1
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List stores",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:ADSR",
          "Partner:RESERVATION",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/units": {
      "get": {
        "operationId": "listUnits",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by UOM name or code",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (1 = active, 0 = inactive)",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List units of measure (uomId on items)",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/kitchens": {
      "get": {
        "operationId": "listKitchens",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by kitchen name",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (1 = active, 0 = inactive)",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List kitchens (kitchenId on categories and items)",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/variant-attributes": {
      "get": {
        "operationId": "listVariantAttributes",
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List variant attribute names (Size, Colour, …)",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/online-orders": {
      "get": {
        "operationId": "listOrders",
        "parameters": [
          {
            "name": "order",
            "required": false,
            "in": "query",
            "description": "Sort order (asc or desc)",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "example": "entryDate",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by order number, customer name, mobile, or channel order ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "storeId",
            "required": false,
            "in": "query",
            "description": "Store IDs. Pass 0 (or omit) for all. Supports repeated query keys (`storeId=1&storeId=2`) or a comma-joined string (`storeId=1,2`).",
            "schema": {
              "type": "array",
              "items": {
                "type": "number"
              }
            }
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Filter by order type (0=Standard, 1=DineIn, 2=Delivery, 3=Takeaway, 4=Provider)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status (0=Failed, 1=Active, 2=Done, 3=Cancelled)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "customerId",
            "required": false,
            "in": "query",
            "description": "Filter by customer ID",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "fromDate",
            "required": false,
            "in": "query",
            "description": "From date (YYYY-MM-DD)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "toDate",
            "required": false,
            "in": "query",
            "description": "To date (YYYY-MM-DD)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List online orders",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      },
      "post": {
        "description": "Creates the order and notifies the store in realtime, exactly as the backoffice new-order screen does.",
        "operationId": "createOrder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOnlineOrderDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Create an online order",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/online-orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get an online order",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/online-orders/{id}/status": {
      "get": {
        "description": "Status values: `new`, `preparing`, `ready`, `completed`, `cancelled` — the same buckets the backoffice order counters use.",
        "operationId": "getOrderStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get the current status of an online order",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:ONLINE_ORDER",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/tables": {
      "get": {
        "description": "The merchant's active floor-plan tables, per store. Use `tableId` to print one QR per table and to place orders.",
        "operationId": "listTables",
        "parameters": [
          {
            "name": "storeId",
            "required": false,
            "in": "query",
            "description": "Only this store. Omitted = every store the merchant shared with the app.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Rows per page. Values above 100 are clamped to 100.",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 10,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "List tables",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/tables/{tableId}/bill": {
      "get": {
        "description": "Open orders this app placed on the table, with what is paid and still outstanding. Items rung up on the till are not included.",
        "operationId": "tableBill",
        "parameters": [
          {
            "name": "tableId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get the open bill of a table",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/table-orders": {
      "post": {
        "description": "Prices every line from the store menu (partners never send prices), sends the order to the store in realtime and returns it. Idempotent on `externalOrderId`: 201 when created, 200 with `duplicate: true` when the order already exists.",
        "operationId": "createOrder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePartnerTableOrderDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Place an order on a table",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/table-orders/{orderId}": {
      "get": {
        "description": "Lines, totals and recorded payments.",
        "operationId": "getOrder",
        "parameters": [
          {
            "name": "orderId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get a table order",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/table-orders/{orderId}/status": {
      "get": {
        "description": "`status`: `new`, `preparing`, `ready`, `completed`, `cancelled`. `paymentStatus`: `unpaid`, `partially_paid`, `paid`. `billStatus`: 0 unbilled, 1 billed, 2 paid on the till.",
        "operationId": "getStatus",
        "parameters": [
          {
            "name": "orderId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Get the status of a table order",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/table-orders/{orderId}/payments": {
      "post": {
        "description": "Records a payment the guest made through your app. Partial payments are allowed; once they cover the bill the order is sent to the till as prepaid. Idempotent on `paymentRef`: 201 when recorded, 200 with `duplicate: true` on a replay.",
        "operationId": "recordPayment",
        "parameters": [
          {
            "name": "orderId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePartnerTablePaymentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Record a payment",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    },
    "/table-orders/{orderId}/cancel": {
      "post": {
        "description": "Allowed until the store accepts the order and while no payment is recorded. Cancelling twice is a no-op.",
        "operationId": "cancelOrder",
        "parameters": [
          {
            "name": "orderId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelPartnerTableOrderDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": ""
          },
          "401": {
            "description": "Missing, malformed or expired app token"
          },
          "403": {
            "description": "Token lacks the required scope (partner.scope_denied), is used against the wrong environment (partner.env_mismatch), or comes from a non-allowlisted IP (partner.ip_denied)"
          },
          "429": {
            "description": "Per-app rate limit exceeded (partner.rate_limited)"
          }
        },
        "security": [
          {
            "appToken": []
          }
        ],
        "summary": "Cancel a table order",
        "tags": [
          "Partner",
          "Partner:FULL",
          "Partner:TABLE_QR_PAYMENT"
        ]
      }
    }
  },
  "info": {
    "title": "LithosPOS TABLE QR PAYMENT API",
    "description": "Order and pay at the table for pay-at-table apps: read the stores, floor plan and menu, place guest orders on a table (priced by the store), record the payments guests make by scanning the table QR, and follow order and payment status.\n\nAuthenticate with an app access token — `POST /v1/oauth/token` (authorization code or refresh token, after the merchant authorises the app) or `POST /v1/token` (client credentials, sandbox quickstart). The token selects the environment and the merchant.\n\nCall every path below against `https://api.lithospos.com/v1` — one gateway host for both sandbox and production. The `api_base_url` in the token response is authoritative: prefer it over this document if the two differ.",
    "version": "1.0",
    "contact": {}
  },
  "tags": [],
  "servers": [
    {
      "url": "https://api.lithospos.com/v1",
      "description": "Global API gateway — same host for sandbox and production; the token response api_base_url is authoritative"
    }
  ],
  "components": {
    "securitySchemes": {
      "appToken": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http"
      }
    },
    "schemas": {
      "PartnerCreateCustomerDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Customer name",
            "maxLength": 150
          },
          "mobile": {
            "type": "string",
            "description": "Mobile number",
            "maxLength": 40
          },
          "email": {
            "type": "string",
            "description": "Email address",
            "maxLength": 65
          },
          "loyaltyNo": {
            "type": "string",
            "description": "Loyalty number",
            "maxLength": 20
          },
          "trn": {
            "type": "string",
            "description": "Tax Registration Number",
            "maxLength": 20
          },
          "countryId": {
            "type": "number",
            "description": "Country ID",
            "default": 0
          },
          "stateId": {
            "type": "number",
            "description": "State ID",
            "default": 0
          },
          "state": {
            "type": "string",
            "description": "State name",
            "maxLength": 45
          },
          "address": {
            "type": "string",
            "description": "Address"
          },
          "status": {
            "type": "number",
            "description": "Status (1 = active, 0 = inactive)",
            "default": 1
          },
          "creditCust": {
            "type": "number",
            "description": "Credit customer flag",
            "default": 0
          },
          "creditLimit": {
            "type": "number",
            "description": "Credit limit"
          },
          "opening": {
            "type": "number",
            "description": "Opening balance"
          },
          "loyaltyPoint": {
            "type": "number",
            "description": "Loyalty points",
            "default": 0
          },
          "birthDate": {
            "type": "string",
            "description": "Birth date (YYYY-MM-DD)"
          },
          "anniversary": {
            "type": "string",
            "description": "Anniversary date (YYYY-MM-DD)"
          },
          "firebaseUserId": {
            "type": "string",
            "description": "Firebase user ID",
            "maxLength": 200
          },
          "latitude": {
            "type": "number",
            "description": "Latitude"
          },
          "longitude": {
            "type": "number",
            "description": "Longitude"
          },
          "landmark": {
            "type": "string",
            "description": "Landmark"
          },
          "ZIP": {
            "type": "string",
            "description": "ZIP code",
            "maxLength": 45
          },
          "StoreId": {
            "type": "number",
            "description": "Store ID",
            "default": 0
          },
          "TaxExempt": {
            "type": "number",
            "description": "Tax exempt flag",
            "default": 0
          }
        },
        "required": [
          "name"
        ]
      },
      "PartnerUpdateCustomerDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Customer name",
            "maxLength": 150
          },
          "mobile": {
            "type": "string",
            "description": "Mobile number",
            "maxLength": 40
          },
          "email": {
            "type": "string",
            "description": "Email address",
            "maxLength": 65
          },
          "loyaltyNo": {
            "type": "string",
            "description": "Loyalty number",
            "maxLength": 20
          },
          "trn": {
            "type": "string",
            "description": "Tax Registration Number",
            "maxLength": 20
          },
          "countryId": {
            "type": "number",
            "description": "Country ID",
            "default": 0
          },
          "stateId": {
            "type": "number",
            "description": "State ID",
            "default": 0
          },
          "state": {
            "type": "string",
            "description": "State name",
            "maxLength": 45
          },
          "address": {
            "type": "string",
            "description": "Address"
          },
          "status": {
            "type": "number",
            "description": "Status (1 = active, 0 = inactive)",
            "default": 1
          },
          "creditCust": {
            "type": "number",
            "description": "Credit customer flag",
            "default": 0
          },
          "creditLimit": {
            "type": "number",
            "description": "Credit limit"
          },
          "opening": {
            "type": "number",
            "description": "Opening balance"
          },
          "loyaltyPoint": {
            "type": "number",
            "description": "Loyalty points",
            "default": 0
          },
          "birthDate": {
            "type": "string",
            "description": "Birth date (YYYY-MM-DD)"
          },
          "anniversary": {
            "type": "string",
            "description": "Anniversary date (YYYY-MM-DD)"
          },
          "firebaseUserId": {
            "type": "string",
            "description": "Firebase user ID",
            "maxLength": 200
          },
          "latitude": {
            "type": "number",
            "description": "Latitude"
          },
          "longitude": {
            "type": "number",
            "description": "Longitude"
          },
          "landmark": {
            "type": "string",
            "description": "Landmark"
          },
          "ZIP": {
            "type": "string",
            "description": "ZIP code",
            "maxLength": 45
          },
          "StoreId": {
            "type": "number",
            "description": "Store ID",
            "default": 0
          },
          "TaxExempt": {
            "type": "number",
            "description": "Tax exempt flag",
            "default": 0
          }
        }
      },
      "CreateOnlineOrderItemDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "number"
          },
          "item": {
            "type": "string"
          },
          "qty": {
            "type": "number"
          },
          "rate": {
            "type": "number"
          },
          "note": {
            "type": "string"
          },
          "variantId": {
            "type": "number",
            "description": "Variant master ID (0 for standard items)"
          },
          "itemModifiers": {
            "description": "Modifier group IDs selected on this line",
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "modifiers": {
            "type": "array",
            "description": "Selected modifier items"
          }
        },
        "required": [
          "id",
          "item",
          "qty",
          "rate"
        ]
      },
      "CreateOnlineOrderDto": {
        "type": "object",
        "properties": {
          "storeId": {
            "type": "number",
            "description": "Store the order belongs to"
          },
          "items": {
            "description": "Cart line items",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateOnlineOrderItemDto"
            }
          },
          "orderType": {
            "type": "number",
            "description": "2 = delivery (Home Delivery), 3 = takeout (Take Away)",
            "enum": [
              2,
              3
            ]
          },
          "status": {
            "type": "number",
            "description": "1 = new/confirmed (default); 0 = legacy unsent/pay-failed — avoid"
          },
          "customerId": {
            "type": "number"
          },
          "customerName": {
            "type": "string"
          },
          "customerPhone": {
            "type": "string"
          },
          "subtotal": {
            "type": "number"
          },
          "taxAmount": {
            "type": "number"
          },
          "cessAmount": {
            "type": "number",
            "description": "Total cess amount across the cart"
          },
          "tipAmount": {
            "type": "number"
          },
          "tipPercent": {
            "type": "number"
          },
          "totalAmount": {
            "type": "number"
          },
          "note": {
            "type": "string"
          },
          "takenBy": {
            "type": "string",
            "description": "Who took the order (free text)"
          },
          "cartList": {
            "type": "array",
            "description": "Rich v3 POS-style CartList payload. When provided, persisted verbatim into onlineOrders.CartList; the simpler `items` field is ignored."
          }
        },
        "required": [
          "storeId",
          "items"
        ]
      },
      "PartnerTableOrderModifierDto": {
        "type": "object",
        "properties": {
          "modifierId": {
            "type": "number",
            "description": "Modifier option id (`modifiers[].options[].id` in the menu)"
          },
          "qty": {
            "type": "number",
            "default": 1,
            "minimum": 1,
            "description": "Times the option is picked per unit"
          }
        },
        "required": [
          "modifierId"
        ]
      },
      "PartnerTableOrderItemDto": {
        "type": "object",
        "properties": {
          "itemId": {
            "type": "number",
            "description": "Item id from the menu"
          },
          "variantId": {
            "type": "number",
            "default": 0,
            "description": "Variant id — required for an item sold in variants, 0 otherwise"
          },
          "qty": {
            "type": "number",
            "minimum": 1,
            "description": "Quantity (whole units)"
          },
          "note": {
            "type": "string",
            "maxLength": 200,
            "description": "Kitchen note for this line"
          },
          "modifiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerTableOrderModifierDto"
            }
          }
        },
        "required": [
          "itemId",
          "qty"
        ]
      },
      "PartnerTableOrderCustomerDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "phone": {
            "type": "string",
            "maxLength": 30,
            "description": "E.164 phone, e.g. +971500000000"
          }
        }
      },
      "CreatePartnerTableOrderDto": {
        "type": "object",
        "properties": {
          "storeId": {
            "type": "number",
            "description": "Store the table belongs to"
          },
          "tableId": {
            "type": "number",
            "description": "Table id from `GET /v1/tables`. Wins over `tableName`."
          },
          "tableName": {
            "type": "string",
            "maxLength": 50,
            "description": "Table name as shown on the floor plan (exact match)"
          },
          "externalOrderId": {
            "type": "string",
            "maxLength": 100,
            "description": "Your order id. Idempotency key: sending the same value again returns the existing order (200, `duplicate: true`) instead of creating a second one."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerTableOrderItemDto"
            }
          },
          "customer": {
            "$ref": "#/components/schemas/PartnerTableOrderCustomerDto"
          },
          "note": {
            "type": "string",
            "maxLength": 500,
            "description": "Order note shown on the POS"
          },
          "guests": {
            "type": "number",
            "minimum": 1,
            "description": "Number of guests at the table"
          }
        },
        "required": [
          "storeId",
          "externalOrderId",
          "items"
        ]
      },
      "CreatePartnerTablePaymentDto": {
        "type": "object",
        "properties": {
          "paymentRef": {
            "type": "string",
            "maxLength": 100,
            "description": "Your payment / transaction id. Idempotency key: the same value again returns the original result (200, `duplicate: true`)."
          },
          "amount": {
            "type": "number",
            "description": "Amount paid towards the bill, excluding tip. Must be > 0 and ≤ the outstanding amount."
          },
          "tipAmount": {
            "type": "number",
            "default": 0,
            "description": "Tip on top of the bill"
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 code. When sent it must equal the store currency."
          },
          "paidAt": {
            "type": "string",
            "description": "When the guest paid (ISO 8601). Defaults to now."
          },
          "method": {
            "type": "string",
            "maxLength": 50,
            "description": "Informational: card, apple_pay, …"
          }
        },
        "required": [
          "paymentRef",
          "amount"
        ]
      },
      "CancelPartnerTableOrderDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "maxLength": 200
          }
        }
      }
    }
  }
}
