{
  "openapi": "3.1.0",
  "info": {
    "title": "Furos de Água Grândola e Comporta — pedido de orçamento",
    "version": "1.0.0",
    "summary": "Pedir um orçamento gratuito de furo de água por JSON.",
    "description": "API pública para agentes de IA e integrações pedirem um orçamento gratuito de furo de água no litoral alentejano (Grândola, Melides, Comporta, Carvalhal, Tróia e Santiago do Cacém). O pedido entra no mesmo fluxo dos formulários do site e respondemos em 24–48 h. Só envie depois de o utilizador confirmar que autoriza a Furos de Água a tratar os dados do pedido. Documentação: https://furosdeaguagrandola.pt/for-agents/. Servidor MCP: https://furosdeaguagrandola.pt/mcp.",
    "contact": {
      "name": "Furos de Água Grândola e Comporta",
      "url": "https://furosdeaguagrandola.pt/for-agents/"
    }
  },
  "servers": [
    {
      "url": "https://furosdeaguagrandola.pt"
    }
  ],
  "externalDocs": {
    "description": "Para agentes de IA",
    "url": "https://furosdeaguagrandola.pt/for-agents/"
  },
  "paths": {
    "/api/orcamento": {
      "get": {
        "operationId": "descreverApi",
        "summary": "Descrição resumida da API",
        "responses": {
          "200": {
            "description": "Resumo e ligações.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "pedirOrcamento",
        "summary": "Pedir orçamento gratuito de furo de água",
        "description": "Valida e regista o pedido como submissão do formulário \"pedido-orcamento\" (origem=agente). Anti-spam: honeypot, limite por origem, limite diário, tamanho máximo 8 KB, verificação de Origin e deteção de duplicados em 24 h. Use X-Dry-Run: 1 para testar.",
        "parameters": [
          {
            "name": "X-Dry-Run",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            },
            "description": "Valida sem guardar nem enviar."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PedidoOrcamento"
              },
              "example": {
                "nome": "Ana Silva",
                "telefone": "+351912345678",
                "email": "ana@exemplo.pt",
                "localidade": "Melides, Grândola",
                "servico": "Novo furo de água",
                "uso_agua": "Jardim ou horta",
                "descricao": "Terreno rústico de 5 000 m²",
                "consent": true,
                "submetido_por": "agente"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Pedido recebido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recebido"
                }
              }
            }
          },
          "200": {
            "description": "Duplicado, dry-run ou pré-visualização (nada de novo foi enviado).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recebido"
                }
              }
            }
          },
          "400": {
            "description": "JSON inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "403": {
            "description": "Origem não permitida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "413": {
            "description": "Corpo superior a 8 KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "415": {
            "description": "Content-Type não é application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "422": {
            "description": "Validação falhou.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErroValidacao"
                }
              }
            }
          },
          "429": {
            "description": "Limite de pedidos excedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Segundos até poder repetir."
              }
            }
          },
          "502": {
            "description": "Falha ao registar o pedido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "503": {
            "description": "Limite diário atingido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      }
    },
    "/obrigado/": {
      "post": {
        "operationId": "submeterFormularioWeb",
        "summary": "Alternativa: POST ao formulário Netlify (não aceita JSON)",
        "description": "Mesmo efeito do formulário /orcamento/. Enviar application/x-www-form-urlencoded com form-name=pedido-orcamento, os campos do formulário, origem=agente, submetido_por=agente, consent=on e bot-field vazio.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "form-name",
                  "nome",
                  "telefone",
                  "email",
                  "localidade",
                  "servico",
                  "uso_agua",
                  "google_maps_pin",
                  "consent"
                ],
                "properties": {
                  "form-name": {
                    "type": "string",
                    "const": "pedido-orcamento"
                  },
                  "nome": {
                    "type": "string"
                  },
                  "telefone": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "localidade": {
                    "type": "string"
                  },
                  "servico": {
                    "type": "string",
                    "enum": [
                      "Novo furo de água",
                      "Limpeza / manutenção de furo",
                      "Bomba / equipamento",
                      "Licenciamento",
                      "Outro"
                    ],
                    "description": "Também aceita o slug: novo-furo, limpeza, bomba, licenciamento, outro."
                  },
                  "uso_agua": {
                    "type": "string",
                    "enum": [
                      "Casa (consumo doméstico)",
                      "Jardim ou horta",
                      "Rega agrícola (exploração)",
                      "Empresa ou indústria",
                      "Ainda não sei"
                    ]
                  },
                  "google_maps_pin": {
                    "type": "string",
                    "format": "uri"
                  },
                  "descricao": {
                    "type": "string"
                  },
                  "origem": {
                    "type": "string",
                    "default": "agente"
                  },
                  "submetido_por": {
                    "type": "string",
                    "default": "agente"
                  },
                  "consent": {
                    "type": "string",
                    "const": "on"
                  },
                  "bot-field": {
                    "type": "string",
                    "maxLength": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Página de obrigado.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PedidoOrcamento": {
        "type": "object",
        "required": [
          "nome",
          "telefone",
          "email",
          "localidade",
          "servico",
          "consent"
        ],
        "additionalProperties": false,
        "properties": {
          "nome": {
            "type": "string",
            "minLength": 2,
            "maxLength": 100,
            "description": "Nome completo.",
            "examples": [
              "Ana Silva"
            ]
          },
          "telefone": {
            "type": "string",
            "maxLength": 20,
            "description": "Telefone português de 9 dígitos (2xxxxxxxx, 91/92/93/96xxxxxxx); aceita +351/00351 e espaços.",
            "examples": [
              "+351912345678"
            ]
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "examples": [
              "ana@exemplo.pt"
            ]
          },
          "localidade": {
            "type": "string",
            "minLength": 2,
            "maxLength": 80,
            "description": "Localidade ou concelho do terreno. Zonas servidas: Grândola, Melides, Comporta, Carvalhal, Tróia, Santiago do Cacém.",
            "examples": [
              "Melides, Grândola"
            ]
          },
          "servico": {
            "type": "string",
            "enum": [
              "Novo furo de água",
              "Limpeza / manutenção de furo",
              "Bomba / equipamento",
              "Licenciamento",
              "Outro"
            ],
            "description": "Também aceita o slug: novo-furo, limpeza, bomba, licenciamento, outro."
          },
          "uso_agua": {
            "type": "string",
            "enum": [
              "Casa (consumo doméstico)",
              "Jardim ou horta",
              "Rega agrícola (exploração)",
              "Empresa ou indústria",
              "Ainda não sei"
            ],
            "description": "Recomendado."
          },
          "descricao": {
            "type": "string",
            "maxLength": 1000
          },
          "google_maps_pin": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Link https do Google Maps (maps.app.goo.gl/… ou google.com/maps/…). Recomendado."
          },
          "consent": {
            "type": "boolean",
            "const": true,
            "description": "Obrigatório. Só true depois de o utilizador autorizar a Furos de Água a tratar os dados do pedido (ver /privacidade/)."
          },
          "submetido_por": {
            "type": "string",
            "enum": [
              "agente"
            ],
            "default": "agente"
          },
          "agente": {
            "type": "string",
            "maxLength": 60,
            "description": "Nome do assistente de IA (opcional)."
          },
          "dry_run": {
            "type": "boolean",
            "default": false,
            "description": "Se true, valida sem guardar nem enviar. Equivale ao cabeçalho X-Dry-Run: 1."
          }
        }
      },
      "Recebido": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "estado": {
            "type": "string",
            "enum": [
              "recebido",
              "duplicado",
              "dry_run",
              "preview_nao_enviado"
            ]
          },
          "prazo_resposta_horas": {
            "type": "integer",
            "const": 48
          },
          "mensagem": {
            "type": "string"
          },
          "contacto": {
            "type": "string"
          },
          "valido": {
            "type": "boolean"
          },
          "normalizado": {
            "type": "object"
          }
        },
        "required": [
          "estado"
        ]
      },
      "Erro": {
        "type": "object",
        "properties": {
          "erro": {
            "type": "string"
          },
          "mensagem": {
            "type": "string"
          },
          "retry_after_segundos": {
            "type": "integer"
          }
        },
        "required": [
          "erro",
          "mensagem"
        ]
      },
      "ErroValidacao": {
        "type": "object",
        "properties": {
          "erro": {
            "type": "string",
            "const": "validacao"
          },
          "mensagem": {
            "type": "string"
          },
          "detalhes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "campo": {
                  "type": "string"
                },
                "mensagem": {
                  "type": "string"
                }
              },
              "required": [
                "campo",
                "mensagem"
              ]
            }
          }
        },
        "required": [
          "erro",
          "detalhes"
        ]
      }
    }
  }
}
