{
  "openapi": "3.1.0",
  "info": {
    "title": "Orbis API de integrações",
    "version": "1.0.0",
    "description": "Contrato externo publicado. Revisto em 2026-11-09. Autenticação servidor a servidor; empresa e unidade derivadas da chave. Não enviar campos extra, preços ou totais. Não autoriza APIs internas. Guia: https://docs.orbisgroup.pt/. Exemplos e regras de meio a meio no guia. Criações em produção geram registos reais."
  },
  "servers": [
    {
      "url": "https://api.orbisgroup.pt/integrations/v1",
      "description": "Produção; a Demo não é um sandbox desta API"
    }
  ],
  "security": [
    {
      "IntegrationKey": []
    }
  ],
  "tags": [
    {
      "name": "Catálogo"
    },
    {
      "name": "Pedidos"
    },
    {
      "name": "Reservas"
    }
  ],
  "paths": {
    "/catalog": {
      "get": {
        "operationId": "getCatalog",
        "summary": "Consultar o catálogo da unidade",
        "tags": [
          "Catálogo"
        ],
        "description": "Exige a permissão catalog:read. No máximo 60 consultas por minuto, rota e IP, sujeito aos limites gerais. Resposta com Cache-Control: no-store.",
        "x-orbis-scope": "catalog:read",
        "responses": {
          "200": {
            "description": "Objeto JSON direto, sem envelope data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Catalog"
                },
                "example": {
                  "restaurant": {
                    "id": "11111111-1111-4111-8111-111111111111",
                    "name": "Restaurante de exemplo",
                    "slug": "restaurante-exemplo",
                    "address": null,
                    "postal_code": null,
                    "city": null,
                    "phone": null,
                    "timezone": "Europe/Lisbon",
                    "opening_hours": null,
                    "currency": "EUR"
                  },
                  "categorias": [
                    {
                      "categoria": "Pizzas",
                      "ordem": 0,
                      "itens": [
                        {
                          "id": "22222222-2222-4222-8222-222222222222",
                          "nome": "Pizza A",
                          "descricao": "Descrição de exemplo",
                          "preco": 9.9,
                          "alergenios": [
                            "Glúten",
                            "Leite"
                          ],
                          "disponivel": true,
                          "fotos": [],
                          "variantes": [
                            {
                              "id": "33333333-3333-4333-8333-333333333333",
                              "nome": "25 cm",
                              "preco": 9.9,
                              "disponivel": true,
                              "alergenios": [
                                "Glúten",
                                "Leite"
                              ],
                              "chave_tamanho": "25cm",
                              "preco_meio_a_meio": null
                            }
                          ],
                          "meio_a_meio": {
                            "grupo": "pizzas",
                            "regra_preco": "highest"
                          }
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Formato ou escolha inválida. validation_error inclui issues; integration_request_invalid cobre escolhas de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Permissão, unidade, criador ou estado da conta não autoriza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite por rota e IP excedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Quando presente, aguardar este número de segundos.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Erro interno. Não confirmar sucesso; conservar o request_id para repetição segura.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders": {
      "post": {
        "operationId": "createOrder",
        "summary": "Criar pedido de levantamento ou consumo no local",
        "tags": [
          "Pedidos"
        ],
        "description": "Exige a permissão orders:write. No máximo 10 criações por minuto, rota e IP, sujeito aos limites gerais. Resposta com Cache-Control: no-store. Não inicia pagamento ou entrega. variant_id é obrigatório em produtos com opções; em dine_in, table_id é obrigatório. Meio a meio exige dois produtos distintos e a primeira metade deve coincidir com a linha. request_id é idempotente por credencial e operação; repetir corpo idêntico devolve 201.",
        "x-orbis-scope": "orders:write",
        "responses": {
          "201": {
            "description": "Objeto JSON direto, sem envelope data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "77777777-7777-4777-8777-777777777777",
                  "order_number": "1042",
                  "status": "placed",
                  "type": "takeaway",
                  "subtotal": 9.9,
                  "total": 9.9,
                  "created_at": "2026-12-01T19:00:00Z",
                  "items": [
                    {
                      "id": "88888888-8888-4888-8888-888888888888",
                      "product_id": "22222222-2222-4222-8222-222222222222",
                      "variant_id": "33333333-3333-4333-8333-333333333333",
                      "product_name": "Pizza A",
                      "variant_name": "25 cm",
                      "composition": null,
                      "quantity": 1,
                      "unit_price": 9.9,
                      "total": 9.9,
                      "note": "Nota opcional do item",
                      "status": "pending"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Formato ou escolha inválida. validation_error inclui issues; integration_request_invalid cobre escolhas de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Permissão, unidade, criador ou estado da conta não autoriza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Mesmo request_id usado com dados diferentes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite por rota e IP excedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Quando presente, aguardar este número de segundos.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Erro interno. Não confirmar sucesso; conservar o request_id para repetição segura.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderCreate"
              },
              "examples": {
                "tamanho": {
                  "summary": "Um tamanho",
                  "value": {
                    "request_id": "44444444-4444-4444-8444-444444444444",
                    "type": "takeaway",
                    "guest_name": "Cliente de exemplo",
                    "guest_phone": "910000000",
                    "note": "Nota opcional do pedido",
                    "items": [
                      {
                        "product_id": "22222222-2222-4222-8222-222222222222",
                        "variant_id": "33333333-3333-4333-8333-333333333333",
                        "quantity": 1,
                        "note": "Nota opcional do item"
                      }
                    ]
                  }
                },
                "meioAMeio": {
                  "summary": "Dois sabores no mesmo tamanho",
                  "value": {
                    "request_id": "44444444-4444-4444-8444-444444444444",
                    "type": "takeaway",
                    "guest_name": "Cliente de exemplo",
                    "guest_phone": "910000000",
                    "note": "Nota opcional do pedido",
                    "items": [
                      {
                        "product_id": "22222222-2222-4222-8222-222222222222",
                        "variant_id": "33333333-3333-4333-8333-333333333333",
                        "quantity": 1,
                        "halves": [
                          {
                            "product_id": "22222222-2222-4222-8222-222222222222",
                            "variant_id": "33333333-3333-4333-8333-333333333333"
                          },
                          {
                            "product_id": "55555555-5555-4555-8555-555555555555",
                            "variant_id": "66666666-6666-4666-8666-666666666666"
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "summary": "Consultar pedido originado pela mesma chave",
        "tags": [
          "Pedidos"
        ],
        "description": "Exige a permissão orders:read. No máximo 60 consultas por minuto, rota e IP, sujeito aos limites gerais. Resposta com Cache-Control: no-store.",
        "x-orbis-scope": "orders:read",
        "responses": {
          "200": {
            "description": "Objeto JSON direto, sem envelope data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "77777777-7777-4777-8777-777777777777",
                  "order_number": "1042",
                  "status": "placed",
                  "type": "takeaway",
                  "subtotal": 9.9,
                  "total": 9.9,
                  "created_at": "2026-12-01T19:00:00Z",
                  "items": [
                    {
                      "id": "88888888-8888-4888-8888-888888888888",
                      "product_id": "22222222-2222-4222-8222-222222222222",
                      "variant_id": "33333333-3333-4333-8333-333333333333",
                      "product_name": "Pizza A",
                      "variant_name": "25 cm",
                      "composition": null,
                      "quantity": 1,
                      "unit_price": 9.9,
                      "total": 9.9,
                      "note": "Nota opcional do item",
                      "status": "pending"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Formato ou escolha inválida. validation_error inclui issues; integration_request_invalid cobre escolhas de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Permissão, unidade, criador ou estado da conta não autoriza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Pedido inexistente ou originado por outra chave.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite por rota e IP excedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Quando presente, aguardar este número de segundos.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Erro interno. Não confirmar sucesso; conservar o request_id para repetição segura.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/availability": {
      "get": {
        "operationId": "getAvailability",
        "summary": "Consultar horários e condições de reserva",
        "tags": [
          "Reservas"
        ],
        "description": "Exige a permissão reservations:read. No máximo 60 consultas por minuto, rota e IP, sujeito aos limites gerais. Resposta com Cache-Control: no-store.",
        "x-orbis-scope": "reservations:read",
        "responses": {
          "200": {
            "description": "Objeto JSON direto, sem envelope data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Availability"
                },
                "example": {
                  "dia": "2026-12-01",
                  "pessoas": 2,
                  "horas": [
                    {
                      "slot_at": "2026-12-01T20:00:00Z",
                      "lugares_livres": 12,
                      "cabe": true
                    }
                  ],
                  "policy": {
                    "deposit_mode": "none",
                    "deposit_amount": 0,
                    "payment_deadline_minutes": 30,
                    "cancellation_hours": 24,
                    "terms": "Condições de exemplo do restaurante."
                  }
                }
              }
            }
          },
          "400": {
            "description": "Formato ou escolha inválida. validation_error inclui issues; integration_request_invalid cobre escolhas de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Permissão, unidade, criador ou estado da conta não autoriza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite por rota e IP excedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Quando presente, aguardar este número de segundos.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Erro interno. Não confirmar sucesso; conservar o request_id para repetição segura.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "dia",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
            }
          },
          {
            "name": "pessoas",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          }
        ]
      }
    },
    "/reservations": {
      "post": {
        "operationId": "createReservation",
        "summary": "Criar reserva ou pedido de reserva",
        "tags": [
          "Reservas"
        ],
        "description": "Exige a permissão reservations:write. No máximo 10 criações por minuto, rota e IP, sujeito aos limites gerais. Resposta com Cache-Control: no-store. Usar slot_at como inicio, sem converter o fuso. Aceitar a política quando configurada. Apenas status confirmed significa confirmação. Não inicia pagamento de sinal. Repetição idempotente tem as mesmas regras dos pedidos.",
        "x-orbis-scope": "reservations:write",
        "responses": {
          "201": {
            "description": "Objeto JSON direto, sem envelope data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reservation"
                },
                "example": {
                  "id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
                  "starts_at": "2026-12-01T20:00:00Z",
                  "party_size": 2,
                  "duration_minutes": 90,
                  "status": "pending",
                  "deposit_due": 10,
                  "deposit_status": "pending",
                  "payment_expires_at": "2026-12-01T19:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Formato ou escolha inválida. validation_error inclui issues; integration_request_invalid cobre escolhas de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Permissão, unidade, criador ou estado da conta não autoriza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Horário indisponível ou mesmo request_id com dados diferentes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite por rota e IP excedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Quando presente, aguardar este número de segundos.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Erro interno. Não confirmar sucesso; conservar o request_id para repetição segura.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReservationCreate"
              },
              "example": {
                "request_id": "99999999-9999-4999-8999-999999999999",
                "inicio": "2026-12-01T20:00:00Z",
                "pessoas": 2,
                "nome": "Cliente de exemplo",
                "telefone": "910000000",
                "email": "cliente@example.com",
                "nota": "Nota opcional",
                "aceita_politica": true
              }
            }
          }
        }
      }
    },
    "/reservations/{id}": {
      "get": {
        "operationId": "getReservation",
        "summary": "Consultar reserva originada pela mesma chave",
        "tags": [
          "Reservas"
        ],
        "description": "Exige a permissão reservations:read. No máximo 60 consultas por minuto, rota e IP, sujeito aos limites gerais. Resposta com Cache-Control: no-store.",
        "x-orbis-scope": "reservations:read",
        "responses": {
          "200": {
            "description": "Objeto JSON direto, sem envelope data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reservation"
                },
                "example": {
                  "id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
                  "starts_at": "2026-12-01T20:00:00Z",
                  "party_size": 2,
                  "duration_minutes": 90,
                  "status": "pending",
                  "deposit_due": 10,
                  "deposit_status": "pending",
                  "payment_expires_at": "2026-12-01T19:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Formato ou escolha inválida. validation_error inclui issues; integration_request_invalid cobre escolhas de negócio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Permissão, unidade, criador ou estado da conta não autoriza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Reserva inexistente ou originada por outra chave.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite por rota e IP excedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Quando presente, aguardar este número de segundos.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Erro interno. Não confirmar sucesso; conservar o request_id para repetição segura.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "IntegrationKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "orbis_sk_...",
        "description": "Segredo completo criado em Definições > API e site. Guardar exclusivamente no servidor do site."
      }
    },
    "schemas": {
      "OrderCreate": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "type": {
            "type": "string",
            "enum": [
              "dine_in",
              "takeaway"
            ]
          },
          "table_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "guest_name": {
            "type": "string",
            "maxLength": 160
          },
          "guest_phone": {
            "type": "string",
            "maxLength": 40
          },
          "note": {
            "type": "string",
            "maxLength": 500
          },
          "items": {
            "minItems": 1,
            "maxItems": 200,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "product_id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                },
                "variant_id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                },
                "halves": {
                  "type": "array",
                  "prefixItems": [
                    {
                      "type": "object",
                      "properties": {
                        "product_id": {
                          "type": "string",
                          "format": "uuid",
                          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                        },
                        "variant_id": {
                          "type": "string",
                          "format": "uuid",
                          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                        }
                      },
                      "required": [
                        "product_id",
                        "variant_id"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "product_id": {
                          "type": "string",
                          "format": "uuid",
                          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                        },
                        "variant_id": {
                          "type": "string",
                          "format": "uuid",
                          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                        }
                      },
                      "required": [
                        "product_id",
                        "variant_id"
                      ],
                      "additionalProperties": false
                    }
                  ],
                  "items": false,
                  "minItems": 2,
                  "maxItems": 2
                },
                "quantity": {
                  "type": "number",
                  "exclusiveMinimum": 0,
                  "maximum": 999,
                  "multipleOf": 0.01
                },
                "note": {
                  "type": "string",
                  "maxLength": 300
                }
              },
              "required": [
                "product_id",
                "quantity"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "request_id",
          "type",
          "items"
        ],
        "additionalProperties": false
      },
      "ReservationCreate": {
        "type": "object",
        "properties": {
          "inicio": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
          },
          "pessoas": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "nome": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120
          },
          "telefone": {
            "type": "string",
            "minLength": 6,
            "maxLength": 30
          },
          "email": {
            "type": "string",
            "maxLength": 160,
            "format": "email",
            "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
          },
          "nota": {
            "type": "string",
            "maxLength": 500
          },
          "aceita_politica": {
            "type": "boolean"
          },
          "request_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          }
        },
        "required": [
          "inicio",
          "pessoas",
          "nome",
          "telefone",
          "request_id"
        ],
        "additionalProperties": false
      },
      "Catalog": {
        "type": "object",
        "properties": {
          "restaurant": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "name": {
                "type": "string"
              },
              "slug": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "postal_code": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "city": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "timezone": {
                "type": "string"
              },
              "opening_hours": {
                "$ref": "#/components/schemas/Catalog___schema0"
              },
              "currency": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name",
              "slug",
              "address",
              "postal_code",
              "city",
              "phone",
              "timezone",
              "opening_hours",
              "currency"
            ],
            "additionalProperties": false
          },
          "categorias": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "categoria": {
                  "type": "string"
                },
                "ordem": {
                  "type": "number"
                },
                "itens": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                      },
                      "nome": {
                        "type": "string"
                      },
                      "descricao": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "preco": {
                        "type": "number"
                      },
                      "alergenios": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "disponivel": {
                        "type": "boolean"
                      },
                      "fotos": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "variantes": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid",
                              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                            },
                            "nome": {
                              "type": "string"
                            },
                            "preco": {
                              "type": "number"
                            },
                            "disponivel": {
                              "type": "boolean"
                            },
                            "alergenios": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "chave_tamanho": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "preco_meio_a_meio": {
                              "type": [
                                "number",
                                "null"
                              ]
                            }
                          },
                          "required": [
                            "id",
                            "nome",
                            "preco",
                            "disponivel",
                            "alergenios",
                            "chave_tamanho",
                            "preco_meio_a_meio"
                          ],
                          "additionalProperties": false
                        }
                      },
                      "meio_a_meio": {
                        "anyOf": [
                          {
                            "type": "object",
                            "properties": {
                              "grupo": {
                                "type": "string"
                              },
                              "regra_preco": {
                                "type": "string",
                                "enum": [
                                  "highest",
                                  "average",
                                  "fixed"
                                ]
                              }
                            },
                            "required": [
                              "grupo",
                              "regra_preco"
                            ],
                            "additionalProperties": false
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "id",
                      "nome",
                      "descricao",
                      "preco",
                      "alergenios",
                      "disponivel",
                      "fotos",
                      "variantes",
                      "meio_a_meio"
                    ],
                    "additionalProperties": false
                  }
                }
              },
              "required": [
                "categoria",
                "ordem",
                "itens"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "restaurant",
          "categorias"
        ],
        "additionalProperties": false
      },
      "Catalog___schema0": {
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "number"
          },
          {
            "type": "boolean"
          },
          {
            "type": "null"
          },
          {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Catalog___schema0"
            }
          },
          {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/Catalog___schema0"
            }
          }
        ]
      },
      "Order": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "order_number": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "subtotal": {
            "type": "number"
          },
          "total": {
            "type": "number"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                },
                "product_id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                },
                "variant_id": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uuid",
                      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "product_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "variant_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "composition": {
                  "anyOf": [
                    {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "product_id": {
                            "type": "string",
                            "format": "uuid",
                            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                          },
                          "variant_id": {
                            "type": "string",
                            "format": "uuid",
                            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                          },
                          "product_name": {
                            "type": "string"
                          },
                          "variant_name": {
                            "type": "string"
                          },
                          "size_key": {
                            "type": "string"
                          },
                          "ratio": {
                            "type": "number"
                          },
                          "unit_price": {
                            "type": "number"
                          }
                        },
                        "required": [
                          "product_id",
                          "variant_id",
                          "product_name",
                          "variant_name",
                          "size_key",
                          "ratio",
                          "unit_price"
                        ],
                        "additionalProperties": false
                      }
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "quantity": {
                  "type": "number"
                },
                "unit_price": {
                  "type": "number"
                },
                "total": {
                  "type": "number"
                },
                "note": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "status": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "product_id",
                "variant_id",
                "product_name",
                "variant_name",
                "composition",
                "quantity",
                "unit_price",
                "total",
                "note",
                "status"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "id",
          "order_number",
          "status",
          "type",
          "subtotal",
          "total",
          "created_at",
          "items"
        ],
        "additionalProperties": false
      },
      "Availability": {
        "type": "object",
        "properties": {
          "dia": {
            "type": "string",
            "format": "date",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
          },
          "pessoas": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "horas": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "slot_at": {
                  "type": "string",
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                },
                "lugares_livres": {
                  "type": "number"
                },
                "cabe": {
                  "type": "boolean"
                }
              },
              "required": [
                "slot_at",
                "lugares_livres",
                "cabe"
              ],
              "additionalProperties": false
            }
          },
          "policy": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "deposit_mode": {
                    "type": "string",
                    "enum": [
                      "none",
                      "per_booking",
                      "per_person"
                    ]
                  },
                  "deposit_amount": {
                    "type": "number"
                  },
                  "payment_deadline_minutes": {
                    "type": "number"
                  },
                  "cancellation_hours": {
                    "type": "number"
                  },
                  "terms": {
                    "type": "string"
                  }
                },
                "required": [
                  "deposit_mode",
                  "deposit_amount",
                  "payment_deadline_minutes",
                  "cancellation_hours",
                  "terms"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "dia",
          "pessoas",
          "horas",
          "policy"
        ],
        "additionalProperties": false
      },
      "Reservation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "starts_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
          },
          "party_size": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "duration_minutes": {
            "type": "number"
          },
          "status": {
            "type": "string"
          },
          "deposit_due": {
            "type": "number"
          },
          "deposit_status": {
            "type": "string"
          },
          "payment_expires_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "starts_at",
          "party_size",
          "duration_minutes",
          "status",
          "deposit_due",
          "deposit_status",
          "payment_expires_at"
        ],
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "issues": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "url": "https://docs.orbisgroup.pt/",
    "description": "Guia, exemplos de pedidos e reservas e limitações"
  }
}
