{
  "openapi": "3.1.0",
  "info": {
    "title": "Якудза Суши — публичный API",
    "version": "1.0",
    "description": "Открытый API только для чтения: меню, цены, состав, часы работы, условия доставки и акции сайта yakuzasushi.by (доставка суши в Бресте).\n\n* Авторизация не нужна, CORS открыт (`Access-Control-Allow-Origin: *`).\n* Только GET (HEAD, OPTIONS). Ответы в UTF-8 JSON, цены в BYN.\n* Кеширование: `Cache-Control: public, max-age=300` и `ETag` — повторяйте запрос с `If-None-Match`, чтобы получить `304`. Данные на сервере обновляются сразу после изменений в каталоге.\n* Пожалуйста, не опрашивайте API чаще раза в 5 минут.\n* Тот же каталог в формате Яндекс YML: https://yakuzasushi.by/catalog.xml\n\nОформить заказ через API нельзя: заказ — на сайте или по телефону.",
    "contact": {
      "name": "Якудза Суши",
      "url": "https://yakuzasushi.by/"
    }
  },
  "servers": [
    {
      "url": "https://yakuzasushi.by/api/v1"
    }
  ],
  "tags": [
    {
      "name": "Меню"
    },
    {
      "name": "Информация"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "getIndex",
        "summary": "Список эндпоинтов",
        "tags": [
          "Информация"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/pretty"
          }
        ],
        "responses": {
          "200": {
            "description": "Список эндпоинтов",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "meta",
                    "endpoints"
                  ],
                  "properties": {
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "description": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "formats": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Не изменилось (запрос с If-None-Match)"
          }
        }
      }
    },
    "/menu": {
      "get": {
        "operationId": "getMenu",
        "summary": "Всё меню одним запросом",
        "description": "Категории и все позиции. Позиция ссылается на категорию через `category_id`, категории образуют дерево через `parent_id`.",
        "tags": [
          "Меню"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/pretty"
          }
        ],
        "responses": {
          "200": {
            "description": "Меню",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "meta",
                    "categories",
                    "products"
                  ],
                  "properties": {
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Category"
                      }
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Не изменилось (запрос с If-None-Match)"
          }
        }
      }
    },
    "/categories": {
      "get": {
        "operationId": "getCategories",
        "summary": "Категории меню",
        "tags": [
          "Меню"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/pretty"
          }
        ],
        "responses": {
          "200": {
            "description": "Категории",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "meta",
                    "categories"
                  ],
                  "properties": {
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Category"
                      }
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Не изменилось (запрос с If-None-Match)"
          }
        }
      }
    },
    "/products": {
      "get": {
        "operationId": "getProducts",
        "summary": "Позиции меню",
        "description": "Без параметров — все позиции. С `category` — позиции категории вместе с подкатегориями. С `id` — одна позиция в поле `product` (вместо `products`).",
        "tags": [
          "Меню"
        ],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "ID категории",
            "example": 7
          },
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "ID позиции",
            "example": 829
          },
          {
            "$ref": "#/components/parameters/pretty"
          }
        ],
        "responses": {
          "200": {
            "description": "Позиции",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "meta"
                  ],
                  "properties": {
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "product": {
                      "$ref": "#/components/schemas/Product"
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Не изменилось (запрос с If-None-Match)"
          },
          "404": {
            "description": "Не найдено",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/info": {
      "get": {
        "operationId": "getInfo",
        "summary": "Контакты, часы работы, доставка, оплата",
        "tags": [
          "Информация"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/pretty"
          }
        ],
        "responses": {
          "200": {
            "description": "Информация о заведении",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "meta",
                    "info"
                  ],
                  "properties": {
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    },
                    "info": {
                      "$ref": "#/components/schemas/Info"
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Не изменилось (запрос с If-None-Match)"
          }
        }
      }
    },
    "/promotions": {
      "get": {
        "operationId": "getPromotions",
        "summary": "Действующие акции",
        "tags": [
          "Меню"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/pretty"
          }
        ],
        "responses": {
          "200": {
            "description": "Акции",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "meta",
                    "promotions"
                  ],
                  "properties": {
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    },
                    "promotions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Promotion"
                      }
                    }
                  }
                }
              }
            }
          },
          "304": {
            "description": "Не изменилось (запрос с If-None-Match)"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Meta": {
        "type": "object",
        "required": [
          "version",
          "source"
        ],
        "properties": {
          "version": {
            "type": "string",
            "description": "Версия API",
            "examples": [
              "1.0"
            ]
          },
          "source": {
            "type": "string",
            "description": "Адрес сайта-источника",
            "format": "uri"
          },
          "count": {
            "type": "integer",
            "description": "Количество элементов в ответе (для списков)"
          },
          "categories": {
            "type": "integer",
            "description": "Количество категорий (только /menu)"
          },
          "products": {
            "type": "integer",
            "description": "Количество позиций (только /menu)"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer",
                "examples": [
                  404
                ]
              },
              "message": {
                "type": "string",
                "examples": [
                  "Product not found"
                ]
              }
            }
          }
        }
      },
      "Category": {
        "type": "object",
        "required": [
          "id",
          "parent_id",
          "name",
          "code",
          "sort",
          "depth",
          "url",
          "image",
          "products_count"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "examples": [
              15
            ]
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "ID родительской категории; null — категория верхнего уровня",
            "examples": [
              6
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Ура маки"
            ]
          },
          "code": {
            "type": "string",
            "description": "Символьный код (часть URL)",
            "examples": [
              "uramaki"
            ]
          },
          "sort": {
            "type": "integer",
            "description": "Порядок сортировки на сайте (по возрастанию)"
          },
          "depth": {
            "type": "integer",
            "description": "Уровень вложенности, 1 — верхний"
          },
          "url": {
            "type": "string",
            "description": "Страница категории на сайте",
            "format": "uri"
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "Картинка категории",
            "format": "uri"
          },
          "products_count": {
            "type": "integer",
            "description": "Число активных позиций в категории"
          }
        }
      },
      "ProductOption": {
        "type": "object",
        "required": [
          "group",
          "name",
          "price",
          "old_price",
          "weight"
        ],
        "description": "Вариант позиции, например основа для WOK",
        "properties": {
          "group": {
            "type": "string",
            "description": "Группа вариантов",
            "examples": [
              "Основа"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Лапша гречневая"
            ]
          },
          "price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Цена варианта, BYN",
            "examples": [
              15.5
            ]
          },
          "old_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Старая цена варианта"
          },
          "weight": {
            "type": [
              "number",
              "null"
            ],
            "description": "Вес варианта, г",
            "examples": [
              500
            ]
          }
        }
      },
      "Nutrition": {
        "type": [
          "object",
          "null"
        ],
        "description": "Пищевая ценность, как на странице товара. null — не заполнено",
        "properties": {
          "proteins": {
            "type": "number",
            "description": "Белки, г"
          },
          "fats": {
            "type": "number",
            "description": "Жиры, г"
          },
          "carbohydrates": {
            "type": "number",
            "description": "Углеводы, г"
          },
          "calories": {
            "type": "number",
            "description": "Калорийность, ккал"
          }
        }
      },
      "Product": {
        "type": "object",
        "required": [
          "id",
          "category_id",
          "name",
          "code",
          "sort",
          "url",
          "price",
          "old_price",
          "currency",
          "weight",
          "weight_unit",
          "pieces",
          "description",
          "ingredients",
          "nutrition_per_100g",
          "badges",
          "options",
          "discounts_apply",
          "likes",
          "image",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "examples": [
              829
            ]
          },
          "category_id": {
            "type": "integer",
            "description": "ID категории (см. /categories)"
          },
          "name": {
            "type": "string",
            "examples": [
              "Запеченный ролл с лососем"
            ]
          },
          "code": {
            "type": "string",
            "description": "Символьный код (часть URL)"
          },
          "sort": {
            "type": "integer",
            "description": "Порядок сортировки внутри категории"
          },
          "url": {
            "type": "string",
            "description": "Страница позиции на сайте",
            "format": "uri"
          },
          "price": {
            "type": "number",
            "description": "Цена, BYN. Если у позиции есть варианты (options) без базовой цены — минимальная цена варианта",
            "examples": [
              18.5
            ]
          },
          "old_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Цена до скидки; null — скидки нет"
          },
          "currency": {
            "type": "string",
            "description": "Код валюты ISO 4217",
            "examples": [
              "BYN"
            ]
          },
          "weight": {
            "type": [
              "number",
              "null"
            ],
            "description": "Вес",
            "examples": [
              260
            ]
          },
          "weight_unit": {
            "type": [
              "string",
              "null"
            ],
            "description": "Единица веса",
            "examples": [
              "г"
            ]
          },
          "pieces": {
            "type": [
              "number",
              "null"
            ],
            "description": "Количество штук в порции",
            "examples": [
              8
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Описание, обычный текст без HTML"
          },
          "ingredients": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Состав",
            "examples": [
              [
                "Лосось",
                "Сыр сливочный"
              ]
            ]
          },
          "nutrition_per_100g": {
            "$ref": "#/components/schemas/Nutrition"
          },
          "badges": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Стикеры: Новинка, Острое, Популярное и т. п.",
            "examples": [
              [
                "Новинка"
              ]
            ]
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductOption"
            },
            "description": "Варианты позиции; пустой массив — вариантов нет"
          },
          "discounts_apply": {
            "type": "boolean",
            "description": "false — на позицию не действуют скидки и акции"
          },
          "likes": {
            "type": "integer",
            "description": "Число лайков на сайте"
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "Фото позиции",
            "format": "uri"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата последнего изменения",
            "format": "date-time"
          }
        }
      },
      "DeliveryZone": {
        "type": "object",
        "required": [
          "id",
          "name",
          "delivery_price",
          "free_delivery_from",
          "min_order_sum"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "examples": [
              "Доставка по городу Брест"
            ]
          },
          "delivery_price": {
            "type": "number",
            "description": "Стоимость доставки, BYN",
            "examples": [
              0
            ]
          },
          "free_delivery_from": {
            "type": [
              "number",
              "null"
            ],
            "description": "Сумма заказа, от которой доставка бесплатна",
            "examples": [
              30
            ]
          },
          "min_order_sum": {
            "type": [
              "number",
              "null"
            ],
            "description": "Минимальная сумма заказа на доставку в эту зону, BYN",
            "examples": [
              30
            ]
          }
        }
      },
      "PickupPoint": {
        "type": "object",
        "required": [
          "id",
          "name",
          "phone",
          "geo"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "examples": [
              "г. Брест, улица Гоголя, 87"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "geo": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "lat": {
                "type": "number"
              },
              "lon": {
                "type": "number"
              }
            }
          }
        }
      },
      "Hours": {
        "type": "object",
        "required": [
          "from",
          "to",
          "days"
        ],
        "properties": {
          "from": {
            "type": "string",
            "description": "Время начала, HH:MM, часовой пояс из info.timezone",
            "examples": [
              "10:00"
            ]
          },
          "to": {
            "type": "string",
            "description": "Время окончания, HH:MM",
            "examples": [
              "22:30"
            ]
          },
          "days": {
            "type": "string",
            "description": "Дни работы",
            "examples": [
              "daily"
            ]
          }
        }
      },
      "Info": {
        "type": "object",
        "required": [
          "name",
          "url",
          "phone",
          "address",
          "timezone",
          "currency",
          "delivery",
          "pickup",
          "payment_methods",
          "hours",
          "status"
        ],
        "properties": {
          "name": {
            "type": "string",
            "examples": [
              "Якудза Суши"
            ]
          },
          "description": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Телефон для заказов",
            "examples": [
              "+375 (29) 783-11-11"
            ]
          },
          "phone_e164": {
            "type": [
              "string",
              "null"
            ],
            "description": "Телефон в формате E.164",
            "examples": [
              "+375297831111"
            ]
          },
          "address": {
            "type": "object",
            "properties": {
              "text": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "г. Брест, ул. Гоголя 87"
                ]
              },
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string",
                "description": "ISO 3166-1 alpha-2",
                "examples": [
                  "BY"
                ]
              }
            }
          },
          "timezone": {
            "type": "string",
            "description": "Часовой пояс IANA",
            "examples": [
              "Europe/Minsk"
            ]
          },
          "currency": {
            "type": "string",
            "examples": [
              "BYN"
            ]
          },
          "delivery": {
            "type": "object",
            "required": [
              "min_order_sum",
              "zones"
            ],
            "properties": {
              "min_order_sum": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Минимальная сумма заказа на доставку, BYN (наименьшая среди зон)",
                "examples": [
                  30
                ]
              },
              "zones": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/DeliveryZone"
                }
              },
              "zones_map_url": {
                "type": "string",
                "description": "Страница с картой зон доставки",
                "format": "uri"
              }
            }
          },
          "pickup": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "discount_percent": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Скидка на самовывоз, %",
                "examples": [
                  10
                ]
              },
              "min_order_sum": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Минимальная сумма заказа на самовывоз; null — минимума нет"
              },
              "points": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PickupPoint"
                }
              }
            }
          },
          "payment_methods": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "type"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "examples": [
                    "Картой онлайн"
                  ]
                },
                "type": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "online — оплата на сайте, offline — при получении",
                  "examples": [
                    "online"
                  ]
                }
              }
            }
          },
          "social": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string",
              "format": "uri"
            },
            "description": "Соцсети: instagram, vk, facebook…"
          },
          "legal": {
            "type": "object",
            "properties": {
              "company": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "unp": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "УНП"
              }
            }
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "format": "uri"
            }
          },
          "hours": {
            "type": "object",
            "required": [
              "orders",
              "delivery"
            ],
            "properties": {
              "orders": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Hours"
                  }
                ],
                "description": "Приём заказов"
              },
              "delivery": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Hours"
                  }
                ],
                "description": "Работа доставки"
              },
              "note": {
                "type": "string"
              }
            }
          },
          "status": {
            "type": "object",
            "required": [
              "accepting_orders"
            ],
            "properties": {
              "accepting_orders": {
                "type": "boolean",
                "description": "Принимаются ли заказы прямо сейчас (с учётом часов работы). Ответ может кешироваться до 5 минут"
              }
            }
          }
        }
      },
      "Promotion": {
        "type": "object",
        "required": [
          "id",
          "name",
          "url"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "examples": [
              "Счастливые часы"
            ]
          },
          "url": {
            "type": "string",
            "description": "Страница акции",
            "format": "uri"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "active_from": {
            "type": [
              "string",
              "null"
            ],
            "description": "Начало действия",
            "format": "date-time"
          },
          "active_to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Окончание действия; null — бессрочно",
            "format": "date-time"
          },
          "days_of_week": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 7
            },
            "description": "Дни недели, 1 — понедельник; null — каждый день",
            "examples": [
              [
                1,
                2,
                3,
                4,
                5
              ]
            ]
          },
          "time_from": {
            "type": [
              "string",
              "null"
            ],
            "description": "Действует с (HH:MM)",
            "examples": [
              "10:00"
            ]
          },
          "time_to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Действует до (HH:MM)",
            "examples": [
              "15:00"
            ]
          },
          "discount_percent": {
            "type": [
              "number",
              "null"
            ],
            "description": "Скидка на товары акции, %"
          },
          "pickup_discount_percent": {
            "type": [
              "number",
              "null"
            ],
            "description": "Скидка при самовывозе, %"
          },
          "pickup_only": {
            "type": "boolean",
            "description": "Только при самовывозе"
          },
          "min_basket_sum": {
            "type": [
              "number",
              "null"
            ],
            "description": "Минимальная сумма корзины, BYN"
          },
          "category_ids": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer"
            },
            "description": "Категории, на которые действует акция"
          }
        }
      }
    },
    "parameters": {
      "pretty": {
        "name": "pretty",
        "in": "query",
        "schema": {
          "type": "integer",
          "enum": [
            1
          ]
        },
        "description": "1 — JSON с отступами"
      }
    },
    "headers": {
      "ETag": {
        "schema": {
          "type": "string"
        },
        "description": "Хеш ответа для условных запросов"
      },
      "CacheControl": {
        "schema": {
          "type": "string"
        },
        "example": "public, max-age=300"
      }
    }
  }
}