{
  "openapi": "3.1.0",
  "info": {
    "title": "Verão Tour — API pública de reservas",
    "version": "1.0.0",
    "summary": "Catálogo, disponibilidade e reserva com pagamento de passeios no litoral sul de Pernambuco.",
    "description": "API usada pelo próprio site veraotour.com.br e liberada para assistentes e agentes de IA que reservam em nome do cliente.\n\nFluxo recomendado para um agente:\n1. GET /catalogo — escolher um item ativo (campo `id`) e conferir o preço (`price`). Para embarcações, usar GET /embarcacoes e o `produtoId` do roteiro.\n2. GET /disponibilidade — confirmar vagas na data (sempre a partir de amanhã; 25/12 e 01/01 não operam).\n3. POST /criar-pix — gera a cobrança Pix e devolve o código copia-e-cola para o cliente pagar. POST /criar-cartao devolve um link de pagamento com cartão (Mercado Pago).\n4. GET /status — acompanhar até `approved`. O ticket é enviado por e-mail ao cliente.\n\nO preço cobrado é sempre calculado pelo servidor a partir do catálogo; valores enviados pelo cliente são ignorados. Em caso de dúvida, o atendimento humano é pelo WhatsApp +55 81 98294-0650.",
    "contact": { "name": "Verão Tour", "url": "https://veraotour.com.br/", "email": "contato@veraotour.com.br" },
    "termsOfService": "https://veraotour.com.br/politica-de-cancelamento-e-reembolso.html"
  },
  "servers": [{ "url": "https://veraotour.com.br/api" }],
  "paths": {
    "/catalogo": {
      "get": {
        "operationId": "listarCatalogo",
        "summary": "Catálogo público de passeios, catamarãs, travessias e embarcações",
        "description": "Use apenas itens com `active: true` e categoria diferente de `agencia`. O `id` do item é a base do `produto_id` usado no pagamento.",
        "responses": {
          "200": {
            "description": "Catálogo",
            "content": { "application/json": { "schema": { "type": "object", "properties": {
              "ok": { "type": "boolean" },
              "items": { "type": "array", "items": { "$ref": "#/components/schemas/ItemCatalogo" } },
              "inactiveKeys": { "type": "array", "items": { "type": "string" } }
            } } } }
          }
        }
      }
    },
    "/embarcacoes": {
      "get": {
        "operationId": "listarEmbarcacoes",
        "summary": "Lanchas para aluguel privativo, com preço por roteiro",
        "description": "Cada roteiro traz o `produtoId` pronto (ex.: `barco:alfa-300:santo-aleixo`). O aluguel é da embarcação inteira: use `quantidade` = 1 no pagamento.",
        "responses": {
          "200": {
            "description": "Embarcações vendáveis",
            "content": { "application/json": { "schema": { "type": "object", "properties": {
              "ok": { "type": "boolean" },
              "embarcacoes": { "type": "array", "items": { "$ref": "#/components/schemas/Embarcacao" } }
            } } } }
          }
        }
      }
    },
    "/disponibilidade": {
      "get": {
        "operationId": "consultarDisponibilidade",
        "summary": "Vagas restantes de um produto numa data",
        "parameters": [
          { "name": "produto_id", "in": "query", "required": true, "schema": { "type": "string" }, "example": "santo-aleixo:monteiros-tour:0" },
          { "name": "data", "in": "query", "required": true, "schema": { "type": "string", "format": "date" }, "description": "AAAA-MM-DD, a partir de amanhã (horário de Recife)." }
        ],
        "responses": {
          "200": {
            "description": "Disponibilidade",
            "content": { "application/json": { "schema": { "type": "object", "properties": {
              "ok": { "type": "boolean" },
              "produto_id": { "type": "string" },
              "data": { "type": "string", "format": "date" },
              "disponivel": { "type": "integer", "description": "Vagas restantes. 0 = esgotado ou data bloqueada." },
              "ocupado": { "type": "boolean" },
              "capacidade": { "type": "integer" }
            } } } }
          },
          "400": { "description": "Data inválida (hoje, passado, 25/12 ou 01/01)." }
        }
      }
    },
    "/criar-pix": {
      "post": {
        "operationId": "reservarComPix",
        "summary": "Cria a reserva e a cobrança Pix",
        "description": "Devolve o Pix copia-e-cola (`copia_cola`) e o QR Code em base64. A reserva fica pendente até o pagamento e é confirmada automaticamente quando o Pix é pago.",
        "parameters": [{ "$ref": "#/components/parameters/Idempotencia" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PedidoReserva" } } } },
        "responses": {
          "200": {
            "description": "Cobrança criada",
            "content": { "application/json": { "schema": { "type": "object", "properties": {
              "ok": { "type": "boolean" },
              "status": { "type": "string" },
              "copia_cola": { "type": "string", "description": "Código Pix para o cliente colar no app do banco." },
              "qr_base64": { "type": "string", "description": "QR Code PNG em base64." },
              "checkout_token": { "type": "string", "description": "Necessário para consultar GET /status." }
            } } } }
          },
          "400": { "description": "Dados inválidos (mensagem em `erro`)." },
          "409": { "description": "Sem vagas na data, ou código de reserva já usado." },
          "429": { "description": "Muitas requisições; aguarde um minuto." }
        }
      }
    },
    "/criar-cartao": {
      "post": {
        "operationId": "reservarComCartao",
        "summary": "Cria a reserva e um link de pagamento com cartão (Mercado Pago)",
        "parameters": [{ "$ref": "#/components/parameters/Idempotencia" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PedidoReserva" } } } },
        "responses": {
          "200": {
            "description": "Link criado — envie `checkout_url` ao cliente",
            "content": { "application/json": { "schema": { "type": "object", "properties": {
              "ok": { "type": "boolean" },
              "checkout_url": { "type": "string", "format": "uri" },
              "checkout_token": { "type": "string" },
              "status": { "type": "string" }
            } } } }
          },
          "400": { "description": "Dados inválidos (mensagem em `erro`)." },
          "409": { "description": "Sem vagas na data, ou código de reserva já usado." }
        }
      }
    },
    "/status": {
      "get": {
        "operationId": "consultarPagamento",
        "summary": "Situação do pagamento de uma reserva",
        "parameters": [
          { "name": "codigo", "in": "query", "required": true, "schema": { "type": "string" }, "example": "VT-7K2Q9XMA" },
          { "name": "token", "in": "query", "required": true, "schema": { "type": "string" }, "description": "O `checkout_token` devolvido na criação." }
        ],
        "responses": {
          "200": { "description": "`approved` = pago e confirmado; `pending` = aguardando pagamento.", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string" } } } } } },
          "404": { "description": "Código ou token não conferem." }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Idempotencia": {
        "name": "X-Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Texto aleatório de 16 a 128 caracteres (letras, números, - e _). Repetir a mesma chave com o mesmo pedido devolve a mesma cobrança em vez de criar outra.",
        "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16,128}$" }
      }
    },
    "schemas": {
      "PedidoReserva": {
        "type": "object",
        "required": ["codigo", "produto_id", "quantidade", "reserva"],
        "properties": {
          "codigo": { "type": "string", "pattern": "^VT-[A-Z0-9]{6,32}$", "description": "Código da reserva gerado por você, único. Ex.: VT-7K2Q9XMA." },
          "produto_id": { "type": "string", "description": "`id` do catálogo (ou `produtoId` da embarcação) seguido do transfer: `:sem-transfer`, `:carro_4_lugares` (R$ 350), `:carro_6_lugares` (R$ 450) ou `:van_15_lugares` (R$ 950).", "example": "santo-aleixo:monteiros-tour:0:sem-transfer" },
          "quantidade": { "type": "integer", "minimum": 1, "description": "Total de pessoas. Em aluguel de embarcação, 1." },
          "adultos": { "type": "integer", "minimum": 1, "description": "Opcional. Se enviar faixas etárias, adultos + criancas_meia + criancas_gratis deve ser igual a `quantidade`." },
          "criancas_meia": { "type": "integer", "minimum": 0, "description": "Crianças de 6 a 9 anos (50%)." },
          "criancas_gratis": { "type": "integer", "minimum": 0, "description": "Crianças de 1 a 5 anos (grátis)." },
          "sinal_percent": { "type": "integer", "enum": [50, 100], "default": 100, "description": "50 = paga metade agora e o restante no embarque." },
          "cupom": { "type": "string" },
          "reserva": {
            "type": "object",
            "required": ["nome", "email", "data"],
            "properties": {
              "nome": { "type": "string", "maxLength": 120 },
              "email": { "type": "string", "format": "email", "description": "O ticket é enviado para este e-mail." },
              "telefone": { "type": "string", "description": "WhatsApp com DDD, para contato no dia." },
              "data": { "type": "string", "format": "date", "description": "Data do passeio (AAAA-MM-DD), a partir de amanhã." },
              "horario": { "type": "string" },
              "hotel": { "type": "string", "description": "Onde está hospedado, para transfer." }
            }
          }
        }
      },
      "ItemCatalogo": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "category": { "type": "string", "enum": ["passeio", "catamara", "barco", "agencia"] },
          "title": { "type": "string" },
          "price": { "type": "number", "description": "Preço por pessoa em BRL (ou por veículo/embarcação, conforme o item)." },
          "capacity": { "type": "integer" },
          "maxQuantity": { "type": "integer" },
          "active": { "type": "boolean" },
          "duration": { "type": "string" },
          "departure": { "type": "string" },
          "horaSaida": { "type": "string" }
        }
      },
      "Embarcacao": {
        "type": "object",
        "properties": {
          "slug": { "type": "string" },
          "nome": { "type": "string" },
          "pessoas": { "type": "integer", "description": "Capacidade máxima de passageiros." },
          "aceitaPet": { "type": "boolean" },
          "horaSaida": { "type": "string" },
          "horaFim": { "type": "string" },
          "roteiros": { "type": "array", "items": { "type": "object", "properties": {
            "id": { "type": "string" }, "titulo": { "type": "string" }, "preco": { "type": "number" }, "produtoId": { "type": "string" }
          } } }
        }
      }
    }
  }
}
