openapi: 3.1.0

info:
  title: Immo Spot API
  version: 1.0.0
  description: >
    API d'Immo Spot donnant accès aux données immobilières françaises agrégées
    depuis les sources officielles (DGFiP, ADEME, Etalab, Géorisques…).
    Toutes les routes nécessitent une authentification (Bearer token).

servers:
  - url: /api/v1
    description: Serveur principal

security:
  - sanctum: []

tags:
  - name: Parcelles
    description: Informations sur une parcelle cadastrale spécifique
  - name: Zones — Données
    description: Données brutes (listes paginées) dans une zone géographique
  - name: Zones — Statistiques
    description: Statistiques agrégées par période dans une zone géographique

components:

  securitySchemes:
    sanctum:
      type: http
      scheme: bearer
      bearerFormat: token
      description: Token Sanctum obtenu après authentification

  parameters:
    ZoneType:
      name: zoneType
      in: path
      required: true
      description: "Type de zone géographique"
      schema:
        type: string
        enum: [commune, departement, iris, secteur]
        example: "commune"
    ZoneId:
      name: zoneId
      in: path
      required: true
      description: >
        Identifiant de la zone selon son type :
        code INSEE commune (ex. 35238), code INSEE département (ex. 35 ou 2A),
        code IRIS à 9 chiffres (ex. 352380000), ou UUID de secteur personnalisé.
      schema:
        type: string
        example: "35238"
    PerPage:
      name: per_page
      in: query
      required: false
      description: Nombre de résultats par page (défaut 20, max 20)
      schema:
        type: integer
        minimum: 1
        maximum: 20
        default: 20
        example: 20
    Page:
      name: page
      in: query
      required: false
      description: Numéro de page à récupérer (défaut 1)
      schema:
        type: integer
        minimum: 1
        default: 1
        example: 1

  schemas:

    Parcelle:
      type: object
      description: >
        Parcelle cadastrale française identifiée par son code INSEE cadastral.
        Source : Etalab (cadastre ouvert).
      properties:
        insee:
          type: string
          description: Code INSEE cadastral unique de la parcelle (ex. 75056000BX0042)
          example: "35238000BE0329"
        region_insee:
          type: string
          nullable: true
          description: Code INSEE de la région
          example: "53"
        departement_insee:
          type: string
          description: Code INSEE du département (2 ou 3 caractères)
          example: "35"
        commune_insee:
          type: string
          description: Code INSEE de la commune
          example: "35238"
        section_insee:
          type: string
          description: Code INSEE de la section cadastrale
          example: "35238000BE"
        prefix:
          type: string
          description: Préfixe de section (généralement "000")
          example: "000"
        section:
          type: string
          description: Lettre(s) de section
          example: "BE"
        numero:
          type: string
          description: Numéro de parcelle dans la section
          example: "0329"
        contenance:
          type: number
          format: float
          description: Surface cadastrale déclarée en m² (contenance au plan)
          example: 1273.0
        arpente:
          type: boolean
          description: Indique si la parcelle a été arpentée (mesurée précisément)
          example: false
        surface:
          type: number
          format: float
          description: Surface géométrique calculée en m² à partir de la géométrie réelle
          example: 1272.7
        adresse:
          type: string
          nullable: true
          description: Adresse postale la plus probable, obtenue par géocodage inverse du centroïde
          example: "12 Rue de la Paix, 35238 Saint-Grégoire"
        adresses:
          type: array
          description: Liste des adresses candidates issues du géocodage inverse (jusqu'à 5 résultats)
          items:
            type: string
          example:
            - "12 Rue de la Paix, 35238 Saint-Grégoire"
            - "14 Rue de la Paix, 35238 Saint-Grégoire"

    TaxeAmenagement:
      type: object
      description: Taux de taxe d'aménagement pour un niveau administratif donné
      properties:
        zone_application:
          type: string
          description: "Niveau d'application : Communale, Départementale ou Régionale"
          example: "Communale"
        taux:
          type: number
          format: float
          nullable: true
          description: Taux en pourcentage
          example: 5.0
        date_effet:
          type: string
          format: date
          nullable: true
          description: Date d'entrée en vigueur du taux
          example: "2024-01-01"
        commune_insee:
          type: string
          nullable: true
          description: Code INSEE de la commune concernée
          example: "35238"
        parcelle_insee:
          type: string
          nullable: true
          description: Code INSEE cadastral de la parcelle (pour les taux majorés)
          example: "35238000BE0329"

    Copropriete:
      type: object
      description: >
        Copropriété issue du Registre National des Copropriétés (RNC) géré par l'ANAH.
        Données mises à jour annuellement.
      properties:
        immatriculation:
          type: string
          nullable: true
          description: Numéro d'immatriculation au RNC
          example: "AA2099810"
        nom:
          type: string
          nullable: true
          description: Nom de la copropriété
          example: "0149 - SDC 20 RUE GRAMONT"
        adresse:
          type: string
          nullable: true
          description: Adresse complète de la copropriété
          example: "20 r de gramont 75002 Paris"
        periode_construction:
          type: string
          nullable: true
          description: >
            Période de construction. Valeurs possibles : AVANT_1949, DE_1949_A_1960,
            DE_1961_A_1974, DE_1975_A_1993, DE_1994_A_2000, DE_2001_A_2010, APRES_2010,
            NON_CONNUE.
          example: "AVANT_1949"
        lots_total:
          type: integer
          nullable: true
          description: Nombre total de lots
          example: 39
        lots_habitation:
          type: integer
          nullable: true
          description: Nombre de lots à usage d'habitation
          example: 18
        lots_stationnement:
          type: integer
          nullable: true
          description: Nombre de lots à usage de stationnement
          example: 0
        lots_bureaux_commerces:
          type: integer
          nullable: true
          description: Nombre de lots à usage de bureaux ou commerces
          example: 5
        syndic_type:
          type: string
          nullable: true
          description: "Type de syndic : professionnel, bénévole, coopératif…"
          example: "professionnel"
        syndic_nom:
          type: string
          nullable: true
          description: Nom du syndic
          example: "PICKERING REAL ESTATE"
        syndic_siret:
          type: string
          nullable: true
          description: SIRET du syndic
          example: "84258950900012"
        syndic_cooperatif:
          type: boolean
          nullable: true
          description: Indique si le syndicat est coopératif
          example: null
        syndic_principal:
          type: boolean
          nullable: true
          description: Indique s'il s'agit du syndic principal
          example: true
        residence_service:
          type: boolean
          nullable: true
          description: Indique si la copropriété est une résidence-service
          example: null
        nombre_asl:
          type: integer
          nullable: true
          description: Nombre d'ASL (Associations Syndicales Libres) rattachées
          example: 0
        nombre_aful:
          type: integer
          nullable: true
          description: Nombre d'AFUL (Associations Foncières Urbaines Libres) rattachées
          example: 0
        nombre_unions:
          type: integer
          nullable: true
          description: Nombre d'unions de syndicats rattachées
          example: 0
        date_immatriculation:
          type: string
          format: date
          nullable: true
          description: Date d'immatriculation au RNC (YYYY-MM-DD)
          example: "2017-08-18"
        date_reglement:
          type: string
          format: date
          nullable: true
          description: Date du règlement de copropriété (YYYY-MM-DD)
          example: "1952-03-14"
        date_mandat_fin:
          type: string
          format: date
          nullable: true
          description: Date de fin du mandat du syndic (YYYY-MM-DD)
          example: null
        mandat_status:
          type: string
          nullable: true
          description: "Statut du mandat syndic : Mandat en cours, Mandat échu…"
          example: "Mandat en cours"
        commune_insee:
          type: string
          nullable: true
          description: Code INSEE de la commune
          example: "75002"
        departement_insee:
          type: string
          nullable: true
          description: Code INSEE du département
          example: "75"

    Batiment:
      type: object
      description: >
        Bâtiment issu de la base de données nationale des bâtiments (BAN/Etalab — cadastre).
        Enrichi de la surface d'intersection avec la parcelle interrogée.
      properties:
        batiment_id:
          type: string
          nullable: true
          description: Identifiant national unique du bâtiment
          example: "BATIMENT0000000297086212"
        nature:
          type: string
          nullable: true
          description: Nature du bâtiment (ex. Indifférenciée, En construction…)
          example: "Indifférenciée"
        usage1:
          type: string
          nullable: true
          description: Usage principal du bâtiment (ex. Résidentiel, Commercial et services…)
          example: "Résidentiel"
        usage2:
          type: string
          nullable: true
          description: Usage secondaire du bâtiment (si applicable)
          example: "Annexe"
        leger:
          type: boolean
          nullable: true
          description: Indique si le bâtiment est de construction légère
          example: false
        etat:
          type: string
          nullable: true
          description: "État du bâtiment : En service, En projet, En construction, Démoli…"
          example: "En service"
        nb_logts:
          type: integer
          nullable: true
          description: Nombre de logements dans le bâtiment
          example: 3
        nb_etages:
          type: integer
          nullable: true
          description: Nombre d'étages du bâtiment
          example: 2
        mat_murs:
          type: string
          nullable: true
          description: Matériau principal des murs
          example: null
        mat_toits:
          type: string
          nullable: true
          description: Matériau principal de la toiture
          example: null
        hauteur:
          type: number
          nullable: true
          description: Hauteur estimée du bâtiment en mètres
          example: 5.5
        region_insee:
          type: string
          nullable: true
          description: Code INSEE de la région
          example: null
        departement_insee:
          type: string
          nullable: true
          description: Code INSEE du département
          example: "35"
        commune_insee:
          type: string
          nullable: true
          description: Code INSEE de la commune
          example: null
        surface:
          type: number
          nullable: true
          description: Surface totale du bâtiment en m²
          example: 97.28
        intersection:
          type: number
          nullable: true
          description: Surface d'intersection entre le bâtiment et la parcelle en m²
          example: 88.17
        intersection_percent:
          type: number
          nullable: true
          description: Part du bâtiment intersectant la parcelle en %
          example: 91

    Dpe:
      type: object
      description: >
        Diagnostic de Performance Énergétique (DPE) ou audit énergétique, issu de la base ADEME.
        Couvre logements existants, logements neufs et bâtiments tertiaires.
        Données mises à jour mensuellement. Un DPE est valide 10 ans.
      properties:
        etiquette_dpe:
          type: string
          nullable: true
          description: Étiquette DPE de A (très performant) à G (très énergivore)
          example: "D"
        etiquette_ges:
          type: string
          nullable: true
          description: Étiquette GES (émissions de gaz à effet de serre) de A à G
          example: "D"
        conso_kwhep_m2_an:
          type: number
          nullable: true
          description: Consommation énergétique en kWhEP/m²/an
          example: null
        emission_ges_kg_co2_m2_an:
          type: number
          nullable: true
          description: Émissions de GES en kgCO₂/m²/an
          example: null
        type:
          type: string
          nullable: true
          description: "Type de DPE : logement-existant, logement-neuf ou tertiaire"
          example: "tertiaire"
        modele_dpe:
          type: string
          nullable: true
          description: Modèle de méthode de calcul DPE utilisé
          example: "DPE 2006 tertiaire et ERP"
        version_dpe:
          type: string
          nullable: true
          description: Version du logiciel DPE
          example: "2.3"
        date_etablissement_dpe:
          type: string
          format: date
          nullable: true
          description: Date d'établissement du DPE (YYYY-MM-DD)
          example: "2024-02-07"
        date_fin_validite_dpe:
          type: string
          format: date
          nullable: true
          description: Date de fin de validité du DPE (YYYY-MM-DD)
          example: "2034-02-06"
        type_batiment:
          type: string
          nullable: true
          description: "Type de bâtiment : maison, appartement, immeuble…"
          example: null
        secteur_activite:
          type: string
          nullable: true
          description: Secteur d'activité pour les bâtiments tertiaires
          example: "W : Administrations, banques, bureaux"
        annee_construction:
          type: integer
          nullable: true
          description: Année de construction du bâtiment
          example: null
        periode_construction:
          type: string
          nullable: true
          description: Période de construction (tranche)
          example: "avant 1948"
        nombre_occupant:
          type: integer
          nullable: true
          description: Nombre d'occupants pris en compte dans le calcul DPE
          example: null
        surface_habitable_logement:
          type: number
          nullable: true
          description: Surface habitable du logement en m²
          example: null
        surface_utile:
          type: number
          nullable: true
          description: Surface utile en m² (bâtiments tertiaires)
          example: 100.0
        surface_shon:
          type: number
          nullable: true
          description: Surface hors œuvre nette en m²
          example: null
        adresse:
          type: string
          nullable: true
          description: Adresse du logement (source BAN)
          example: null
        commune_insee:
          type: string
          nullable: true
          description: Code INSEE de la commune
          example: "35238"
        departement_insee:
          type: string
          nullable: true
          description: Code INSEE du département
          example: "35"
        type_energie_n1:
          type: string
          nullable: true
          description: Énergie principale utilisée
          example: "Gaz naturel"
        frais_annuel_energie_n1:
          type: number
          nullable: true
          description: Frais annuels estimés pour l'énergie principale en euros
          example: 14712.0
        type_energie_n2:
          type: string
          nullable: true
          description: Deuxième énergie utilisée (si applicable)
          example: "Électricité"
        frais_annuel_energie_n2:
          type: number
          nullable: true
          description: Frais annuels estimés pour la deuxième énergie en euros
          example: 3866.3
        type_energie_n3:
          type: string
          nullable: true
          description: Troisième énergie utilisée (si applicable)
          example: null
        frais_annuel_energie_n3:
          type: number
          nullable: true
          description: Frais annuels estimés pour la troisième énergie en euros
          example: null

    Friche:
      type: object
      description: >
        Friche industrielle ou urbaine (site délaissé ou en reconversion), issue de la
        base Cartofriches (Cerema), conforme au standard CNIG "Sites et friches" v1.0.1.
        Ne couvre pas l'ensemble du territoire national : certains départements ou
        communes ne sont pas encore renseignés par la source. Les champs de catégorie
        (type, statut, occupation…) sont saisis en texte libre par les producteurs de
        la donnée : les valeurs ne sont pas parfaitement normalisées (variantes de casse,
        libellés proches) et ne sont donc pas restreintes à une énumération stricte ici.
      properties:
        site_id:
          type: string
          nullable: true
          description: Identifiant du site (format `<code_insee_commune>_<identifiant>`)
          example: "01001_32316"
        site_nom:
          type: string
          nullable: true
          description: Nom usuel ou désignation du site
          example: "Transformation en salle de sport"
        site_type:
          type: string
          nullable: true
          description: >
            Catégorie du site friche : industrielle, commerciale, hospitalière,
            agro-industrielle, carrière/mine, militaire, d'habitat, équipement public,
            portuaire, aéroportuaire, ferroviaire, logistique, loisir/tourisme/hôtellerie,
            enseignement, cultuelle, mixte, inconnu, autre. Valeurs libres, non normalisées.
          example: "friche industrielle"
        site_adresse:
          type: string
          nullable: true
          description: Adresse du site
          example: "route du château"
        site_occupation:
          type: string
          nullable: true
          description: >
            Statut d'occupation du site : occupé, partiellement occupé/inoccupé,
            totalement inoccupé, occupation illicite (avérée/supposée), occupation
            transitoire ou temporaire, inconnu. Valeurs libres, non normalisées.
          example: "totalement inoccupé"
        site_statut:
          type: string
          nullable: true
          description: Statut de la friche vis-à-vis d'un projet de reconversion
          enum: ["friche potentielle", "friche sans projet", "friche avec projet", "friche reconvertie"]
          example: "friche avec projet"
        site_surface:
          type: number
          nullable: true
          description: Surface du site en m²
          example: 5133.94
        centre_bourg:
          type: boolean
          nullable: true
          description: >
            Indique si le site est situé en centre-bourg. Champ d'enrichissement
            propre à Cartofriches, absent du standard CNIG.
          example: true
        activite_libelle:
          type: string
          nullable: true
          description: Libellé(s) de la ou des anciennes activités exercées sur le site
          example: "Fabrication de textiles"
        activite_code:
          type: string
          nullable: true
          description: Code(s) de l'ancienne activité, nomenclature NAF/BASIAS
          example: "B05.10Z"
        activite_fin_annee:
          type: string
          nullable: true
          description: Année de fin de la dernière activité connue
          example: "1998"
        bati_type:
          type: string
          nullable: true
          description: >
            Type(s) de bâti présent(s) sur le site : industriel, commercial,
            résidentiel, tertiaire, agricole, équipement public, mixte, sans objet,
            inconnu. Plusieurs valeurs possibles séparées par `|` (ex. "industriel|résidentiel").
            Valeurs libres, non normalisées.
          example: "industriel|résidentiel"
        bati_nombre:
          type: integer
          nullable: true
          description: Nombre de bâtiments présents sur le site
          example: 3
        bati_vacance:
          type: string
          nullable: true
          description: >
            État d'occupation du bâti : occupé, partiellement occupé/inoccupé,
            vacant, sans objet, inconnu. Valeurs libres, non normalisées.
          example: "vacant"
        bati_etat:
          type: string
          nullable: true
          description: >
            Niveau de dégradation du bâti : inexistante/faible, moyenne, très importante,
            hétérogène, sans objet, inconnu. Valeurs libres, non normalisées.
          example: "dégradation moyenne"
        site_reconv_type:
          type: string
          nullable: true
          description: >
            Type de projet de reconversion identifié pour le site (ex. habitat, mixte,
            bureau, commerce, panneaux photovoltaïques, renaturation). Champ
            d'enrichissement propre à Cartofriches, absent du standard CNIG.
          example: "mixte"
        site_reconv_annee:
          type: string
          nullable: true
          description: >
            Année prévue ou réalisée de la reconversion. Champ d'enrichissement propre
            à Cartofriches, absent du standard CNIG.
          example: "2025"
        proprio_type:
          type: array
          nullable: true
          items:
            type: string
          description: >
            Catégorie(s) du propriétaire actuel, codifiée(s) selon la nomenclature des
            catégories de propriétaires des Fichiers fonciers (DGFiP) — ex. P5a = commune,
            G1a = État. Un site peut avoir plusieurs propriétaires (indivision).
          example: ["P5a"]
        proprio_nom:
          type: array
          nullable: true
          items:
            type: string
          description: >
            Nom(s) du ou des propriétaires actuels (personnes morales uniquement, source
            Fichiers fonciers). Un site peut avoir plusieurs propriétaires (indivision).
          example: ["COMMUNE DE L'ABERGEMENT-CLEMENCIAT"]
        sol_pollution_existe:
          type: string
          nullable: true
          description: >
            Existence connue d'une pollution des sols (source BASOL) : inexistante,
            traitée, peu probable, supposée, probable, avérée, inconnu. Valeurs libres,
            non normalisées.
          example: "pollution inexistante"
        sol_pollution_origine:
          type: string
          nullable: true
          description: >
            Origine de la pollution des sols (source BASOL) : dépôt de déchets/stockage
            de produits, pollution due au fonctionnement de l'installation, origine
            accidentelle, retombées atmosphériques, inconnu. Valeurs libres, non normalisées.
          example: null
        sol_pollution_annee:
          type: string
          nullable: true
          description: Année de constatation de la pollution des sols
          example: null
        urba_zone_type:
          type: string
          nullable: true
          description: >
            Type de zone au document d'urbanisme, nomenclature CNIG PLU
            (U, AU, AUc, AUs, A, N, Zc, ZCa, ZnC, Ah, Nh…)
          example: "U"
        urba_zone_lib:
          type: string
          nullable: true
          description: Libellé (court, avec description) de la zone au document d'urbanisme
          example: "UB - Zone urbaine peripherique peu dense"
        comm_nom:
          type: string
          nullable: true
          description: Nom de la commune
          example: "L'Abergement-Clémenciat"
        commune_insee:
          type: string
          nullable: true
          description: Code INSEE de la commune
          example: "01001"
        departement_insee:
          type: string
          nullable: true
          description: Code INSEE du département
          example: "01"
        site_url:
          type: string
          nullable: true
          description: >
            Lien vers la fiche BASIAS, BASOL, SIS ou un observatoire local du site,
            lorsqu'il existe.
          example: null

    FondsCommerce:
      type: object
      description: >
        Cession de fonds de commerce publiée au BODACC (Bulletin Officiel des Annonces
        Civiles et Commerciales). Source : Etalab/data.gouv.fr, mise à jour hebdomadaire.
      properties:
        annonce_id:
          type: string
          nullable: true
          description: Identifiant unique de l'annonce BODACC
          example: "A20090141594"
        numero_annonce:
          type: string
          nullable: true
          description: Numéro de l'annonce dans le BODACC
          example: "594"
        adresse:
          type: string
          nullable: true
          description: Adresse du fonds de commerce
          example: "les Bourgeons 35690 Acigné"
        code_postal:
          type: string
          nullable: true
          description: Code postal de la commune
          example: "35690"
        commune:
          type: string
          nullable: true
          description: Nom de la commune
          example: "Acigné"
        montant:
          type: number
          nullable: true
          description: Montant de la cession en euros
          example: 19000.0
        date:
          type: string
          format: date
          nullable: true
          description: Date de parution de l'annonce au BODACC (YYYY-MM-DD)
          example: "2009-07-26"
        url:
          type: string
          nullable: true
          description: Lien vers l'annonce sur bodacc.fr
          example: "https://www.bodacc.fr/pages/annonces-commerciales-detail/?q.id=id:A20090141594"
        tribunal:
          type: string
          nullable: true
          description: Greffe du tribunal de commerce ayant enregistré la cession
          example: "GREFFE DU TRIBUNAL DE COMMERCE DE RENNES"
        commercant:
          type: string
          nullable: true
          description: Nom du commerçant ou de l'enseigne
          example: "BREIZH PAVAGE, REDOUTE, Michel"
        liste_personnes:
          type: string
          nullable: true
          description: JSON — liste des personnes physiques ou morales impliquées
          example: "[{\"capital\":{\"montantCapital\":\"20000.00\",\"devise\":\"EUR\"}}]"
        acte:
          type: string
          nullable: true
          description: JSON — informations sur l'acte de cession (type de vente, etc.)
          example: "{\"vente\":{\"categorieVente\":\"Apport d'un établissement principal\"}}"
        liste_etablissements:
          type: string
          nullable: true
          description: JSON — liste des établissements compris dans la cession
          example: "[{\"origineFonds\":\"Etablissement principal\",\"activite\":\"travaux\"}]"
        liste_precedent_exploitant:
          type: string
          nullable: true
          description: JSON — liste des précédents exploitants
          example: null
        liste_precendent_proprietaires:
          type: string
          nullable: true
          description: JSON — liste des précédents propriétaires
          example: "[{\"nom\":\"REDOUTE\",\"prenom\":\"Michel\",\"typePersonne\":\"pp\"}]"

    Permis:
      type: object
      description: >
        Autorisation d'urbanisme (permis de construire, déclaration préalable, permis
        d'aménager ou permis de démolir) issue de la base Sitadel du Ministère de la
        Transition Écologique.
      properties:
        numero:
          type: string
          nullable: true
          description: Numéro du dossier d'autorisation
          example: "03513312B0005"
        type:
          type: string
          nullable: true
          enum: [permis-construire, declaration-prealable, permis-amenager, permis-demolir]
          description: Type technique de l'autorisation
          example: "permis-amenager"
        type_label:
          type: string
          nullable: true
          description: "Libellé du type d'autorisation : Permis de construire, Déclaration préalable, Permis aménager, Permis de démolir"
          example: "Permis aménager"
        adresse:
          type: string
          nullable: true
          description: Adresse du projet
          example: "12 rue de la Mairie 35133 Fougères"
        an_depot:
          type: integer
          nullable: true
          description: Année de dépôt du dossier
          example: 2020
        date_reelle_autorisation:
          type: string
          format: date
          nullable: true
          description: Date réelle d'autorisation (YYYY-MM-DD)
          example: "2013-01-22"
        date_reelle_chantier:
          type: string
          format: date
          nullable: true
          description: "Date réelle de début de chantier (YYYY-MM-DD). Absente pour les permis de démolir."
          example: "2013-06-01"
        date_reelle_achevement:
          type: string
          format: date
          nullable: true
          description: "Date réelle d'achèvement des travaux (YYYY-MM-DD). Absente pour les permis de démolir."
          example: "2014-09-15"
        etat_permis:
          type: string
          nullable: true
          description: "État du dossier (libellé) : Autorisé, Commencé, Terminé, Annulé"
          example: "Autorisé"
        etat_permis_slug:
          type: string
          nullable: true
          enum: [autorise, commence, termine, annule]
          description: État du dossier, normalisé (utilisé pour le filtre `statut`)
          example: "autorise"
        destination_principale:
          type: string
          nullable: true
          description: Destination principale du projet (logement, commerce, bureaux…)
          example: "Logement"
        nb_lgt_tot_crees:
          type: integer
          nullable: true
          description: "Nombre total de logements créés (uniquement pour les permis de construire)"
          example: 4
        surf_hab_creee:
          type: number
          nullable: true
          description: "Surface habitable créée en m² (uniquement pour les permis de construire)"
          example: 320.5
        demandeur:
          type: object
          description: Informations sur le demandeur de l'autorisation
          properties:
            denomination:
              type: string
              nullable: true
              description: Dénomination du demandeur (personne morale)
              example: "ZAC"
            siren:
              type: string
              nullable: true
              example: "213502388"
            siret:
              type: string
              nullable: true
              example: "21350238802445"
            ape:
              type: string
              nullable: true
              description: Code APE/NAF du demandeur
              example: "42.99Z"
            forme_juridique:
              type: string
              nullable: true
              example: "Commune et commune nouvelle"
            forme_juridique_code:
              type: string
              nullable: true
              example: "7210"
            code_postal:
              type: string
              nullable: true
              example: "35031"
            commune:
              type: string
              nullable: true
              example: "RENNES"
        i_piscine:
          type: boolean
          nullable: true
          description: "Présence d'une piscine (uniquement pour les permis de construire)"
          example: false
        i_extension:
          type: boolean
          nullable: true
          description: "Extension (uniquement pour les permis de construire)"
          example: false
        i_surelevation:
          type: boolean
          nullable: true
          description: "Surélévation (uniquement pour les permis de construire)"
          example: false
        i_nivsupp:
          type: boolean
          nullable: true
          description: "Niveau supplémentaire (uniquement pour les permis de construire)"
          example: false
        i_garage:
          type: boolean
          nullable: true
          description: "Garage (uniquement pour les permis de construire)"
          example: false
        i_veranda:
          type: boolean
          nullable: true
          description: "Véranda (uniquement pour les permis de construire)"
          example: false
        i_abri_jardin:
          type: boolean
          nullable: true
          description: "Abri de jardin (uniquement pour les permis de construire)"
          example: false
        i_autre_annexe:
          type: boolean
          nullable: true
          description: "Autre annexe (uniquement pour les permis de construire)"
          example: false
        commune_insee:
          type: string
          nullable: true
          description: Code INSEE de la commune
          example: "35133"
        departement_insee:
          type: string
          nullable: true
          description: Code INSEE du département
          example: "35"

    Loyer:
      type: object
      description: >
        Loyer de référence au m² (hors charges) pour un type de logement, issu de
        l'Observatoire des Loyers (OLAP/CLCV). Couvre les grandes agglomérations françaises,
        données annuelles.
      properties:
        year:
          type: integer
          description: Année de référence du loyer
          example: 2025
        type:
          type: string
          description: "Type de logement : appartement, appartement_1_2, appartement_3, appartement_4+, maison"
          example: "appartement_3"
        loyer_m2:
          type: number
          nullable: true
          description: Loyer médian au m² en euros
          example: 13.86
        int95_lower_bound:
          type: number
          nullable: true
          description: Borne inférieure de l'intervalle de confiance à 95 %
          example: 10.42
        int95_upper_bound:
          type: number
          nullable: true
          description: Borne supérieure de l'intervalle de confiance à 95 %
          example: 18.42
        nb_observations:
          type: integer
          nullable: true
          description: Nombre d'observations ayant servi au calcul du loyer
          example: 16286

    Mutation:
      type: object
      description: >
        Mutation immobilière (vente) issue de la base Demandes de Valeurs Foncières (DVF)
        publiée par la DGFiP. Couvre les mutations depuis 2014, mises à jour semestriellement.
      properties:
        id:
          type: integer
          description: Identifiant unique de la mutation
          example: 64165091
        nature:
          type: string
          nullable: true
          description: Nature de la mutation (ex. Vente, Vente en l'état futur d'achèvement, Adjudication…)
          example: "Vente"
        vefa:
          type: boolean
          nullable: true
          description: Indique si la vente est en l'état futur d'achèvement (VEFA)
          example: null
        valeur_fonciere:
          type: number
          nullable: true
          description: Valeur foncière de la mutation en euros
          example: 455000
        surface_terrain:
          type: number
          nullable: true
          description: Surface du terrain en m²
          example: 245
        surface_bati:
          type: number
          nullable: true
          description: Surface bâtie totale en m²
          example: 111
        date:
          type: string
          format: date-time
          nullable: true
          description: Date de la mutation
          example: "2025-06-29"
        annee:
          type: integer
          nullable: true
          description: Année de la mutation
          example: 2025
        commune_insee:
          type: string
          nullable: true
          description: Code INSEE de la commune
          example: "35238"
        departement_insee:
          type: string
          nullable: true
          description: Code INSEE du département
          example: "35"
        adresse:
          type: string
          nullable: true
          description: Adresse complète de la mutation
          example: "59 Rue Ginguene 35000 Rennes"
        nb_parcelles:
          type: integer
          nullable: true
          description: Nombre de parcelles concernées par la mutation
          example: 1
        parcelles:
          type: array
          nullable: true
          description: Liste des codes INSEE cadastraux des parcelles concernées
          items:
            type: string
          example: ["35238000BV0175"]
        nb_maisons:
          type: integer
          nullable: true
          description: Nombre de maisons comprises dans la mutation
          example: 0
        nb_appartements:
          type: integer
          nullable: true
          description: Nombre d'appartements compris dans la mutation
          example: 3
        nb_dependances:
          type: integer
          nullable: true
          description: Nombre de dépendances comprises dans la mutation
          example: 4
        nb_activites:
          type: integer
          nullable: true
          description: Nombre de locaux d'activité compris dans la mutation
          example: 0
        nb_pieces:
          type: integer
          nullable: true
          description: Nombre de pièces principales
          example: 4
        prix_surface_bati:
          type: number
          nullable: true
          description: Prix au m² de surface bâtie (valeur_fonciere / surface_bati)
          example: 4099.1
        prix_surface_terr:
          type: number
          nullable: true
          description: Prix au m² de terrain (valeur_fonciere / surface_terrain)
          example: 1857.14

    DvfStatsPeriod:
      type: object
      description: >
        Statistiques DVF agrégées pour un pas de temps donné.
        Les indicateurs portent sur l'ensemble des mutations correspondant aux filtres appliqués.
      properties:
        date:
          type: string
          description: Libellé lisible de la période (ex. "2024", "Jan 2024", "T1 2024")
          example: "2024"
        date_start:
          type: string
          format: date
          description: Date de début de la période
          example: "2024-01-01"
        date_end:
          type: string
          format: date
          description: Date de fin de la période
          example: "2024-12-31"
        count:
          type: integer
          description: Nombre de mutations
          example: 47
        prix_avg:
          type: integer
          nullable: true
          description: Prix moyen en euros
          example: 285000
        prix_med:
          type: integer
          nullable: true
          description: Prix médian en euros
          example: 265000
        prix_min:
          type: integer
          nullable: true
          description: Prix minimum en euros
          example: 120000
        prix_max:
          type: integer
          nullable: true
          description: Prix maximum en euros
          example: 850000
        surface_bati_avg:
          type: integer
          nullable: true
          description: Surface bâtie moyenne en m²
          example: 102
        surface_bati_med:
          type: integer
          nullable: true
          description: Surface bâtie médiane en m²
          example: 95
        surface_bati_min:
          type: integer
          nullable: true
          description: Surface bâtie minimale en m²
          example: 35
        surface_bati_max:
          type: integer
          nullable: true
          description: Surface bâtie maximale en m²
          example: 320
        surface_terr_avg:
          type: integer
          nullable: true
          description: Surface terrain moyenne en m²
          example: 620
        surface_terr_med:
          type: integer
          nullable: true
          description: Surface terrain médiane en m²
          example: 480
        surface_terr_min:
          type: integer
          nullable: true
          description: Surface terrain minimale en m²
          example: 0
        surface_terr_max:
          type: integer
          nullable: true
          description: Surface terrain maximale en m²
          example: 8500
        prix_m2_bati_avg:
          type: integer
          nullable: true
          description: Prix moyen au m² de bâti en euros
          example: 2780
        prix_m2_bati_med:
          type: integer
          nullable: true
          description: Prix médian au m² de bâti en euros
          example: 2640
        prix_m2_bati_min:
          type: integer
          nullable: true
          description: Prix minimum au m² de bâti en euros
          example: 900
        prix_m2_bati_max:
          type: integer
          nullable: true
          description: Prix maximum au m² de bâti en euros
          example: 7200
        prix_m2_terr_avg:
          type: integer
          nullable: true
          description: Prix moyen au m² de terrain en euros
          example: 110
        prix_m2_terr_med:
          type: integer
          nullable: true
          description: Prix médian au m² de terrain en euros
          example: 95
        prix_m2_terr_min:
          type: integer
          nullable: true
          description: Prix minimum au m² de terrain en euros
          example: 15
        prix_m2_terr_max:
          type: integer
          nullable: true
          description: Prix maximum au m² de terrain en euros
          example: 1800

    DpeStatsPeriod:
      type: object
      description: >
        Statistiques DPE agrégées pour un pas de temps donné.
        Inclut le total de DPE établis pendant la période et leur répartition par classe énergétique.
      properties:
        date:
          type: string
          description: Libellé lisible de la période (ex. "2024", "Jan 2024", "T1 2024")
          example: "2024"
        date_start:
          type: string
          format: date
          description: Date de début de la période
          example: "2024-01-01"
        date_end:
          type: string
          format: date
          description: Date de fin de la période
          example: "2024-12-31"
        count:
          type: integer
          description: Nombre total de DPE établis pendant la période
          example: 1802
        count_a:
          type: integer
          description: Nombre de DPE de classe A
          example: 68
        count_b:
          type: integer
          description: Nombre de DPE de classe B
          example: 187
        count_c:
          type: integer
          description: Nombre de DPE de classe C
          example: 391
        count_d:
          type: integer
          description: Nombre de DPE de classe D
          example: 542
        count_e:
          type: integer
          description: Nombre de DPE de classe E
          example: 410
        count_f:
          type: integer
          description: Nombre de DPE de classe F
          example: 148
        count_g:
          type: integer
          description: Nombre de DPE de classe G
          example: 56

    Error:
      type: object
      properties:
        message:
          type: string
          description: Description de l'erreur
          example: "Unauthenticated."

paths:

  /parcelle:
    get:
      operationId: resolve_parcelle
      x-title: "Parcelle (par adresse ou coordonnées)"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleResolve
        method: apply
        params: []
      summary: Trouver une parcelle cadastrale par adresse ou coordonnées GPS
      description: >
        Résout un emplacement en parcelle cadastrale. Deux modes d'entrée, mutuellement exclusifs :

        **Mode adresse** (`q`) — géocode l'adresse via l'API Géoplateforme (IGN/cartes.gouv.fr)
        pour obtenir les coordonnées, puis effectue un géocodage inverse avec l'index "parcel"
        pour identifier la parcelle.

        **Mode coordonnées** (`lat` + `lon`) — effectue directement un géocodage inverse
        avec l'index "parcel" aux coordonnées fournies.

        Dans les deux cas, retourne les mêmes informations que `GET /parcelle/{insee}`.
        Le code `insee` retourné peut ensuite être utilisé pour interroger les autres endpoints.
      tags:
        - Parcelles
      parameters:
        - name: q
          in: query
          required: false
          description: >
            Adresse ou lieu en texte libre. Obligatoire si lat/lon absents.
            Exemples : "12 rue de la Paix 75002 Paris", "Place du Capitole Toulouse".
          schema:
            type: string
            minLength: 3
            example: "12 rue de la Paix 75002 Paris"
        - name: lat
          in: query
          required: false
          description: Latitude WGS84. Obligatoire avec lon si q absent.
          schema:
            type: number
            format: float
            minimum: -90
            maximum: 90
            example: 48.8698
        - name: lon
          in: query
          required: false
          description: Longitude WGS84. Obligatoire avec lat si q absent.
          schema:
            type: number
            format: float
            minimum: -180
            maximum: 180
            example: 2.3322
      responses:
        "200":
          description: Parcelle trouvée
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Parcelle'
        "404":
          description: Aucune adresse ou parcelle trouvée pour les paramètres fournis
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Paramètres invalides (q manquant ou lat/lon manquants)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /parcelle/{insee}:
    get:
      operationId: parcelle_by_insee
      x-title: "Parcelle (par code cadastral)"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleRead
        method: apply
        params: [insee]
      summary: Récupérer les informations cadastrales
      description: >
        Retourne les informations de base d'une parcelle cadastrale française à partir
        de son code INSEE cadastral : surface, localisation administrative, adresse
        et données cadastrales.
        Les données proviennent du cadastre Etalab (mise à jour semestrielle).
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: >
            Code INSEE cadastral de la parcelle. Format : code commune (5 chiffres)
            + préfixe (3 chiffres) + section (1-2 lettres) + numéro (4 chiffres).
            Exemple : 75056000BX0042 (Paris 16e, section BX, parcelle 42).
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Parcelle trouvée
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Parcelle'
              example:
                insee: "35238000AB0964"
                region_insee: "53"
                departement_insee: "35"
                commune_insee: "35238"
                section_insee: "35238000AB"
                prefix: "000"
                section: "AB"
                numero: "0964"
                contenance: 5134.0
                arpente: false
                surface: 5126.3
                centroid:
                  type: Point
                  coordinates: [-1.679943256, 48.118727939]
                adresse: "4 Allée Marie Berhaut 35000 Rennes"
                adresses:
                  - "4 Allée Marie Berhaut 35000 Rennes"
                  - "6 Allée Marie Berhaut 35000 Rennes"
        "401":
          description: Non authentifié
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "404":
          description: Parcelle introuvable pour ce code INSEE
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /parcelle/{insee}/dvf:
    get:
      operationId: parcelle_dvf
      x-title: "Parcelle DVF"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleDvf
        method: apply
        params: [insee]
      summary: Mutations immobilières (DVF)
      description: >
        Retourne toutes les mutations immobilières (ventes) enregistrées sur la parcelle
        ou à proximité immédiate, issues de la base Demandes de Valeurs Foncières (DVF)
        publiée par la DGFiP. Résultat JSON avec clé `data` (tableau).
        Chaque entrée contient : id, nature, valeur_fonciere, surface_terrain, surface_bati,
        date, annee, adresse, nb_parcelles, nb_maisons, nb_appartements, nb_dependances,
        nb_activites, nb_pieces, prix_surface_bati, prix_surface_terr, l_idpar.
        Les données couvrent les mutations depuis 2014, mises à jour semestriellement.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle (ex. 75056000BX0042)
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Liste des mutations immobilières
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Mutation'
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/dpe:
    get:
      operationId: parcelle_dpe
      x-title: "Parcelle DPE"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleDpe
        method: apply
        params: [insee]
      summary: Diagnostics de Performance Énergétique (DPE)
      description: >
        Retourne tous les DPE (Diagnostics de Performance Énergétique) et audits
        énergétiques associés aux bâtiments de la parcelle, issus de la base ADEME.
        Résultat JSON (tableau), trié par date décroissante.
        Couvre logements existants, logements neufs et bâtiments tertiaires.
        Inclut étiquettes DPE/GES, consommation, émissions, surfaces, énergies utilisées
        et frais annuels estimés. Les DPE sont valides 10 ans. Données mises à jour mensuellement.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle (ex. 75056000BX0042)
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Liste des DPE associés aux bâtiments de la parcelle
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Dpe'
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/loyers:
    get:
      operationId: parcelle_loyers
      x-title: "Parcelle Loyers"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleLoyers
        method: apply
        params: [insee]
      summary: Loyers de référence au m²
      description: >
        Retourne les loyers de référence au m² (hors charges) pour la commune
        où se trouve la parcelle, selon l'Observatoire des Loyers (OLAP/CLCV).
        Données disponibles par type de logement (T1, T2, T3, T4+) et par année.
        Chaque entrée contient : year, type (T1/T2/T3/T4+), loyer_m2, int95_lower_bound,
        int95_upper_bound, nb_obs_maille. Seule l'année la plus récente disponible est retournée.
        Ces données sont indicatives et couvrent les grandes agglomérations françaises.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle (ex. 75056000BX0042)
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Loyers de référence au m² pour l'année la plus récente disponible
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Loyer'
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/proprietaires:
    get:
      operationId: parcelle_proprietaires
      x-title: "Parcelle Propriétaires"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleProprietaires
        method: apply
        params: [insee]
      summary: Propriétaires
      description: >
        Retourne la liste des propriétaires (personnes morales) de la parcelle,
        issue des données SPF (Service de Publicité Foncière) via les actes notariaux.
        Résultat JSON avec clé `data` (tableau), dédupliqué par SIREN.
        Chaque entrée contient : proprietaire_siren, proprietaire_forme_juridique,
        proprietaire_nom, adresse, parcelle_insee, commune_insee, departement_insee.
        Note : ces données couvrent principalement les personnes morales (entreprises, SCI, etc.),
        pas les particuliers (données protégées).
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle (ex. 75056000BX0042)
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Propriétaires (personnes morales) de la parcelle
          content:
            application/json:
              example:
                data:
                  - proprietaire_siren: "383349107"
                    proprietaire_forme_juridique: "ASS"
                    proprietaire_nom: "FREHA"
                    adresse: "69 Rue DE CLERY PARIS 02"
                    parcelle_insee: "75102000AO0117"
                    commune_insee: "75102"
                    departement_insee: "75"
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/batiments:
    get:
      operationId: parcelle_batiments
      x-title: "Parcelle Bâtiments"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleBatiments
        method: apply
        params: [insee]
      summary: Bâtiments
      description: >
        Retourne les bâtiments présents sur la parcelle (intersectant sa géométrie)
        issus de la base de données nationale des bâtiments (BAN/Etalab),
        accompagnés d'indicateurs de surface calculés pour la parcelle.

        La réponse contient deux clés :

        - `parcelle` — agrégats de surface : `surface_batie` (m²), `surface_non_batie` (m²),
          `surface_batie_percent` (%), `surface_non_batie_percent` (%).

        - `batiments` — tableau de bâtiments avec ≥ 10 % d'intersection. Chaque entrée inclut :
          nature, usage1, usage2, nb_logts, nb_etages, mat_murs, mat_toits, hauteur,
          surface, intersection, intersection_percent.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: "Agrégats de surface parcelle + liste de bâtiments"
          content:
            application/json:
              schema:
                type: object
                properties:
                  parcelle:
                    type: object
                    description: Agrégats de surface calculés pour la parcelle
                    properties:
                      surface_batie:
                        type: number
                        description: Surface couverte par les bâtiments en m²
                        example: 88.17
                      surface_non_batie:
                        type: number
                        description: Surface non bâtie de la parcelle en m²
                        example: 5038.56
                      surface_batie_percent:
                        type: number
                        description: Part bâtie de la parcelle en %
                        example: 2
                      surface_non_batie_percent:
                        type: number
                        description: Part non bâtie de la parcelle en %
                        example: 98
                  batiments:
                    type: array
                    description: Bâtiments intersectant la parcelle à ≥ 10 %, triés par intersection décroissante
                    items:
                      $ref: '#/components/schemas/Batiment'
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/coproprietes:
    get:
      operationId: parcelle_coproprietes
      x-title: "Parcelle Copropriétés"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleCoproprietes
        method: apply
        params: [insee]
      summary: Copropriétés
      description: >
        Retourne les copropriétés issues du Registre National des Copropriétés (RNC/ANAH)
        dont l'adresse correspond à la parcelle.
        Chaque entrée contient : numéro d'immatriculation, nom, adresse, nombre de lots,
        période de construction, syndicat de copropriété et données financières disponibles.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Copropriétés dont l'adresse correspond à la parcelle
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Copropriete'
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/fonds-commerce:
    get:
      operationId: parcelle_fonds_commerce
      x-title: "Parcelle Fonds de Commerce"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleFondsCommerce
        method: apply
        params: [insee]
      summary: Cessions de fonds de commerce
      description: >
        Retourne les cessions de fonds de commerce publiées au BODACC (Bulletin Officiel
        des Annonces Civiles et Commerciales) pour la parcelle, triées par date décroissante.
        Résultat JSON avec clé `data` (tableau). Chaque entrée contient : date_parution,
        type_avis_lib, commercant, ville, adresse, liste_etablissements, montant.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Cessions de fonds de commerce sur la parcelle
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FondsCommerce'
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/permis:
    get:
      operationId: parcelle_permis
      x-title: "Parcelle Permis"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcellePermis
        method: apply
        params: [insee]
      summary: Autorisations d'urbanisme
      description: >
        Retourne les autorisations d'urbanisme (permis de construire, déclarations préalables,
        permis d'aménager, permis de démolir) associées à la parcelle, issues de la base
        Sitadel du Ministère de la Transition Écologique.
        La recherche combine le numéro de parcelle dans les dossiers et une zone tampon de 5 m.
        Résultat JSON avec clé `data` (tableau). Chaque entrée contient : numero, type, type_label,
        adresse, date_reelle_autorisation, etat_permis, destination_principale, nb_lgt_tot_crees.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Autorisations d'urbanisme liées à la parcelle
          content:
            application/json:
              example:
                data:
                  - numero: "03513312B0005"
                    type: "permis-amenager"
                    type_label: "Permis aménager"
                    adresse: null
                    date_reelle_autorisation: "2013-01-22 00:00:00"
                    etat_permis: "Autorisé"
                    destination_principale: null
                    nb_lgt_tot_crees: null
                    surf_hab_creee: null
                    commune_insee: "35133"
                    departement_insee: "35"
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/urbanisme-documents:
    get:
      operationId: parcelle_urbanisme_documents
      x-title: "Parcelle Urbanisme Documents"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleUrbanismeDocuments
        method: apply
        params: [insee]
      summary: Documents d'urbanisme
      description: >
        Retourne les documents d'urbanisme (PLU, PLUi, POS, CC, SCоT…) applicables
        à la géométrie de la parcelle, via le Géoportail de l'Urbanisme (GPU/Ministère).
        La réponse est mise en cache 1 heure côté serveur.
        Attention : cet endpoint interroge une API externe et peut être plus lent que les autres.

        La réponse est structurée en trois catégories :

        - `du` — Documents d'Urbanisme (PLU, PLUi, POS, CC…)
        - `psmv` — Plans de Sauvegarde et de Mise en Valeur
        - `scot` — Schémas de Cohérence Territoriale

        Chaque document contient : `id`, `name`, `approved_date` (date d'approbation),
        et `documents` (tableau de fichiers avec `url`, `filename`, `title`, `date`).
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Documents d'urbanisme structurés par catégorie
          content:
            application/json:
              schema:
                type: object
                properties:
                  du:
                    type: array
                    description: Documents d'Urbanisme (PLU, PLUi, POS…)
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                          nullable: true
                        approved_date:
                          type: string
                          format: date
                          nullable: true
                        documents:
                          type: array
                          items:
                            type: object
                            properties:
                              url:
                                type: string
                              filename:
                                type: string
                              title:
                                type: string
                              date:
                                type: string
                  psmv:
                    type: array
                    description: Plans de Sauvegarde et de Mise en Valeur
                    items:
                      type: object
                  scot:
                    type: array
                    description: Schémas de Cohérence Territoriale
                    items:
                      type: object
              example:
                du:
                  - id: "fr-35238-PLUi-20230615"
                    name: "35238_PLUi_20230615"
                    approved_date: "2023-06-15"
                    documents:
                      - url: "https://www.geoportail-urbanisme.gouv.fr/data/35238_plui_reglement_20230615.pdf"
                        filename: "35238_plui_reglement_20230615.pdf"
                        title: "Reglement"
                        date: "15/06/2023"
                      - url: "https://www.geoportail-urbanisme.gouv.fr/data/35238_plui_annexes_20230615.pdf"
                        filename: "35238_plui_annexes_20230615.pdf"
                        title: "Annexes"
                        date: "15/06/2023"
                psmv: []
                scot: []
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/urbanisme-zones:
    get:
      operationId: parcelle_urbanisme_zones
      x-title: "Parcelle Urbanisme Zones"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleUrbanismeZones
        method: apply
        params: [insee]
      summary: Zones d'urbanisme et de patrimoine
      description: >
        Retourne les zones réglementaires d'urbanisme et de patrimoine intersectant la parcelle,
        issues de bases officielles françaises (Géoportail de l'Urbanisme, Ministère de la Culture,
        Cartofriches/Cerema, Collectivité de Corse).

        La réponse est structurée par catégorie de zone. Chaque catégorie contient un
        tableau d'enregistrements (tableau vide si la parcelle n'est concernée par aucune zone
        de cette catégorie) :

        - `zonage_plu` — zonage du document d'urbanisme en vigueur (PLU, PLUi, POS, carte communale)
        - `monuments_historiques` — monuments historiques classés ou inscrits
        - `perimetres_protection` — périmètres de protection des abords d'un monument historique
        - `sites_patrimoniaux` — sites patrimoniaux remarquables (servitude AC4), incluant les anciennes
          ZPPAUP, AVAP et PSMV, absorbées par les SPR depuis la loi LCAP de 2016
        - `zppa` — zones de présomption de prescription archéologique (ZPPA) : toute demande d'urbanisme
          dans ces zones doit être transmise au service régional de l'archéologie
        - `friches` — friches industrielles et urbaines (Cartofriches)
        - `padduc` — zonage du Plan d'Aménagement et de Développement Durable de la Corse (PADDUC),
          uniquement pour les parcelles situées en Corse
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Zones réglementaires par catégorie
          content:
            application/json:
              schema:
                type: object
                properties:
                  zonage_plu:
                    type: array
                    items:
                      type: object
                  monuments_historiques:
                    type: array
                    items:
                      type: object
                  perimetres_protection:
                    type: array
                    items:
                      type: object
                  sites_patrimoniaux:
                    type: array
                    items:
                      type: object
                  zppa:
                    type: array
                    items:
                      type: object
                  friches:
                    type: array
                    items:
                      type: object
                  padduc:
                    type: array
                    items:
                      type: object
              example:
                zonage_plu:
                  - id: 45210
                    type: "PLU"
                    libelle: "UB"
                    libelong: "Zone urbaine à dominante d'habitat"
                    typezone: "U"
                    typesect: null
                    destdomi: "habitat"
                    datappro: "2019-06-27 00:00:00"
                    datvalid: "2019-06-27 00:00:00"
                    commune_insee: "35238"
                    epci_insee: "243500139"
                monuments_historiques: []
                perimetres_protection: []
                sites_patrimoniaux:
                  - id: 342
                    appelation: "Site patrimonial remarquable de Rennes"
                    type: "SPR"
                    legende: "ZPPAUP"
                    statut: "actif"
                    commune_insee: "35238"
                    departement_insee: "35"
                zppa:
                  - id: 8696
                    protection_code: "14172"
                    protection_libelle: "Zone de saisine (décret 2002-89)"
                    protection_objet: "Villa gallo-romaine de La Tasque"
                    protection_date: null
                    commune_insee: "35238"
                    departement_insee: "35"
                friches:
                  - id: 12034
                    site_id: "FR-35238-0007"
                    site_nom: "Ancienne friche industrielle"
                    site_type: "Friche industrielle"
                    site_statut: "En friche"
                    site_surface: 4820.5
                    urba_zone_type: "AUc"
                    urba_zone_lib: "Zone à urbaniser"
                    commune_insee: "35238"
                    departement_insee: "35"
                padduc: []
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/taxe-amenagement:
    get:
      operationId: parcelle_taxe_amenagement
      x-title: "Parcelle Taxe d'Aménagement"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleTaxeAmenagement
        method: apply
        params: [insee]
      summary: Taxe d'aménagement
      description: >
        Retourne les taux de taxe d'aménagement applicables à la parcelle, aux quatre
        niveaux administratifs, issus des données publiées par les collectivités
        (Direction Générale des Finances Publiques).

        La réponse contient quatre clés, chacune pouvant être `null` si le taux
        n'est pas disponible pour cette parcelle :

        - `communale` — taux communal standard (s'applique à toutes les parcelles de la commune)
        - `communale_majoree` — taux communal majoré spécifique à cette parcelle (secteur particulier)
        - `departementale` — taux départemental
        - `regionale` — taux régional (Île-de-France uniquement)

        Chaque entrée contient : `zone_application`, `taux` (en %), `date_effet`,
        `commune_insee`, `parcelle_insee`.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Taux de taxe d'aménagement par niveau administratif
          content:
            application/json:
              schema:
                type: object
                properties:
                  communale:
                    nullable: true
                    $ref: '#/components/schemas/TaxeAmenagement'
                  communale_majoree:
                    nullable: true
                    $ref: '#/components/schemas/TaxeAmenagement'
                  departementale:
                    nullable: true
                    $ref: '#/components/schemas/TaxeAmenagement'
                  regionale:
                    nullable: true
                    $ref: '#/components/schemas/TaxeAmenagement'
              example:
                communale:
                  zone_application: "Communale"
                  taux: 1.5
                  date_effet: "2025-01-01"
                  commune_insee: "22174"
                  parcelle_insee: null
                communale_majoree: null
                departementale:
                  zone_application: "Départementale"
                  taux: 1.8
                  date_effet: "2025-01-01"
                  commune_insee: null
                  parcelle_insee: null
                regionale: null
        "404":
          description: Parcelle introuvable

  /parcelle/{insee}/erp:
    get:
      operationId: parcelle_erp
      x-title: "Parcelle ERP"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleErp
        method: apply
        params: [insee]
      summary: Rapport État des Risques et Pollutions (ERP)
      description: >
        Retourne le rapport ERP (État des Risques et Pollutions) au format PDF, généré
        par le service Géorisques du Ministère de la Transition Écologique à partir
        des coordonnées du centroïde de la parcelle.

        Ce rapport est obligatoire lors de toute vente ou location d'un bien immobilier
        en France. Il synthétise les risques naturels, miniers, technologiques et la pollution
        des sols applicables à l'adresse.

        **Réponse :** fichier PDF binaire (`application/pdf`) ou erreur 422 si le service
        Géorisques est temporairement indisponible.
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Rapport ERP au format PDF
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "404":
          description: Parcelle introuvable
        "422":
          description: Service Géorisques temporairement indisponible

  /parcelle/{insee}/risques:
    get:
      operationId: parcelle_risques
      x-title: "Parcelle Risques"
      x-handler:
        class: App\Http\Requests\Api\Parcelle\ParcelleRisques
        method: apply
        params: [insee]
      summary: Risques naturels et industriels applicables
      description: >
        Retourne les données de risques naturels et industriels intersectant la parcelle
        (ou à proximité immédiate pour les risques ponctuels), issues des bases de données
        officielles françaises (Géorisques, BRGM, DREAL…).

        La réponse est structurée par catégorie de risque. Chaque catégorie contient un
        tableau d'enregistrements (tableau vide si aucun risque dans cette catégorie) :

        - `inondations` — zones de risque inondation (SYRTHERIS/EPRI)
        - `argiles` — zones de retrait-gonflement des argiles (BRGM), avec champ `alea` (Faible/Moyen/Fort)
        - `mouvements_terrain` — mouvements de terrain recensés (BRGM)
        - `remontees_nappes` — zones à risque de remontée de nappe phréatique (BRGM)
        - `cavites_souterraines` — cavités souterraines dans un rayon de 200 m (BRGM)
        - `pprn` — Plans de Prévention des Risques Naturels applicables à la commune
        - `pprt` — Plans de Prévention des Risques Technologiques applicables à la commune
        - `icpe` — Installations Classées pour la Protection de l'Environnement dans un rayon de 500 m
        - `sis` — Secteurs d'Information sur les Sols (terrains potentiellement pollués)
      tags:
        - Parcelles
      parameters:
        - name: insee
          in: path
          required: true
          description: Code INSEE cadastral de la parcelle
          schema:
            type: string
            pattern: '^\d{5}\d{3}[A-Z]{1,2}\d{4}$'
            example: "35238000BE0329"
      responses:
        "200":
          description: Données de risques par catégorie
          content:
            application/json:
              schema:
                type: object
                properties:
                  inondations:
                    type: array
                    items:
                      type: object
                  argiles:
                    type: array
                    items:
                      type: object
                  mouvements_terrain:
                    type: array
                    items:
                      type: object
                  remontees_nappes:
                    type: array
                    items:
                      type: object
                  cavites_souterraines:
                    type: array
                    items:
                      type: object
                  pprn:
                    type: array
                    items:
                      type: object
                  pprt:
                    type: array
                    items:
                      type: object
                  icpe:
                    type: array
                    items:
                      type: object
                  sis:
                    type: array
                    items:
                      type: object
              example:
                inondations:
                  - id: 567976
                    type: "surface_inondable"
                    typ_inond: "01"
                    scenario: "02Moy"
                    nom: null
                    cours_deau: "La Seine"
                    est_ref: 1
                argiles:
                  - id: 193423
                    alea: "Fort"
                    niveau: 3
                mouvements_terrain:
                  - id: 1
                    type_mvt: "2"
                    libelle_type: "Chute de blocs / Eboulement"
                    date_debut: "1987-12-10 00:00:00"
                    lieu_dit: "CD 936"
                    commune: "DORTAN"
                remontees_nappes:
                  - id: 899568
                    classe: "Pas de débordement de nappe ni d'inondation de cave"
                cavites_souterraines:
                  - id: 1
                    nature_cavite: "orifice naturel"
                    type_cavite: "naturelle"
                    nom_cavite: "Abîme aux Nables"
                    dangerosite: null
                    date_validite: "2003-09-25 00:00:00"
                pprn: []
                pprt: []
                icpe:
                  - id: 151128
                    nom_ets: "NAZART"
                    adresse: null
                    commune: "Fougères"
                    code_postal: "35300"
                    cd_regime: "E"
                    lib_regime: "Enregistrement"
                    seveso: "3"
                    lib_seveso: "Non Seveso"
                    url_fiche: "https://www.georisques.gouv.fr/risques/installations/donnees/details/0005501412"
                sis:
                  - id: 5520
                    code_metier: "SSP00069240101"
                    nom_usuel: "Ancienne décharge Prairie des Mauves"
                    adresse: "PRAIRIE DES MAUVES"
        "404":
          description: Parcelle introuvable

  /{zoneType}/{zoneId}/dvf:
    get:
      operationId: zone_dvf
      x-title: "Zone DVF"
      x-handler:
        class: App\Http\Requests\Api\ZoneData\ZoneDvf
        method: apply
        params: [zoneType, zoneId]
      summary: Mutations immobilières (DVF)
      description: >
        Retourne la liste paginée des mutations immobilières (DVF) dans la zone géographique
        spécifiée, issues de la base Demandes de Valeurs Foncières (DGFiP).

        **Types de zone supportés :**

        - `commune` — commune française identifiée par son code INSEE (5 chiffres)
        - `departement` — département identifié par son code INSEE (2 ou 3 caractères)
        - `iris` — IRIS identifié par son code INSEE (9 chiffres)
        - `secteur` — polygone personnalisé de l'utilisateur (UUID)

        Les résultats sont paginés (20 par défaut, max 20). La réponse suit l'enveloppe
        de pagination Laravel : clés `data` (tableau), `links` (navigation) et `meta` (totaux).
        Les mutations sont triées par date décroissante.
      tags:
        - Zones — Données
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: type_vente
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur la nature de la mutation (tableau).
            Par défaut toutes les natures sont incluses.
          schema:
            type: array
            items:
              type: string
              enum: [vente, vefa, adjudication, echange]
        - name: typologies
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le type de bien (tableau).
            `11` — maison, `12` — appartement, `14` — local d'activité,
            `1` — autre bâti, `2` — terrain.
          schema:
            type: array
            items:
              type: string
              enum: ["1", "2", "11", "12", "14"]
        - name: date_start
          in: query
          required: false
          description: Date de début de la période de filtrage (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2022-01-01"
        - name: date_end
          in: query
          required: false
          description: Date de fin de la période de filtrage (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2024-12-31"
        - name: pieces_min
          in: query
          required: false
          description: Nombre de pièces minimum
          schema:
            type: integer
            example: 3
        - name: pieces_max
          in: query
          required: false
          description: Nombre de pièces maximum
          schema:
            type: integer
            example: 5
        - name: montant_min
          in: query
          required: false
          description: Valeur foncière minimale en euros
          schema:
            type: integer
            example: 100000
        - name: montant_max
          in: query
          required: false
          description: Valeur foncière maximale en euros
          schema:
            type: integer
            example: 500000
        - name: surface_bati_min
          in: query
          required: false
          description: Surface bâtie minimale en m²
          schema:
            type: integer
            example: 50
        - name: surface_bati_max
          in: query
          required: false
          description: Surface bâtie maximale en m²
          schema:
            type: integer
            example: 150
        - name: surface_terrain_min
          in: query
          required: false
          description: Surface terrain minimale en m²
          schema:
            type: integer
            example: 200
        - name: surface_terrain_max
          in: query
          required: false
          description: Surface terrain maximale en m²
          schema:
            type: integer
            example: 1000
      responses:
        "200":
          description: Liste paginée des mutations DVF
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Mutation'
                  links:
                    type: object
                    properties:
                      first:
                        type: string
                        nullable: true
                        example: "/api/v1/commune/35238/dvf?page=1"
                      last:
                        type: string
                        nullable: true
                        example: "/api/v1/commune/35238/dvf?page=12"
                      prev:
                        type: string
                        nullable: true
                        example: null
                      next:
                        type: string
                        nullable: true
                        example: "/api/v1/commune/35238/dvf?page=2"
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      from:
                        type: integer
                        nullable: true
                        example: 1
                      last_page:
                        type: integer
                        example: 12
                      per_page:
                        type: integer
                        example: 50
                      to:
                        type: integer
                        nullable: true
                        example: 50
                      total:
                        type: integer
                        example: 587
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /{zoneType}/{zoneId}/dvf/stats:
    get:
      operationId: zone_dvf_stats
      x-title: "Zone DVF — Statistiques"
      x-handler:
        class: App\Http\Requests\Api\ZoneStats\ZoneDvfStats
        method: apply
        params: [zoneType, zoneId]
      summary: Statistiques DVF par période
      description: >
        Retourne les statistiques des mutations immobilières (DVF) dans la zone géographique
        spécifiée, agrégées par pas de temps (mois, trimestre ou année) et par catégorie de bien.

        Quatre catégories sont toujours retournées : `maison`, `appartement`, `logement_neuf`
        (VEFA) et `activite`.

        Pour chaque période et chaque catégorie, les indicateurs calculés sont :
        nombre de mutations (`count`), prix moyen/médian/min/max (`prix_*`),
        surfaces bâties et terrain (`surface_bati_*`, `surface_terr_*`),
        et prix au m² (`prix_m2_bati_*`, `prix_m2_terr_*`).

        Les données couvrent l'historique DVF disponible depuis 2018.
      tags:
        - Zones — Statistiques
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - name: step
          in: query
          required: false
          description: Pas de temps pour l'agrégation (défaut year)
          schema:
            type: string
            enum: [month, quarter, year]
            default: year
            example: "year"
        - name: date_start
          in: query
          required: false
          description: >
            Date de début de la plage temporelle analysée (format YYYY-MM-DD).
            Par défaut : début de l'historique DVF disponible.
          schema:
            type: string
            format: date
            example: "2022-01-01"
        - name: date_end
          in: query
          required: false
          description: >
            Date de fin de la plage temporelle analysée (format YYYY-MM-DD).
            Par défaut : aujourd'hui.
          schema:
            type: string
            format: date
            example: "2024-12-31"
        - name: typologies
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le type de bien (tableau). Chaque valeur est un nom de catégorie
            qui correspond à une combinaison de codes DVF internes :
            `maison` → codtypbien 11,
            `appartement` → codtypbien 12,
            `logement_neuf` → codtypbien 10/11/12 (VEFA),
            `activite` → codtypbien 14.
            Par défaut tous les types sont inclus.
          schema:
            type: array
            items:
              type: string
              enum: [maison, appartement, logement_neuf, activite]
        - name: type_vente
          in: query
          required: false
          style: form
          explode: true
          description: >
            Nature des mutations à inclure dans les statistiques (tableau).
            Par défaut chaque catégorie utilise sa propre nature (`vente` pour
            maison/appartement/activite, `vefa` pour logement_neuf).
            Lorsque ce paramètre est fourni, il s'applique à toutes les catégories sélectionnées.
          schema:
            type: array
            items:
              type: string
              enum: [vente, vefa, adjudication, echange]
        - name: surface_bati_min
          in: query
          required: false
          description: Surface bâtie minimale en m²
          schema:
            type: integer
            example: 50
        - name: surface_bati_max
          in: query
          required: false
          description: Surface bâtie maximale en m²
          schema:
            type: integer
            example: 150
        - name: pieces_min
          in: query
          required: false
          description: Nombre de pièces minimum
          schema:
            type: integer
            example: 3
        - name: pieces_max
          in: query
          required: false
          description: Nombre de pièces maximum
          schema:
            type: integer
            example: 5
      responses:
        "200":
          description: Statistiques DVF agrégées par période et par catégorie de bien
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DvfStatsPeriod'
              example:
                - date: "2023"
                  date_start: "2023-01-01"
                  date_end: "2023-12-31"
                  count: 112
                  prix_avg: 195000
                  prix_med: 178000
                  prix_min: 45000
                  prix_max: 620000
                  surface_bati_avg: 58
                  surface_bati_med: 55
                  surface_bati_min: 18
                  surface_bati_max: 180
                  surface_terr_avg: 0
                  surface_terr_med: 0
                  surface_terr_min: 0
                  surface_terr_max: 0
                  prix_m2_bati_avg: 3350
                  prix_m2_bati_med: 3180
                  prix_m2_bati_min: 1200
                  prix_m2_bati_max: 8500
                  prix_m2_terr_avg: 0
                  prix_m2_terr_med: 0
                  prix_m2_terr_min: 0
                  prix_m2_terr_max: 0
                - date: "2024"
                  date_start: "2024-01-01"
                  date_end: "2024-12-31"
                  count: 98
                  prix_avg: 202000
                  prix_med: 185000
                  prix_min: 52000
                  prix_max: 640000
                  surface_bati_avg: 60
                  surface_bati_med: 57
                  surface_bati_min: 20
                  surface_bati_max: 190
                  surface_terr_avg: 0
                  surface_terr_med: 0
                  surface_terr_min: 0
                  surface_terr_max: 0
                  prix_m2_bati_avg: 3420
                  prix_m2_bati_med: 3250
                  prix_m2_bati_min: 1300
                  prix_m2_bati_max: 8800
                  prix_m2_terr_avg: 0
                  prix_m2_terr_med: 0
                  prix_m2_terr_min: 0
                  prix_m2_terr_max: 0
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /{zoneType}/{zoneId}/dpe:
    get:
      operationId: zone_dpe
      x-title: "Zone DPE"
      x-handler:
        class: App\Http\Requests\Api\ZoneData\ZoneDpe
        method: apply
        params: [zoneType, zoneId]
      summary: DPE
      description: >
        Retourne la liste paginée des Diagnostics de Performance Énergétique (DPE) dans
        la zone géographique spécifiée, issus de la base ADEME.

        **Types de zone supportés :**

        - `commune` — commune française identifiée par son code INSEE (5 chiffres)
        - `departement` — département identifié par son code INSEE (2 ou 3 caractères)
        - `iris` — IRIS identifié par son code INSEE (9 chiffres)
        - `secteur` — polygone personnalisé de l'utilisateur (UUID)

        Les résultats sont paginés (20 par défaut, max 20) et triés par date d'établissement
        décroissante. Les données couvrent les DPE établis depuis juillet 2021 (format DPE V3).
      tags:
        - Zones — Données
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: type
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le type de bien (tableau).
            `logement_neuf` — logement soumis au DPE neuf,
            `logement_existant` — logement existant,
            `tertiaire` — bâtiment tertiaire.
            Par défaut tous les types sont inclus.
          schema:
            type: array
            items:
              type: string
              enum: [logement_neuf, logement_existant, tertiaire]
        - name: type_batiment
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le type de bâtiment (tableau, valeurs libres issues de la base ADEME).
            Exemples : `appartement`, `maison`.
          schema:
            type: array
            items:
              type: string
        - name: chauffage
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le type d'installation de chauffage (tableau).
            `individuel`, `collectif`, `mixte`.
          schema:
            type: array
            items:
              type: string
              enum: [individuel, collectif, mixte]
        - name: etiquette_min
          in: query
          required: false
          description: Classe DPE minimale (A = meilleure, G = pire)
          schema:
            type: string
            enum: [A, B, C, D, E, F, G]
            example: "A"
        - name: etiquette_max
          in: query
          required: false
          description: Classe DPE maximale (A = meilleure, G = pire)
          schema:
            type: string
            enum: [A, B, C, D, E, F, G]
            example: "D"
        - name: date_start
          in: query
          required: false
          description: Date d'établissement minimale du DPE (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2022-01-01"
        - name: date_end
          in: query
          required: false
          description: Date d'établissement maximale du DPE (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2024-12-31"
      responses:
        "200":
          description: Liste paginée des DPE
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Dpe'
                  links:
                    type: object
                    properties:
                      first:
                        type: string
                        nullable: true
                        example: "/api/v1/commune/35238/dpe?page=1"
                      last:
                        type: string
                        nullable: true
                        example: "/api/v1/commune/35238/dpe?page=5"
                      prev:
                        type: string
                        nullable: true
                        example: null
                      next:
                        type: string
                        nullable: true
                        example: "/api/v1/commune/35238/dpe?page=2"
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      from:
                        type: integer
                        nullable: true
                        example: 1
                      last_page:
                        type: integer
                        example: 5
                      per_page:
                        type: integer
                        example: 20
                      to:
                        type: integer
                        nullable: true
                        example: 20
                      total:
                        type: integer
                        example: 94
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /{zoneType}/{zoneId}/dpe/stats:
    get:
      operationId: zone_dpe_stats
      x-title: "Zone DPE — Statistiques"
      x-handler:
        class: App\Http\Requests\Api\ZoneStats\ZoneDpeStats
        method: apply
        params: [zoneType, zoneId]
      summary: Statistiques DPE par période
      description: >
        Retourne les statistiques des DPE dans la zone géographique spécifiée,
        agrégées par pas de temps (mois, trimestre ou année).

        Pour chaque période, la réponse inclut le nombre total de DPE établis
        (`count`) ainsi que la répartition par classe énergétique
        (`count_a` à `count_g`).

        Les données couvrent les DPE établis depuis juillet 2021 (format DPE V3).
      tags:
        - Zones — Statistiques
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - name: step
          in: query
          required: false
          description: Pas de temps pour l'agrégation (défaut year)
          schema:
            type: string
            enum: [month, quarter, year]
            default: year
            example: "year"
        - name: date_start
          in: query
          required: false
          description: >
            Date de début de la plage temporelle analysée (format YYYY-MM-DD).
            Par défaut : 2021-07-01 (début du DPE V3).
          schema:
            type: string
            format: date
            example: "2022-01-01"
        - name: date_end
          in: query
          required: false
          description: >
            Date de fin de la plage temporelle analysée (format YYYY-MM-DD).
            Par défaut : aujourd'hui.
          schema:
            type: string
            format: date
            example: "2024-12-31"
        - name: type
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le type de bien (tableau).
            `logement_neuf`, `logement_existant`, `tertiaire`.
          schema:
            type: array
            items:
              type: string
              enum: [logement_neuf, logement_existant, tertiaire]
        - name: type_batiment
          in: query
          required: false
          style: form
          explode: true
          description: Filtre sur le type de bâtiment (tableau, valeurs libres)
          schema:
            type: array
            items:
              type: string
        - name: chauffage
          in: query
          required: false
          style: form
          explode: true
          description: Filtre sur le type d'installation de chauffage
          schema:
            type: array
            items:
              type: string
              enum: [individuel, collectif, mixte]
        - name: etiquette_min
          in: query
          required: false
          description: Classe DPE minimale
          schema:
            type: string
            enum: [A, B, C, D, E, F, G]
        - name: etiquette_max
          in: query
          required: false
          description: Classe DPE maximale
          schema:
            type: string
            enum: [A, B, C, D, E, F, G]
      responses:
        "200":
          description: Statistiques DPE agrégées par période
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DpeStatsPeriod'
              example:
                - date: "2023"
                  date_start: "2023-01-01"
                  date_end: "2023-12-31"
                  count: 1547
                  count_a: 45
                  count_b: 123
                  count_c: 312
                  count_d: 489
                  count_e: 378
                  count_f: 145
                  count_g: 55
                - date: "2024"
                  date_start: "2024-01-01"
                  date_end: "2024-12-31"
                  count: 1802
                  count_a: 68
                  count_b: 187
                  count_c: 391
                  count_d: 542
                  count_e: 410
                  count_f: 148
                  count_g: 56
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /{zoneType}/{zoneId}/friches:
    get:
      operationId: zone_friches
      x-title: "Zone Friches"
      x-handler:
        class: App\Http\Requests\Api\ZoneData\ZoneFriches
        method: apply
        params: [zoneType, zoneId]
      summary: Friches industrielles et urbaines
      description: >
        Retourne la liste paginée des friches (sites industriels ou urbains délaissés)
        dans la zone géographique spécifiée, issues de la base Cartofriches (Cerema).

        **Types de zone supportés :**

        - `commune` — commune française identifiée par son code INSEE (5 chiffres)
        - `departement` — département identifié par son code INSEE (2 ou 3 caractères)
        - `iris` — IRIS identifié par son code INSEE (9 chiffres)
        - `secteur` — polygone personnalisé de l'utilisateur (UUID)

        Les résultats sont paginés (20 par défaut, max 20).
      tags:
        - Zones — Données
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        "200":
          description: Liste paginée des friches
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Friche'
                  links:
                    type: object
                    properties:
                      first: { type: string, nullable: true, example: "/api/v1/commune/35238/friches?page=1" }
                      last: { type: string, nullable: true, example: "/api/v1/commune/35238/friches?page=1" }
                      prev: { type: string, nullable: true, example: null }
                      next: { type: string, nullable: true, example: null }
                  meta:
                    type: object
                    properties:
                      current_page: { type: integer, example: 1 }
                      from: { type: integer, nullable: true, example: 1 }
                      last_page: { type: integer, example: 1 }
                      per_page: { type: integer, example: 20 }
                      to: { type: integer, nullable: true, example: 3 }
                      total: { type: integer, example: 3 }
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /{zoneType}/{zoneId}/permis:
    get:
      operationId: zone_permis
      x-title: "Zone Permis"
      x-handler:
        class: App\Http\Requests\Api\ZoneData\ZonePermis
        method: apply
        params: [zoneType, zoneId]
      summary: Autorisations d'urbanisme
      description: >
        Retourne la liste paginée des autorisations d'urbanisme (permis de construire,
        déclarations préalables, permis d'aménager, permis de démolir) dans la zone
        géographique spécifiée, issues de la base Sitadel du Ministère de la Transition
        Écologique.

        **Types de zone supportés :**

        - `commune` — commune française identifiée par son code INSEE (5 chiffres)
        - `departement` — département identifié par son code INSEE (2 ou 3 caractères)
        - `iris` — IRIS identifié par son code INSEE (9 chiffres)
        - `secteur` — polygone personnalisé de l'utilisateur (UUID)

        Les résultats sont paginés (20 par défaut, max 20), triés par date d'autorisation
        décroissante.
      tags:
        - Zones — Données
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: type
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le type d'autorisation (tableau). Par défaut tous les types
            sont inclus.
          schema:
            type: array
            items:
              type: string
              enum: [permis-construire, declaration-prealable, permis-amenager, permis-demolir]
        - name: statut
          in: query
          required: false
          style: form
          explode: true
          description: >
            Filtre sur le statut du dossier (tableau). Par défaut tous les statuts
            sont inclus.
          schema:
            type: array
            items:
              type: string
              enum: [autorise, annule, commence, termine]
        - name: demandeur
          in: query
          required: false
          description: >
            Recherche sur la dénomination du demandeur (insensible à la casse,
            sous-chaîne recherchée n'importe où dans le champ). Utile par exemple
            pour retrouver tous les permis déposés par un promoteur donné.
          schema:
            type: string
            example: "lorimmo"
        - name: date_acceptation_min
          in: query
          required: false
          description: Date d'acceptation (autorisation) minimum du permis (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2020-01-01"
        - name: date_acceptation_max
          in: query
          required: false
          description: Date d'acceptation (autorisation) maximum du permis (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2022-12-31"
        - name: annee_depot_min
          in: query
          required: false
          description: Année de dépôt du dossier minimum
          schema:
            type: integer
            minimum: 1900
            example: 2020
        - name: annee_depot_max
          in: query
          required: false
          description: Année de dépôt du dossier maximum
          schema:
            type: integer
            minimum: 1900
            example: 2022
        - name: piscine
          in: query
          required: false
          description: >
            Filtre les permis de construire avec piscine. Uniquement disponible pour
            les permis de construire (champ absent des permis d'aménager/démolir) :
            activer ce filtre exclut donc ces deux types de la recherche.
          schema:
            type: boolean
        - name: extension
          in: query
          required: false
          description: Filtre les permis de construire avec extension (voir note sur `piscine`).
          schema:
            type: boolean
        - name: surelevation
          in: query
          required: false
          description: Filtre les permis de construire avec surélévation (voir note sur `piscine`).
          schema:
            type: boolean
        - name: nivsupp
          in: query
          required: false
          description: Filtre les permis de construire avec niveau supplémentaire (voir note sur `piscine`).
          schema:
            type: boolean
        - name: garage
          in: query
          required: false
          description: Filtre les permis de construire avec garage (voir note sur `piscine`).
          schema:
            type: boolean
        - name: veranda
          in: query
          required: false
          description: Filtre les permis de construire avec véranda (voir note sur `piscine`).
          schema:
            type: boolean
        - name: abri_jardin
          in: query
          required: false
          description: Filtre les permis de construire avec abri de jardin (voir note sur `piscine`).
          schema:
            type: boolean
        - name: autre_annexe
          in: query
          required: false
          description: Filtre les permis de construire avec une autre annexe (voir note sur `piscine`).
          schema:
            type: boolean
      responses:
        "200":
          description: Liste paginée des autorisations d'urbanisme
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Permis'
                  links:
                    type: object
                    properties:
                      first: { type: string, nullable: true, example: "/api/v1/commune/35238/permis?page=1" }
                      last: { type: string, nullable: true, example: "/api/v1/commune/35238/permis?page=1" }
                      prev: { type: string, nullable: true, example: null }
                      next: { type: string, nullable: true, example: null }
                  meta:
                    type: object
                    properties:
                      current_page: { type: integer, example: 1 }
                      from: { type: integer, nullable: true, example: 1 }
                      last_page: { type: integer, example: 1 }
                      per_page: { type: integer, example: 20 }
                      to: { type: integer, nullable: true, example: 3 }
                      total: { type: integer, example: 3 }
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /{zoneType}/{zoneId}/fonds-commerce:
    get:
      operationId: zone_fonds_commerce
      x-title: "Zone Fonds de Commerce"
      x-handler:
        class: App\Http\Requests\Api\ZoneData\ZoneFondsCommerce
        method: apply
        params: [zoneType, zoneId]
      summary: Cessions de fonds de commerce
      description: >
        Retourne la liste paginée des cessions de fonds de commerce publiées au BODACC
        (Bulletin Officiel des Annonces Civiles et Commerciales) dans la zone géographique
        spécifiée, issues d'Etalab/data.gouv.fr.

        **Types de zone supportés :**

        - `commune` — commune française identifiée par son code INSEE (5 chiffres)
        - `departement` — département identifié par son code INSEE (2 ou 3 caractères)
        - `iris` — IRIS identifié par son code INSEE (9 chiffres)
        - `secteur` — polygone personnalisé de l'utilisateur (UUID)

        Les résultats sont paginés (20 par défaut, max 20), triés par date de parution
        décroissante.
      tags:
        - Zones — Données
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: date_min
          in: query
          required: false
          description: Date de parution minimum (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2020-01-01"
        - name: date_max
          in: query
          required: false
          description: Date de parution maximum (YYYY-MM-DD)
          schema:
            type: string
            format: date
            example: "2024-12-31"
        - name: montant_min
          in: query
          required: false
          description: Montant de cession minimum (euros)
          schema:
            type: number
            minimum: 0
            example: 10000
        - name: montant_max
          in: query
          required: false
          description: Montant de cession maximum (euros)
          schema:
            type: number
            minimum: 0
            example: 200000
      responses:
        "200":
          description: Liste paginée des cessions de fonds de commerce
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FondsCommerce'
                  links:
                    type: object
                    properties:
                      first: { type: string, nullable: true, example: "/api/v1/commune/35238/fonds-commerce?page=1" }
                      last: { type: string, nullable: true, example: "/api/v1/commune/35238/fonds-commerce?page=1" }
                      prev: { type: string, nullable: true, example: null }
                      next: { type: string, nullable: true, example: null }
                  meta:
                    type: object
                    properties:
                      current_page: { type: integer, example: 1 }
                      from: { type: integer, nullable: true, example: 1 }
                      last_page: { type: integer, example: 1 }
                      per_page: { type: integer, example: 20 }
                      to: { type: integer, nullable: true, example: 3 }
                      total: { type: integer, example: 3 }
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /{zoneType}/{zoneId}/coproprietes:
    get:
      operationId: zone_coproprietes
      x-title: "Zone Copropriétés"
      x-handler:
        class: App\Http\Requests\Api\ZoneData\ZoneCoproprietes
        method: apply
        params: [zoneType, zoneId]
      summary: Copropriétés
      description: >
        Retourne la liste paginée des copropriétés issues du Registre National des
        Copropriétés (RNC/ANAH) dans la zone géographique spécifiée.

        **Types de zone supportés :**

        - `commune` — commune française identifiée par son code INSEE (5 chiffres)
        - `departement` — département identifié par son code INSEE (2 ou 3 caractères)
        - `iris` — IRIS identifié par son code INSEE (9 chiffres)
        - `secteur` — polygone personnalisé de l'utilisateur (UUID)

        Les résultats sont paginés (20 par défaut, max 20).
      tags:
        - Zones — Données
      parameters:
        - $ref: '#/components/parameters/ZoneType'
        - $ref: '#/components/parameters/ZoneId'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: annee_construction_min
          in: query
          required: false
          description: >
            Année de construction minimum. La donnée source ne stocke qu'une tranche
            (`periode_construction`, ex. DE_1975_A_1993) et non une année exacte : le
            filtre inclut donc toute tranche chevauchant l'année demandée. Les copropriétés
            de tranche inconnue (`NON_CONNUE`) sont exclues dès qu'un filtre d'année est actif.
          schema:
            type: integer
            minimum: 1800
            example: 1975
        - name: annee_construction_max
          in: query
          required: false
          description: >
            Année de construction maximum (même logique de chevauchement par tranche
            que `annee_construction_min`).
          schema:
            type: integer
            minimum: 1800
            example: 2000
        - name: lots_min
          in: query
          required: false
          description: Nombre de lots total minimum de la copropriété
          schema:
            type: integer
            minimum: 0
            example: 10
      responses:
        "200":
          description: Liste paginée des copropriétés
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Copropriete'
                  links:
                    type: object
                    properties:
                      first: { type: string, nullable: true, example: "/api/v1/commune/35238/coproprietes?page=1" }
                      last: { type: string, nullable: true, example: "/api/v1/commune/35238/coproprietes?page=1" }
                      prev: { type: string, nullable: true, example: null }
                      next: { type: string, nullable: true, example: null }
                  meta:
                    type: object
                    properties:
                      current_page: { type: integer, example: 1 }
                      from: { type: integer, nullable: true, example: 1 }
                      last_page: { type: integer, example: 1 }
                      per_page: { type: integer, example: 20 }
                      to: { type: integer, nullable: true, example: 3 }
                      total: { type: integer, example: 3 }
        "404":
          description: Zone introuvable ou identifiant invalide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "422":
          description: Type de zone non supporté ou paramètres invalides
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
