{
  "openapi": "3.0.3",
  "info": {
    "title": "Pizza Fashion Partner API",
    "version": "1.2.0",
    "description": "Catalogue, authoritative pricing and inbound cash orders for separately authenticated partners. Complete guide: https://api.pizzafashion.bg/en/"
  },
  "servers": [
    {
      "url": "https://api.pizzafashion.bg/v1",
      "description": "Canonical v1"
    },
    {
      "url": "https://pizzafashion.bg/api/index.php/v1/pizzafashion/partner",
      "description": "Compatible original v1"
    }
  ],
  "paths": {
    "/catalogue": {
      "get": {
        "operationId": "getCatalogue",
        "summary": "Products, options and combos",
        "security": [
          {
            "partnerBasic": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success; data is under meta.data.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meta": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Catalogue"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "additionalProperties": true
                    },
                    "links": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "jsonapi": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "meta"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Unsupported query parameters / gateway request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, paused or rotated credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "HTTPS required; HTTP API requests are not redirected with credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No own order for this ID, or unknown route.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "Changed/expired quote, conflicting external_order_id or busy request. Reconcile status.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "415": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields/options/contact/minimum, closed period or unavailable product. Correct the request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Respect Retry-After.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "Disabled submission/API/partner or temporary unavailability. Bounded retries with the same ID.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page 1–10000. Continue until has_more=false.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          }
        ]
      }
    },
    "/availability": {
      "get": {
        "operationId": "getAvailability",
        "summary": "Zones and receipt options",
        "security": [
          {
            "partnerBasic": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success; data is under meta.data.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meta": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Availability"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "additionalProperties": true
                    },
                    "links": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "jsonapi": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "meta"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Unsupported query parameters / gateway request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, paused or rotated credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "HTTPS required; HTTP API requests are not redirected with credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No own order for this ID, or unknown route.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "Changed/expired quote, conflicting external_order_id or busy request. Reconcile status.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "415": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields/options/contact/minimum, closed period or unavailable product. Correct the request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Respect Retry-After.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "Disabled submission/API/partner or temporary unavailability. Bounded retries with the same ID.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/quote": {
      "post": {
        "operationId": "createQuote",
        "summary": "Price without creating an order",
        "security": [
          {
            "partnerBasic": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success; data is under meta.data.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meta": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/QuoteResponse"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "additionalProperties": true
                    },
                    "links": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "jsonapi": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "meta"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Unsupported query parameters / gateway request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, paused or rotated credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "HTTPS required; HTTP API requests are not redirected with credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No own order for this ID, or unknown route.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "Changed/expired quote, conflicting external_order_id or busy request. Reconcile status.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "415": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields/options/contact/minimum, closed period or unavailable product. Correct the request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Respect Retry-After.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "Disabled submission/API/partner or temporary unavailability. Bounded retries with the same ID.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/orders": {
      "post": {
        "operationId": "submitOrder",
        "summary": "Create an idempotent order",
        "security": [
          {
            "partnerBasic": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success; data is under meta.data.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meta": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/OrderStatus"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "additionalProperties": true
                    },
                    "links": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "jsonapi": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "meta"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Unsupported query parameters / gateway request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, paused or rotated credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "HTTPS required; HTTP API requests are not redirected with credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No own order for this ID, or unknown route.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "Changed/expired quote, conflicting external_order_id or busy request. Reconcile status.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "415": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields/options/contact/minimum, closed period or unavailable product. Correct the request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Respect Retry-After.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "Disabled submission/API/partner or temporary unavailability. Bounded retries with the same ID.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/orderstatus": {
      "post": {
        "operationId": "getOwnOrderStatus",
        "summary": "Check an own order",
        "security": [
          {
            "partnerBasic": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StatusRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success; data is under meta.data.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meta": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/OrderStatus"
                        }
                      },
                      "required": [
                        "data"
                      ],
                      "additionalProperties": true
                    },
                    "links": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "jsonapi": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "meta"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Unsupported query parameters / gateway request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, paused or rotated credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "HTTPS required; HTTP API requests are not redirected with credentials.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No own order for this ID, or unknown route.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "Changed/expired quote, conflicting external_order_id or busy request. Reconcile status.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "415": {
            "description": "Body exceeds 128 KiB / Content-Type: application/json required.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields/options/contact/minimum, closed period or unavailable product. Correct the request.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached. Respect Retry-After.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "Disabled submission/API/partner or temporary unavailability. Bounded retries with the same ID.",
            "content": {
              "application/vnd.api+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "partnerBasic": {
        "type": "http",
        "scheme": "basic",
        "description": "Username=API key; password=separate password. HTTPS and backend only."
      }
    },
    "schemas": {
      "RequestHeaders": {
        "type": "object",
        "description": "Request headers",
        "properties": {
          "Authorization": {
            "type": "string",
            "description": "HTTP Basic: API key is username, separate password is password. Send only to the HTTPS API host.",
            "example": "Basic <base64(API_KEY:API_PASSWORD)>"
          },
          "Accept": {
            "type": "string",
            "description": "Explicitly request the JSON API response format.",
            "example": "application/vnd.api+json"
          },
          "Content-Type": {
            "type": "string",
            "description": "For POST send a UTF-8 JSON object, not an HTML form or form-data. [post]",
            "example": "application/json"
          }
        },
        "required": [
          "Authorization",
          "Accept"
        ],
        "additionalProperties": false
      },
      "CatalogueQuery": {
        "type": "object",
        "description": "Catalogue pagination",
        "properties": {
          "page": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "default": 1,
            "description": "Integer1–10000, default1. Example: /catalogue?page=2. Other query parameters are unsupported.",
            "example": 1
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "Catalogue": {
        "type": "object",
        "description": "Catalogue response",
        "properties": {
          "schema": {
            "type": "integer",
            "minimum": 0,
            "description": "Payload schema version; currently 1.",
            "readOnly": true,
            "example": 1
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR"
            ],
            "description": "Currency of all prices.",
            "readOnly": true,
            "example": "EUR"
          },
          "timezone": {
            "type": "string",
            "description": "Timezone used for restaurant opening hours.",
            "readOnly": true,
            "example": "Europe/Sofia"
          },
          "categories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Category"
            },
            "description": "Categories; resolve them using each product’s category_ids.",
            "readOnly": true
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            },
            "description": "Up to 50 products per page, including option children.",
            "readOnly": true
          },
          "page": {
            "type": "integer",
            "minimum": 0,
            "description": "Current page number, starting at 1.",
            "readOnly": true,
            "example": 1
          },
          "page_size": {
            "type": "integer",
            "minimum": 0,
            "description": "Page size: 50.",
            "readOnly": true,
            "example": 50
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Total product records, including unlisted option children.",
            "readOnly": true,
            "example": 144
          },
          "has_more": {
            "type": "boolean",
            "description": "When true, request the next page.",
            "readOnly": true,
            "example": true
          },
          "combos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Combo"
            },
            "description": "Combo offers; merge repeated page metadata by code.",
            "readOnly": true
          },
          "pizza_bases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Base"
            },
            "description": "Global pizza bases; use products[].bases for product eligibility.",
            "readOnly": true
          },
          "half": {
            "allOf": [
              {
                "$ref": "#/components/schemas/HalfSettings"
              }
            ],
            "description": "Half-and-half configuration.",
            "readOnly": true
          },
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Generation timestamp, RFC3339.",
            "readOnly": true,
            "example": "2026-10-18T12:00:00+00:00"
          }
        },
        "required": [
          "schema",
          "currency",
          "timezone",
          "categories",
          "products",
          "page",
          "page_size",
          "total",
          "has_more",
          "combos",
          "pizza_bases",
          "half",
          "generated_at"
        ],
        "additionalProperties": true
      },
      "Category": {
        "type": "object",
        "description": "Categories",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 0,
            "description": "Stable category identifier.",
            "readOnly": true,
            "example": 7
          },
          "name": {
            "type": "string",
            "description": "Public category name.",
            "readOnly": true,
            "example": "Пици"
          },
          "icon": {
            "type": "string",
            "description": "Icon code; presentation metadata.",
            "readOnly": true,
            "example": "pizza"
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "description": "Menu product count for the category.",
            "readOnly": true,
            "example": 24
          }
        },
        "required": [
          "id",
          "name",
          "icon",
          "count"
        ],
        "additionalProperties": true
      },
      "Product": {
        "type": "object",
        "description": "Product attributes",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 0,
            "description": "Stable ID; send it as selection.product_id.",
            "readOnly": true,
            "example": 123
          },
          "name": {
            "type": "string",
            "description": "Display name; changing documentation language does not translate dish names.",
            "readOnly": true,
            "example": "Пица Чикън"
          },
          "description": {
            "type": "string",
            "description": "Plain text description without HTML.",
            "readOnly": true,
            "example": "Пилешко месо, моцарела"
          },
          "category_ids": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Category IDs containing this product.",
            "readOnly": true,
            "example": [
              7
            ]
          },
          "image": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Absolute HTTPS product image URL, or null.",
            "readOnly": true,
            "example": "https://pizzafashion.bg/images/example.webp"
          },
          "thumbnail": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Smaller catalogue image URL, or null.",
            "readOnly": true,
            "example": "https://pizzafashion.bg/images/example-thumb.webp"
          },
          "image_illustrative": {
            "type": "boolean",
            "description": "Whether the image is illustrative.",
            "readOnly": true,
            "example": false
          },
          "listed": {
            "type": "boolean",
            "description": "False: flavour/package option child; retain it but do not display a separate card.",
            "readOnly": true,
            "example": true
          },
          "minor": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Base displayed price in EUR cents. Use the selected variant price.",
            "readOnly": true,
            "example": 790
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR"
            ],
            "description": "Currency EUR.",
            "readOnly": true,
            "example": "EUR"
          },
          "available": {
            "type": "boolean",
            "description": "When false, disable ordering this product.",
            "readOnly": true,
            "example": true
          },
          "available_at": {
            "type": "string",
            "nullable": true,
            "description": "Scheduled restock label/timestamp or null; does not override current available.",
            "readOnly": true
          },
          "availability_notice": {
            "type": "string",
            "description": "Availability notice, when configured.",
            "readOnly": true,
            "example": ""
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variant"
            },
            "description": "Available sizes and their prices, ingredients and options.",
            "readOnly": true
          },
          "addons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Addon"
            },
            "description": "Allowed extras; selected-size prices are in variants[].addons.",
            "readOnly": true
          },
          "bases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Base"
            },
            "description": "Allowed bases for this pizza; empty when inapplicable.",
            "readOnly": true
          },
          "choices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChoiceGroup"
            },
            "description": "Flavour/package choices referencing a different product_id.",
            "readOnly": true
          },
          "half_eligible": {
            "type": "boolean",
            "description": "Eligible for half-and-half when enabled and a family variant exists.",
            "readOnly": true,
            "example": true
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "category_ids",
          "image",
          "thumbnail",
          "image_illustrative",
          "listed",
          "minor",
          "currency",
          "available",
          "available_at",
          "availability_notice",
          "variants",
          "addons",
          "bases",
          "choices",
          "half_eligible"
        ],
        "additionalProperties": true
      },
      "Variant": {
        "type": "object",
        "description": "Sizes / variants",
        "properties": {
          "key": {
            "type": "string",
            "enum": [
              "standard",
              "medium",
              "family"
            ],
            "description": "Key for selection.size; use only a returned variant.",
            "readOnly": true,
            "example": "medium"
          },
          "label": {
            "type": "string",
            "description": "Human-readable size label.",
            "readOnly": true,
            "example": "Средна"
          },
          "minor": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Full price for this size, not a size surcharge.",
            "readOnly": true,
            "example": 790
          },
          "weight_g": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Weight in grams, or null when unspecified.",
            "readOnly": true,
            "example": 550
          },
          "ingredients": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Ingredient"
            },
            "description": "Ingredients; only entries with removable=1 may be removed.",
            "readOnly": true
          },
          "allergens": {
            "type": "array",
            "maxItems": 14,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 14
            },
            "description": "Numeric codes 1–14; ingredient removal never guarantees allergen-free food.",
            "readOnly": true,
            "example": [
              1,
              7
            ]
          },
          "crust_selectable": {
            "type": "boolean",
            "description": "Whether crust selection is permitted.",
            "readOnly": true,
            "example": true
          },
          "crust_included": {
            "type": "boolean",
            "description": "Recipe’s included crust; use as the initial crust selection.",
            "readOnly": true,
            "example": false
          },
          "crust_extra_minor": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Surcharge for adding crust; null does not mean free.",
            "readOnly": true,
            "example": 250
          },
          "addons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Addon"
            },
            "description": "Extras priced for this exact size.",
            "readOnly": true
          }
        },
        "required": [
          "key",
          "label",
          "minor",
          "weight_g",
          "ingredients",
          "allergens",
          "crust_selectable",
          "crust_included",
          "crust_extra_minor",
          "addons"
        ],
        "additionalProperties": true
      },
      "Ingredient": {
        "type": "object",
        "description": "Ingredients",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 0,
            "description": "ID for selection.removed[] when removal is allowed.",
            "readOnly": true,
            "example": 17
          },
          "name": {
            "type": "string",
            "description": "Ingredient name.",
            "readOnly": true,
            "example": "Лук"
          },
          "removable": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "1/true permits removal; 0/false prohibits it.",
            "readOnly": true,
            "example": 1
          }
        },
        "required": [
          "id",
          "name",
          "removable"
        ],
        "additionalProperties": true
      },
      "Addon": {
        "type": "object",
        "description": "Sauces and extras",
        "properties": {
          "code": {
            "type": "string",
            "description": "Send this code in selection.addons[], without duplicates.",
            "readOnly": true,
            "example": "garlic_sauce"
          },
          "name": {
            "type": "string",
            "description": "Extra name for the selected size.",
            "readOnly": true,
            "example": "Чеснов сос"
          },
          "group": {
            "type": "string",
            "enum": [
              "sauce",
              "topping"
            ],
            "description": "Extra group: sauce or pizza topping.",
            "readOnly": true,
            "example": "sauce"
          },
          "enabled": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "Definition enabled flag; catalogue includes permitted extras.",
            "readOnly": true,
            "example": 1
          },
          "minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Price in EUR cents; variant-level value is size-specific.",
            "readOnly": true,
            "example": 100
          },
          "allergens": {
            "type": "array",
            "maxItems": 14,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 14
            },
            "description": "Allergen codes of this extra.",
            "readOnly": true,
            "example": [
              7
            ]
          },
          "reviewed": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "Whether allergen data has been reviewed.",
            "readOnly": true,
            "example": 1
          },
          "family_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional family-size price metadata.",
            "readOnly": true,
            "example": 200
          },
          "weight_g": {
            "type": "integer",
            "minimum": 0,
            "description": "Extra weight in grams when known.",
            "readOnly": true,
            "example": 50
          },
          "family_weight_g": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional family-size weight.",
            "readOnly": true,
            "example": 100
          },
          "family_name": {
            "type": "string",
            "description": "Optional family-size display label.",
            "readOnly": true,
            "example": "Добавка — 100 г"
          }
        },
        "required": [
          "code",
          "name",
          "group",
          "enabled",
          "minor",
          "allergens",
          "reviewed"
        ],
        "additionalProperties": true
      },
      "Base": {
        "type": "object",
        "description": "Pizza bases",
        "properties": {
          "code": {
            "type": "string",
            "description": "Code for selection.base; this is not a paid extra.",
            "readOnly": true,
            "example": "tomato"
          },
          "name": {
            "type": "string",
            "description": "Display label.",
            "readOnly": true,
            "example": "Доматено пюре"
          },
          "enabled": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "Definition flag; may be absent in normalized quote bases.",
            "readOnly": true,
            "example": 1
          },
          "default": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "Default base, used when base is omitted.",
            "readOnly": true,
            "example": 1
          },
          "minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Bases are included; value is 0.",
            "readOnly": true,
            "example": 0
          },
          "allergens": {
            "type": "array",
            "maxItems": 14,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 14
            },
            "description": "Base allergen codes.",
            "readOnly": true
          },
          "reviewed": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "Whether allergen data is reviewed.",
            "readOnly": true,
            "example": 1
          }
        },
        "required": [
          "code",
          "name",
          "default",
          "minor",
          "allergens",
          "reviewed"
        ],
        "additionalProperties": true
      },
      "ChoiceGroup": {
        "type": "object",
        "description": "Flavour / package choices",
        "properties": {
          "label": {
            "type": "string",
            "description": "Choice group heading.",
            "readOnly": true,
            "example": "Избери вкус"
          },
          "show_price": {
            "type": "boolean",
            "description": "Whether to display the price beside the option.",
            "readOnly": true,
            "example": true
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChoiceOption"
            },
            "description": "Alternatives, each using a separate product_id.",
            "readOnly": true
          }
        },
        "required": [
          "label",
          "show_price",
          "options"
        ],
        "additionalProperties": true
      },
      "ChoiceOption": {
        "type": "object",
        "description": "Choice option",
        "properties": {
          "product_id": {
            "type": "integer",
            "minimum": 0,
            "description": "After choosing, resolve this product and its own variants/extras.",
            "readOnly": true,
            "example": 124
          },
          "label": {
            "type": "string",
            "description": "Option label.",
            "readOnly": true,
            "example": "Без захар"
          },
          "minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Standalone price of the target product.",
            "readOnly": true,
            "example": 200
          },
          "current": {
            "type": "boolean",
            "description": "Whether this option represents the current product.",
            "readOnly": true,
            "example": false
          }
        },
        "required": [
          "product_id",
          "label",
          "minor",
          "current"
        ],
        "additionalProperties": true
      },
      "Combo": {
        "type": "object",
        "description": "Combo offers",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable code for selection.combo_code.",
            "readOnly": true,
            "example": "combo-example"
          },
          "revision": {
            "type": "string",
            "description": "Copy exactly into combo_revision; never invent a revision.",
            "readOnly": true,
            "example": "<revision-from-catalogue>"
          },
          "name": {
            "type": "string",
            "description": "Public name.",
            "readOnly": true,
            "example": "Комбо предложение"
          },
          "description": {
            "type": "string",
            "description": "Short description.",
            "readOnly": true,
            "example": "Пица и напитка"
          },
          "image": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "HTTPS image URL or null.",
            "readOnly": true
          },
          "minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Default configuration price; options may add a surcharge.",
            "readOnly": true,
            "example": 1490
          },
          "available": {
            "type": "boolean",
            "description": "Current availability; server rechecks it on submission.",
            "readOnly": true,
            "example": true
          },
          "schedule": {
            "type": "string",
            "description": "Human-readable schedule, not authoritative ordering permission.",
            "readOnly": true,
            "example": "Всеки ден · през работното време"
          },
          "slots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComboSlot"
            },
            "description": "Ordered slots; preserve their count and order.",
            "readOnly": true
          },
          "default_selection": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Selection"
              }
            ],
            "description": "Ready-to-use request selection; change only permitted options.",
            "readOnly": true
          }
        },
        "required": [
          "code",
          "revision",
          "name",
          "description",
          "image",
          "minor",
          "available",
          "schedule",
          "slots",
          "default_selection"
        ],
        "additionalProperties": true
      },
      "ComboSlot": {
        "type": "object",
        "description": "Combo slots",
        "properties": {
          "product_ids": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Allowed products for this slot; do not substitute other IDs.",
            "readOnly": true,
            "example": [
              123,
              124
            ]
          },
          "size": {
            "type": "string",
            "enum": [
              "standard",
              "medium",
              "family"
            ],
            "description": "Fixed slot size; cannot be changed.",
            "readOnly": true,
            "example": "medium"
          }
        },
        "required": [
          "product_ids",
          "size"
        ],
        "additionalProperties": true
      },
      "HalfSettings": {
        "type": "object",
        "description": "Half-and-half settings",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "When false, do not offer the half-and-half builder.",
            "readOnly": true,
            "example": true
          },
          "size": {
            "type": "string",
            "enum": [
              "family"
            ],
            "description": "Half-and-half is available only in family size.",
            "readOnly": true,
            "example": "family"
          },
          "price_mode": {
            "type": "string",
            "enum": [
              "higher",
              "average",
              "fixed",
              "unset"
            ],
            "description": "Pricing mode; always obtain the final amount from /quote.",
            "readOnly": true,
            "example": "higher"
          },
          "fixed_minor": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Fixed price for fixed mode; null does not mean zero.",
            "readOnly": true
          }
        },
        "required": [
          "enabled",
          "size",
          "price_mode",
          "fixed_minor"
        ],
        "additionalProperties": true
      },
      "Availability": {
        "type": "object",
        "description": "Receipt and opening hours",
        "properties": {
          "currency": {
            "type": "string",
            "enum": [
              "EUR"
            ],
            "description": "Currency of monetary values.",
            "readOnly": true,
            "example": "EUR"
          },
          "timezone": {
            "type": "string",
            "description": "Local timezone used for schedules.",
            "readOnly": true,
            "example": "Europe/Sofia"
          },
          "payment_methods": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "This version returns cash only.",
            "readOnly": true,
            "example": [
              "cash"
            ]
          },
          "ordering_enabled": {
            "type": "boolean",
            "description": "Global/partner submission switch; does not guarantee a zone is open now.",
            "readOnly": true,
            "example": true
          },
          "zones": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Zone"
            },
            "description": "Delivery zones. Numeric database fields may be numbers or numeric strings.",
            "readOnly": true
          },
          "pickup": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Pickup"
              }
            ],
            "description": "Restaurant collection information.",
            "readOnly": true
          },
          "notice": {
            "type": "string",
            "description": "Reminder to use /quote for authoritative rules.",
            "readOnly": true,
            "example": "Use POST /quote for authoritative current eligibility, fees and minimums."
          }
        },
        "required": [
          "currency",
          "timezone",
          "payment_methods",
          "ordering_enabled",
          "zones",
          "pickup",
          "notice"
        ],
        "additionalProperties": true
      },
      "Zone": {
        "type": "object",
        "description": "Delivery zones",
        "properties": {
          "id": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "pattern": "^[0-9]+$"
              }
            ],
            "description": "ID for choice.zone_id; send it as an integer.",
            "readOnly": true,
            "example": 1
          },
          "name": {
            "type": "string",
            "description": "Delivery area name.",
            "readOnly": true,
            "example": "Велинград"
          },
          "enabled": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "pattern": "^[0-9]+$"
              }
            ],
            "description": "1 means enabled; 0 means disabled.",
            "readOnly": true,
            "example": 1
          },
          "days_mask": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "string",
                "pattern": "^[0-9]+$"
              }
            ],
            "description": "Weekday bitmask: Monday=1, Tuesday=2, … Sunday=64; 127=every day.",
            "readOnly": true,
            "example": 127
          },
          "acceptance_opens": {
            "type": "string",
            "description": "Ordinary opening time, HH:MM, Europe/Sofia.",
            "readOnly": true,
            "example": "09:00"
          },
          "acceptance_cutoff": {
            "type": "string",
            "description": "Ordinary cutoff; late/holiday rules are enforced by /quote.",
            "readOnly": true,
            "example": "22:00"
          },
          "delivery_fee_minor": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0,
                "nullable": true
              },
              {
                "type": "string",
                "pattern": "^[0-9]+$"
              }
            ],
            "description": "Ordinary delivery fee or null; use /quote for the exact fee.",
            "readOnly": true,
            "example": 0
          },
          "minimum_order_minor": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0,
                "nullable": true
              },
              {
                "type": "string",
                "pattern": "^[0-9]+$"
              }
            ],
            "description": "Ordinary minimum or null when not configured.",
            "readOnly": true,
            "example": 0
          },
          "free_delivery_over_minor": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0,
                "nullable": true
              },
              {
                "type": "string",
                "pattern": "^[0-9]+$"
              }
            ],
            "description": "Free-delivery threshold or null if none.",
            "readOnly": true
          }
        },
        "required": [
          "id",
          "name",
          "enabled",
          "days_mask",
          "acceptance_opens",
          "acceptance_cutoff",
          "delivery_fee_minor",
          "minimum_order_minor",
          "free_delivery_over_minor"
        ],
        "additionalProperties": true
      },
      "Pickup": {
        "type": "object",
        "description": "Restaurant collection",
        "properties": {
          "location_id": {
            "type": "string",
            "enum": [
              "main"
            ],
            "description": "Use main in choice.location_id.",
            "readOnly": true,
            "example": "main"
          },
          "name": {
            "type": "string",
            "description": "Restaurant name.",
            "readOnly": true,
            "example": "Pizza Fashion"
          },
          "address": {
            "type": "string",
            "description": "Collection address.",
            "readOnly": true,
            "example": "Велинград, адрес на обекта"
          },
          "opens": {
            "type": "string",
            "description": "Ordinary opening time.",
            "readOnly": true,
            "example": "09:00"
          },
          "cutoff": {
            "type": "string",
            "description": "Ordinary cutoff time.",
            "readOnly": true,
            "example": "22:00"
          },
          "days": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                }
              ]
            },
            "description": "ISO weekdays: 1=Monday, 7=Sunday; values may be strings.",
            "readOnly": true,
            "example": [
              1,
              2,
              3,
              4,
              5,
              6,
              7
            ]
          }
        },
        "required": [
          "location_id",
          "name",
          "address",
          "opens",
          "cutoff",
          "days"
        ],
        "additionalProperties": true
      },
      "OrderRequest": {
        "type": "object",
        "description": "Import an order into Pizza Fashion",
        "properties": {
          "external_order_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
            "description": "Your permanent unique ID: 1–80 ASCII letters/digits and _ . : -; starts alphanumeric. Reuse on retries.",
            "example": "ONEBITE-2026-10001"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Line"
            },
            "description": "1–40 lines; identical to the matching signed quote request.",
            "minItems": 1,
            "maxItems": 40
          },
          "choice": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ReceiptChoice"
              }
            ],
            "description": "Receipt/payment choice; identical to the matching /quote request."
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Customer"
              }
            ],
            "description": "Fulfilment contacts for this order; no Club account is created."
          },
          "expected_total_minor": {
            "type": "integer",
            "minimum": 1,
            "description": "Exact quote.total_minor as a JSON integer; not a price override.",
            "example": 790
          },
          "quote_token": {
            "type": "string",
            "description": "Unchanged /quote token; first submission must be within 15 minutes.",
            "example": "<quote_token-from-matching-quote>"
          }
        },
        "required": [
          "external_order_id",
          "items",
          "choice",
          "customer",
          "expected_total_minor",
          "quote_token"
        ],
        "additionalProperties": false
      },
      "QuoteRequest": {
        "type": "object",
        "description": "Authoritative quote request",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Line"
            },
            "description": "1–40 configured lines; never send item prices.",
            "minItems": 1,
            "maxItems": 40
          },
          "choice": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ReceiptChoice"
              }
            ],
            "description": "Receipt method and cash payment."
          }
        },
        "required": [
          "items",
          "choice"
        ],
        "additionalProperties": false
      },
      "Line": {
        "type": "object",
        "description": "Order line",
        "properties": {
          "selection": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Selection"
              }
            ],
            "description": "Product, half-and-half or combo configuration."
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 1,
            "description": "Quantity 1–20; defaults to 1.",
            "example": 1
          }
        },
        "required": [
          "selection"
        ],
        "additionalProperties": false
      },
      "Selection": {
        "type": "object",
        "description": "Product configuration",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "product",
              "half",
              "combo"
            ],
            "description": "Selection kind; defaults to product. Set explicitly for half and combo.",
            "example": "product"
          },
          "product_id": {
            "type": "integer",
            "minimum": 1,
            "description": "Catalogue ID; for a normal product or nested half/combo slot. [product]",
            "example": 123
          },
          "size": {
            "type": "string",
            "enum": [
              "standard",
              "medium",
              "family"
            ],
            "description": "Returned variants[].key. Preserve combo slot size; half-and-half uses family. [product]",
            "example": "medium"
          },
          "removed": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Up to 100 distinct removable ingredient IDs; defaults to [].",
            "example": [
              17
            ],
            "maxItems": 100,
            "uniqueItems": true,
            "default": []
          },
          "crust": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "Crust selection: 0/1 or false/true. Initialize from crust_included; change only when selectable.",
            "example": 0
          },
          "addons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Up to 40 distinct codes allowed for the product/size; not on the outer half-and-half selection.",
            "example": [
              "garlic_sauce"
            ],
            "maxItems": 40,
            "uniqueItems": true,
            "default": []
          },
          "base": {
            "type": "string",
            "description": "One product.bases code; omission uses default. For half-and-half put it on the outer selection.",
            "example": "tomato"
          },
          "halves": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Selection"
            },
            "description": "Exactly two halves with product_id, size=family and removed. Each half follows included crust; extra selection is unsupported here. [half]",
            "minItems": 2,
            "maxItems": 2
          },
          "combo_code": {
            "type": "string",
            "description": "Exact combos[].code. [combo]",
            "example": "combo-example"
          },
          "combo_revision": {
            "type": "string",
            "description": "Send the returned revision to detect a changed definition/price. [combo]",
            "example": "<revision-from-catalogue>"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Selection"
            },
            "description": "Ordered default_selection.items, one per slot. No nested quantity field. [combo]",
            "minItems": 2,
            "maxItems": 12
          }
        },
        "required": [],
        "additionalProperties": false
      },
      "ReceiptChoice": {
        "type": "object",
        "description": "Delivery, collection and payment",
        "properties": {
          "fulfilment": {
            "type": "string",
            "enum": [
              "delivery",
              "pickup"
            ],
            "description": "Send delivery or pickup explicitly.",
            "example": "delivery"
          },
          "zone_id": {
            "type": "integer",
            "minimum": 1,
            "description": "Returned /availability.zones ID for delivery. [delivery]",
            "example": 1
          },
          "location_id": {
            "type": "string",
            "enum": [
              "main"
            ],
            "description": "Use main for pickup; never change fulfilment to bypass delivery rules. [pickup]",
            "example": "main"
          },
          "payment": {
            "type": "string",
            "enum": [
              "cash"
            ],
            "description": "Cash on delivery/collection only; no online charge or payment-processing fee.",
            "example": "cash"
          }
        },
        "required": [
          "fulfilment",
          "payment"
        ],
        "additionalProperties": false
      },
      "Customer": {
        "type": "object",
        "description": "Customer fulfilment contacts",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "Real customer name, up to 100 characters, plain text.",
            "example": "Име на клиента"
          },
          "phone": {
            "type": "string",
            "maxLength": 80,
            "description": "Complete valid phone, preferably E.164. Bulgarian mobile: 10 domestic digits with leading0 or +359 and 9digits; incomplete numbers are rejected.",
            "example": "0888123456"
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 190,
            "description": "Valid email; required when configured by the restaurant. Send the real address when available. [email]",
            "example": "customer@example.com"
          },
          "address": {
            "type": "string",
            "maxLength": 240,
            "description": "For delivery: street, number and needed details, up to 240 characters. Unused for pickup. [delivery]",
            "example": "Улица Примерна 10, вход А"
          },
          "note": {
            "type": "string",
            "maxLength": 500,
            "description": "Note up to 500 characters; restaurant can disable it. No HTML or line breaks.",
            "example": "Звънец 3"
          }
        },
        "required": [
          "name",
          "phone"
        ],
        "additionalProperties": false
      },
      "StatusRequest": {
        "type": "object",
        "description": "Own-order status request",
        "properties": {
          "external_order_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
            "description": "The same ID this partner used when submitting the order.",
            "example": "ONEBITE-2026-10001"
          }
        },
        "required": [
          "external_order_id"
        ],
        "additionalProperties": false
      },
      "OrderStatus": {
        "type": "object",
        "description": "Order / status response",
        "properties": {
          "external_order_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$",
            "description": "Your order ID.",
            "readOnly": true,
            "example": "ONEBITE-2026-10001"
          },
          "order_number": {
            "type": "string",
            "description": "Reference generated by Pizza Fashion; do not use it as your external ID.",
            "readOnly": true,
            "example": "PF-A2000"
          },
          "status": {
            "type": "string",
            "enum": [
              "accepted",
              "preparing",
              "sent",
              "delivered",
              "cancelled",
              "payment_review"
            ],
            "description": "Machine-readable state; meanings are listed separately.",
            "readOnly": true,
            "example": "accepted"
          },
          "status_label": {
            "type": "string",
            "description": "Restaurant label; may remain Bulgarian regardless of documentation language.",
            "readOnly": true,
            "example": "Приета"
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR"
            ],
            "description": "Order currency.",
            "readOnly": true,
            "example": "EUR"
          },
          "total_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Saved final amount including delivery.",
            "readOnly": true,
            "example": 790
          },
          "payment": {
            "type": "string",
            "enum": [
              "cash"
            ],
            "description": "Payment method is cash.",
            "readOnly": true,
            "example": "cash"
          },
          "paid": {
            "type": "boolean",
            "description": "Recorded payment flag. False is normal for a newly created cash order.",
            "readOnly": true,
            "example": false
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "UTC creation timestamp.",
            "readOnly": true,
            "example": "2026-10-18T12:00:00Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "UTC last-update timestamp.",
            "readOnly": true,
            "example": "2026-10-18T12:05:00Z"
          },
          "replayed": {
            "type": "boolean",
            "description": "Only on /orders: true means an identical replay, not another order.",
            "readOnly": true,
            "example": false
          }
        },
        "required": [
          "external_order_id",
          "order_number",
          "status",
          "status_label",
          "currency",
          "total_minor",
          "payment",
          "paid",
          "created_at",
          "updated_at"
        ],
        "additionalProperties": true
      },
      "QuoteResponse": {
        "type": "object",
        "description": "Quote response",
        "properties": {
          "quote": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Quote"
              }
            ],
            "description": "Authoritative pricing breakdown; do not POST this whole object as an order.",
            "readOnly": true
          },
          "quote_token": {
            "type": "string",
            "description": "Retain and send with the original items/choice to /orders.",
            "readOnly": true,
            "example": "<signed-token>"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "First-submission deadline, approximately 15 minutes.",
            "readOnly": true,
            "example": "2026-10-18T12:15:00+00:00"
          }
        },
        "required": [
          "quote",
          "quote_token",
          "expires_at"
        ],
        "additionalProperties": true
      },
      "Quote": {
        "type": "object",
        "description": "Totals and receipt rules",
        "properties": {
          "schema": {
            "type": "integer",
            "minimum": 0,
            "description": "Pricing payload schema version.",
            "readOnly": true,
            "example": 1
          },
          "test_only": {
            "type": "boolean",
            "description": "True prevents operational partner submission.",
            "readOnly": true,
            "example": false
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR"
            ],
            "description": "All amounts are EUR cents.",
            "readOnly": true,
            "example": "EUR"
          },
          "choice": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuoteChoice"
              }
            ],
            "description": "Normalized display choice; keep the original request choice for /orders.",
            "readOnly": true
          },
          "location": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuoteLocation"
              }
            ],
            "description": "Delivery zone or collection location.",
            "readOnly": true
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuotedLine"
            },
            "description": "Configured lines and pricing breakdowns.",
            "readOnly": true
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "description": "Sum of quantities, not number of lines.",
            "readOnly": true,
            "example": 2
          },
          "subtotal_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Product subtotal before order-level discounts and delivery.",
            "readOnly": true,
            "example": 1580
          },
          "delivery_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Authoritative receipt/delivery fee.",
            "readOnly": true,
            "example": 0
          },
          "total_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Final display amount; use as expected_total_minor.",
            "readOnly": true,
            "example": 1580
          },
          "minimum_minor": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Applicable minimum-order amount.",
            "readOnly": true,
            "example": 0
          },
          "minimum_exclusive": {
            "type": "boolean",
            "description": "True means the amount must be strictly above the minimum.",
            "readOnly": true,
            "example": false
          },
          "minimum_includes_delivery": {
            "type": "boolean",
            "description": "Whether the minimum includes delivery.",
            "readOnly": true,
            "example": false
          },
          "period": {
            "type": "string",
            "enum": [
              "regular",
              "late",
              "pickup"
            ],
            "description": "Applied receipt period.",
            "readOnly": true,
            "example": "pickup"
          },
          "schedule_ready": {
            "type": "boolean",
            "description": "Whether current rules permit operational submission.",
            "readOnly": true,
            "example": true
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Warnings intended for display.",
            "readOnly": true
          },
          "discount_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Order promotion discount; partner API accepts no promo code, normally 0.",
            "readOnly": true,
            "example": 0
          },
          "discount_code": {
            "type": "string",
            "description": "Promotion code; empty for this API.",
            "readOnly": true,
            "example": ""
          },
          "online_discount_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Online payment discount; 0 for cash.",
            "readOnly": true,
            "example": 0
          },
          "online_discount_basis_points": {
            "type": "integer",
            "minimum": 0,
            "description": "Online-discount basis points; 0 for cash.",
            "readOnly": true,
            "example": 0
          },
          "delivery_net_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Delivery amount excluding included VAT.",
            "readOnly": true,
            "example": 0
          },
          "delivery_tax_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Included VAT on delivery.",
            "readOnly": true,
            "example": 0
          },
          "net_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Total net amount; do not add it again to total.",
            "readOnly": true,
            "example": 1317
          },
          "tax_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Total included VAT, not an additional charge.",
            "readOnly": true,
            "example": 263
          },
          "vat_percent": {
            "type": "integer",
            "minimum": 0,
            "description": "VAT percentage from restaurant configuration, not a fixed API policy.",
            "readOnly": true,
            "example": 20
          }
        },
        "required": [
          "schema",
          "test_only",
          "currency",
          "choice",
          "location",
          "items",
          "count",
          "subtotal_minor",
          "delivery_minor",
          "total_minor",
          "minimum_minor",
          "minimum_exclusive",
          "minimum_includes_delivery",
          "period",
          "schedule_ready",
          "warnings",
          "discount_minor",
          "discount_code",
          "online_discount_minor",
          "online_discount_basis_points",
          "delivery_net_minor",
          "delivery_tax_minor",
          "net_minor",
          "tax_minor",
          "vat_percent"
        ],
        "additionalProperties": true
      },
      "QuoteChoice": {
        "type": "object",
        "description": "Normalized quote choice",
        "properties": {
          "fulfilment": {
            "type": "string",
            "enum": [
              "delivery",
              "pickup"
            ],
            "description": "Validated receipt method.",
            "readOnly": true,
            "example": "pickup"
          },
          "zone_id": {
            "type": "integer",
            "minimum": 0,
            "description": "Delivery zone; 0 for pickup.",
            "readOnly": true,
            "example": 0
          },
          "location_id": {
            "type": "string",
            "description": "Main for pickup; empty string for delivery.",
            "readOnly": true,
            "example": "main"
          },
          "payment": {
            "type": "string",
            "enum": [
              "cash"
            ],
            "description": "Validated cash payment method.",
            "readOnly": true,
            "example": "cash"
          }
        },
        "required": [
          "fulfilment",
          "zone_id",
          "location_id",
          "payment"
        ],
        "additionalProperties": true
      },
      "QuoteLocation": {
        "type": "object",
        "description": "Quoted receipt location",
        "properties": {
          "id": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1
              },
              {
                "type": "string",
                "enum": [
                  "main"
                ]
              }
            ],
            "description": "Numeric zone ID or main for pickup.",
            "readOnly": true,
            "example": "main"
          },
          "name": {
            "type": "string",
            "description": "Location/zone name.",
            "readOnly": true,
            "example": "Pizza Fashion"
          },
          "address": {
            "type": "string",
            "description": "Collection address for pickup; empty for a delivery zone.",
            "readOnly": true,
            "example": "Велинград, адрес на обекта"
          },
          "opens": {
            "type": "string",
            "description": "Ordinary opening time.",
            "readOnly": true,
            "example": "09:00"
          },
          "cutoff": {
            "type": "string",
            "description": "Ordinary cutoff.",
            "readOnly": true,
            "example": "22:00"
          }
        },
        "required": [
          "id",
          "name",
          "address",
          "opens",
          "cutoff"
        ],
        "additionalProperties": true
      },
      "QuotedLine": {
        "type": "object",
        "description": "Priced line",
        "properties": {
          "key": {
            "type": "string",
            "description": "Server line key; not a product_id and not a request field.",
            "readOnly": true,
            "example": "<server-line-key>"
          },
          "name": {
            "type": "string",
            "description": "Configured display name.",
            "readOnly": true,
            "example": "Пица Чикън — Средна"
          },
          "quantity": {
            "type": "integer",
            "minimum": 0,
            "description": "Quantity.",
            "readOnly": true,
            "example": 1
          },
          "unit_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Price per configured unit.",
            "readOnly": true,
            "example": 790
          },
          "subtotal_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Unit price × quantity before order-level discounts.",
            "readOnly": true,
            "example": 790
          },
          "configuration": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Configuration"
              }
            ],
            "description": "Full calculated configuration, read-only.",
            "readOnly": true
          },
          "discount_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Allocated line discount.",
            "readOnly": true,
            "example": 0
          },
          "gross_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Discounted line amount including VAT.",
            "readOnly": true,
            "example": 790
          },
          "net_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Line amount excluding included VAT.",
            "readOnly": true,
            "example": 658
          },
          "tax_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Included line VAT.",
            "readOnly": true,
            "example": 132
          }
        },
        "required": [
          "key",
          "name",
          "quantity",
          "unit_minor",
          "subtotal_minor",
          "configuration",
          "discount_minor",
          "gross_minor",
          "net_minor",
          "tax_minor"
        ],
        "additionalProperties": true
      },
      "Configuration": {
        "type": "object",
        "description": "Calculated product / combo",
        "properties": {
          "schema": {
            "type": "integer",
            "minimum": 0,
            "description": "Configuration schema version.",
            "readOnly": true,
            "example": 1
          },
          "type": {
            "type": "string",
            "enum": [
              "product",
              "half",
              "combo"
            ],
            "description": "Configured line kind.",
            "readOnly": true,
            "example": "product"
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "readOnly": true,
            "example": "Пица Чикън — Средна"
          },
          "unit_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Final configured unit price.",
            "readOnly": true,
            "example": 790
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR"
            ],
            "description": "Currency EUR.",
            "readOnly": true,
            "example": "EUR"
          },
          "weight_g": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Total weight when known.",
            "readOnly": true,
            "example": 550
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfiguredPart"
            },
            "description": "One product, two halves or combo parts.",
            "readOnly": true
          },
          "allergens": {
            "type": "array",
            "maxItems": 14,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 14
            },
            "description": "Combined allergen codes.",
            "readOnly": true,
            "example": [
              1,
              7
            ]
          },
          "base": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Base"
              }
            ],
            "description": "Normalized selected base when applicable.",
            "readOnly": true
          },
          "combo_code": {
            "type": "string",
            "description": "Combo code.",
            "readOnly": true,
            "example": "combo-example"
          },
          "combo_revision": {
            "type": "string",
            "description": "Applied combo revision.",
            "readOnly": true,
            "example": "<revision-from-catalogue>"
          },
          "regular_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Equivalent standalone total.",
            "readOnly": true,
            "example": 1800
          },
          "combo_savings_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Savings from the combo price.",
            "readOnly": true,
            "example": 270
          },
          "extras_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Selected option surcharges.",
            "readOnly": true,
            "example": 0
          },
          "selection": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Selection"
              }
            ],
            "description": "Normalized combo selection; retain the original request for token matching.",
            "readOnly": true
          }
        },
        "required": [
          "schema",
          "type",
          "name",
          "unit_minor",
          "currency",
          "weight_g",
          "parts",
          "allergens"
        ],
        "additionalProperties": true
      },
      "ConfiguredPart": {
        "type": "object",
        "description": "Calculated product part",
        "properties": {
          "product_id": {
            "type": "integer",
            "minimum": 0,
            "description": "Product ID.",
            "readOnly": true,
            "example": 123
          },
          "name": {
            "type": "string",
            "description": "Product/part name.",
            "readOnly": true,
            "example": "Пица Чикън"
          },
          "size": {
            "type": "string",
            "enum": [
              "standard",
              "medium",
              "family"
            ],
            "description": "Selected size.",
            "readOnly": true,
            "example": "medium"
          },
          "recipe_revision": {
            "type": "integer",
            "minimum": 0,
            "description": "Recipe revision, read-only.",
            "readOnly": true,
            "example": 3
          },
          "weight_g": {
            "type": "integer",
            "minimum": 0,
            "nullable": true,
            "description": "Weight in grams or null.",
            "readOnly": true,
            "example": 550
          },
          "portion": {
            "type": "string",
            "description": "Configured serving label, when present.",
            "readOnly": true,
            "example": ""
          },
          "ingredients": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Ingredient"
            },
            "description": "Recipe ingredient list.",
            "readOnly": true
          },
          "omitted": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OmittedIngredient"
            },
            "description": "Removed ingredients.",
            "readOnly": true
          },
          "allergens": {
            "type": "array",
            "maxItems": 14,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 14
            },
            "description": "Allergen codes; removing an ingredient does not automatically remove them.",
            "readOnly": true,
            "example": [
              1,
              7
            ]
          },
          "allergens_reviewed": {
            "type": "boolean",
            "description": "Whether allergen data is reviewed.",
            "readOnly": true,
            "example": true
          },
          "composition_complete": {
            "type": "boolean",
            "description": "Whether recorded composition is complete.",
            "readOnly": true,
            "example": true
          },
          "crust": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "integer",
                "enum": [
                  0,
                  1
                ]
              }
            ],
            "description": "Effective crust selection.",
            "readOnly": true,
            "example": 0
          },
          "crust_included": {
            "type": "boolean",
            "description": "Whether the recipe includes crust.",
            "readOnly": true,
            "example": false
          },
          "crust_extra_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Actual applied crust surcharge.",
            "readOnly": true,
            "example": 0
          },
          "base_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Base product/size price.",
            "readOnly": true,
            "example": 790
          },
          "unit_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Part unit price. Do not independently sum half prices for a half-and-half order.",
            "readOnly": true,
            "example": 790
          },
          "half_eligible": {
            "type": "boolean",
            "description": "Half-and-half eligibility.",
            "readOnly": true,
            "example": true
          },
          "addons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Addon"
            },
            "description": "Selected product/combo-part extras; absent on half parts.",
            "readOnly": true
          },
          "addons_extra_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Total extra surcharge.",
            "readOnly": true,
            "example": 0
          },
          "size_extra_minor": {
            "type": "integer",
            "minimum": 0,
            "description": "Informational size surcharge, already included in the price.",
            "readOnly": true,
            "example": 0
          }
        },
        "required": [
          "product_id",
          "name",
          "size",
          "recipe_revision",
          "weight_g",
          "portion",
          "ingredients",
          "omitted",
          "allergens",
          "allergens_reviewed",
          "composition_complete",
          "crust",
          "crust_included",
          "crust_extra_minor",
          "base_minor",
          "unit_minor",
          "half_eligible"
        ],
        "additionalProperties": true
      },
      "OmittedIngredient": {
        "type": "object",
        "description": "Removed ingredient",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 0,
            "description": "Removed ingredient ID.",
            "readOnly": true,
            "example": 17
          },
          "name": {
            "type": "string",
            "description": "Removed ingredient name.",
            "readOnly": true,
            "example": "Лук"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "additionalProperties": true
      },
      "ApiError": {
        "type": "object",
        "description": "API controller error",
        "properties": {
          "status": {
            "type": "integer",
            "minimum": 0,
            "description": "HTTP error status.",
            "readOnly": true,
            "example": 422
          },
          "message": {
            "type": "string",
            "description": "Explanation; some business messages are Bulgarian. Branch on status, not exact text.",
            "readOnly": true,
            "example": "Invalid request"
          }
        },
        "required": [
          "status",
          "message"
        ],
        "additionalProperties": true
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "meta": {
            "type": "object",
            "properties": {
              "error": {
                "$ref": "#/components/schemas/ApiError"
              }
            },
            "required": [
              "error"
            ],
            "additionalProperties": true
          }
        },
        "required": [
          "meta"
        ],
        "additionalProperties": true
      }
    }
  }
}
