openapi: 3.0.3
info:
  title: Hypothèques en ligne — API Documents Fonciers
  version: "1.0.0"
  description: |
    API REST pour automatiser les commandes de documents fonciers — états hypothécaires,
    copies d'actes notariés, documents de copropriété — depuis vos propres systèmes.

    ## Authentification

    L'authentification s'effectue via **OAuth 2.0 JWT Bearer** :
    1. Générez et signez un JWT côté client avec votre certificat.
    2. Échangez-le contre un `access_token` via `POST /oauth`.
    3. Utilisez le token dans le header `Authorization: Bearer <access_token>` pour chaque requête.

    ## Référentiel des communes

    Les paramètres `localisation_bien`, `lieu_naissance` et `code_postal_ville` doivent correspondre
    au référentiel Hypothèques en ligne (format `"CP VILLE"`, ex : `"01000 BOURG-EN-BRESSE"`).
    Ce référentiel est communiqué au partenaire lors de la mise en place de l'intégration.

    ## Notes techniques

    - **Encodage** : UTF-8. Les caractères accentués sont acceptés.
    - **Timeout recommandé** : 10 secondes (temps de réponse constaté : 1 à 3 secondes).
    - **Toutes les réponses** retournent un code HTTP `200`. Le champ `success` indique le résultat.
  contact:
    name: Hypothèques en ligne — Support
    email: contact@hypotheques-en-ligne.fr
    url: https://www.hypotheques-en-ligne.fr/api-clients-partenaires/

servers:
  - url: https://api.hypotheques-en-ligne.fr
    description: Production
  - url: https://api-sandbox.hypotheques-en-ligne.fr
    description: Sandbox (tests)

security:
  - bearerAuth: []

tags:
  - name: Authentification
    description: Obtention d'un access token
  - name: État hypothécaire
    description: Commandes d'états hypothécaires (cadastrale, identité, combinée)
  - name: Copie d'acte
    description: Commandes de copies d'actes notariés
  - name: Document de copropriété
    description: Commandes de règlements de copropriété et états descriptifs de division
  - name: Suivi
    description: Suivi et récupération des commandes

paths:
  # ================================================================
  # AUTHENTIFICATION
  # ================================================================
  /oauth:
    post:
      tags: [Authentification]
      summary: Obtenir un access token (JWT Bearer)
      description: |
        Échange un JWT signé côté client contre un `access_token`
        utilisable sur l'ensemble des endpoints métier.
      operationId: authenticate
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [grant_type, assertion]
              properties:
                grant_type:
                  type: string
                  enum: ["urn:ietf:params:oauth:grant-type:jwt-bearer"]
                  description: Type de grant OAuth.
                assertion:
                  type: string
                  description: JWT signé avec la clé privée du partenaire.
            example:
              grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer"
              assertion: "<JWT_signé_côté_client>"
      responses:
        "200":
          description: Token obtenu avec succès.
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                    description: Token d'accès à utiliser dans le header Authorization.
                    example: "00Dxx0000000001!AQcAQ..."
                  token_type:
                    type: string
                    enum: [Bearer]
                    example: Bearer

  # ================================================================
  # ÉTAT HYPOTHÉCAIRE MULTI-ENTRÉES
  # ================================================================
  /v1/commandes/ehf-multi:
    post:
      tags: [État hypothécaire]
      summary: Créer une commande d'état hypothécaire
      description: |
        Endpoint unifié pour commander un état hypothécaire selon trois modes de recherche :

        - **Cadastrale** : à partir de références de parcelles (max 12) ou, en copropriété,
          d'assises cadastrales (max 5) et de lots (max 12).
        - **Identité** : à partir de personnes physiques ou morales (max 3 au total).
        - **Combinée** : croisement de parcelles et de personnes, en mode exhaustif
          (union des résultats) ou ciblé (intersection).

        **Restriction Alsace-Moselle** : pour les départements 57, 67 et 68 (livre foncier),
        seule la recherche `cadastrale` est autorisée.

        **Calcul des débours** : les frais de réquisition sont calculés automatiquement
        et retournés dans le champ `debours` de la réponse.
      operationId: createCommandeEhfMulti
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EhfMultiRequest"
            examples:
              cadastrale_simple:
                summary: Cadastrale — 1 parcelle (requête minimale)
                value:
                  numero_commande_partenaire: "CMD-2026-0001"
                  localisation_bien: "01000 BOURG-EN-BRESSE"
                  type_recherche: "cadastrale"
                  parcelles:
                    - section_cadastrale: "BJ"
                      numero_parcelle: "42"
              cadastrale_multi_filtre_analyse:
                summary: Cadastrale — 3 parcelles + filtre + analyse Intellig'IA
                value:
                  numero_commande_partenaire: "CMD-2026-0002"
                  reference_partenaire: "DOSSIER-ABC"
                  localisation_bien: "01100 OYONNAX"
                  type_recherche: "cadastrale"
                  filtre_dernier_proprietaire_connu: true
                  analyse_ehf: true
                  parcelles:
                    - prefixe: "000"
                      section_cadastrale: "A"
                      numero_parcelle: "123"
                    - prefixe: "000"
                      section_cadastrale: "A"
                      numero_parcelle: "124"
                    - prefixe: "000"
                      section_cadastrale: "B"
                      numero_parcelle: "7"
              copropriete:
                summary: Copropriété — 1 assise, 2 lots
                value:
                  numero_commande_partenaire: "CMD-2026-0003"
                  localisation_bien: "01100 ARBENT"
                  type_recherche: "cadastrale"
                  copropriete: true
                  assises:
                    - prefixe: "000"
                      section_cadastrale: "AB"
                      numero_parcelle: "15"
                  lots:
                    - numero_lot: "42"
                      volume: "3"
                    - numero_lot: "43"
              identite_pp:
                summary: Identité — personne physique
                value:
                  numero_commande_partenaire: "CMD-2026-0004"
                  localisation_bien: "01090 MONTMERLE-SUR-SAÔNE"
                  type_recherche: "identite"
                  personnes_physiques:
                    - nom_personne: "DUPONT"
                      prenom_1: "Jean"
                      prenom_2: "Pierre"
                      date_naissance: "15/03/1975"
                      lieu_naissance: "01000 BOURG-EN-BRESSE"
                      pays_naissance: "France"
              identite_pm:
                summary: Identité — personne morale
                value:
                  numero_commande_partenaire: "CMD-2026-0005"
                  localisation_bien: "01100 BELLIGNAT"
                  type_recherche: "identite"
                  personnes_morales:
                    - raison_sociale: "SCI LES OLIVIERS"
                      siren: "123456789"
                      code_postal_ville: "01100 BELLIGNAT"
              combinee_exhaustive:
                summary: Combinée exhaustive — parcelles + personne physique
                value:
                  numero_commande_partenaire: "CMD-2026-0006"
                  localisation_bien: "01100 GROISSIAT"
                  type_recherche: "combinee"
                  type_combinee: "exhaustive"
                  parcelles:
                    - section_cadastrale: "AK"
                      numero_parcelle: "201"
                    - section_cadastrale: "AK"
                      numero_parcelle: "202"
                  personnes_physiques:
                    - nom_personne: "MARTIN"
                      prenom_1: "Sophie"
                      date_naissance: "22/07/1980"
                      lieu_naissance: "01110 HAUTEVILLE-LOMPNES"
                      pays_naissance: "France"
              combinee_ciblee_copro:
                summary: Combinée ciblée — copropriété + personne morale
                value:
                  numero_commande_partenaire: "CMD-2026-0007"
                  localisation_bien: "01110 HAUTEVILLE-LOMPNES"
                  type_recherche: "combinee"
                  type_combinee: "ciblee"
                  copropriete: true
                  assises:
                    - section_cadastrale: "CL"
                      numero_parcelle: "88"
                  lots:
                    - numero_lot: "1"
                    - numero_lot: "2"
                    - numero_lot: "3"
                  personnes_morales:
                    - raison_sociale: "SARL IMMOBILIERE AZUR"
                      siren: "987654321"
                      code_postal_ville: "01110 HAUTEVILLE-LOMPNES"
              combinee_max:
                summary: Combinée exhaustive — charge maximale (12 parcelles + 2 PP + 1 PM = 180 € de débours)
                value:
                  numero_commande_partenaire: "CMD-2026-0008"
                  localisation_bien: "01100 MARTIGNAT"
                  type_recherche: "combinee"
                  type_combinee: "exhaustive"
                  analyse_ehf: true
                  parcelles:
                    - { prefixe: "000", section_cadastrale: "A", numero_parcelle: "101" }
                    - { prefixe: "000", section_cadastrale: "A", numero_parcelle: "102" }
                    - { prefixe: "000", section_cadastrale: "A", numero_parcelle: "103" }
                    - { prefixe: "000", section_cadastrale: "B", numero_parcelle: "201" }
                    - { prefixe: "000", section_cadastrale: "B", numero_parcelle: "202" }
                    - { prefixe: "000", section_cadastrale: "B", numero_parcelle: "203" }
                    - { prefixe: "000", section_cadastrale: "C", numero_parcelle: "301" }
                    - { prefixe: "000", section_cadastrale: "C", numero_parcelle: "302" }
                    - { prefixe: "000", section_cadastrale: "C", numero_parcelle: "303" }
                    - { prefixe: "000", section_cadastrale: "D", numero_parcelle: "401" }
                    - { prefixe: "000", section_cadastrale: "D", numero_parcelle: "402" }
                    - { prefixe: "000", section_cadastrale: "D", numero_parcelle: "403" }
                  personnes_physiques:
                    - nom_personne: "DUPONT"
                      prenom_1: "Jean"
                      date_naissance: "15/03/1975"
                      lieu_naissance: "01000 BOURG-EN-BRESSE"
                      pays_naissance: "France"
                    - nom_personne: "MARTIN"
                      prenom_1: "Marie"
                      prenom_2: "Claire"
                      date_naissance: "22/07/1980"
                      lieu_naissance: "01100 OYONNAX"
                      pays_naissance: "France"
                  personnes_morales:
                    - raison_sociale: "SCI DU PARC"
                      siren: "456789123"
                      code_postal_ville: "01100 MARTIGNAT"
              alsace_moselle:
                summary: Alsace-Moselle — cadastrale uniquement, tarif réduit 7 € (débours = 21 €)
                value:
                  numero_commande_partenaire: "CMD-2026-0009"
                  localisation_bien: "67000 STRASBOURG"
                  type_recherche: "cadastrale"
                  parcelles:
                    - section_cadastrale: "12"
                      numero_parcelle: "45"
                    - section_cadastrale: "12"
                      numero_parcelle: "46"
                    - section_cadastrale: "15"
                      numero_parcelle: "8"
      responses:
        "200":
          description: Résultat de la création (succès ou erreur métier).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EhfMultiResponse"
              examples:
                success:
                  summary: Commande créée avec succès
                  value:
                    success: true
                    jobId: "500Aa000001xYzZIAU"
                    debours: 36
                    errors: []
                error_alsace:
                  summary: Erreur — Alsace-Moselle
                  value:
                    success: false
                    jobId: null
                    debours: null
                    errors:
                      - code: "ALSACE_MOSELLE_RESTRICTION"
                        message: "Départements 57/67/68: cadastrale uniquement."
                        field: "type_recherche"
                error_missing:
                  summary: Erreur — champ manquant
                  value:
                    success: false
                    jobId: null
                    debours: null
                    errors:
                      - code: "MISSING_FIELD"
                        message: "localisation_bien obligatoire."
                        field: "localisation_bien"
                success_max:
                  summary: Succès — charge maximale (180 € de débours)
                  value:
                    success: true
                    jobId: "500Aa000001xYzZIAU"
                    debours: 180
                    errors: []

  # ================================================================
  # COPIE D'ACTE NOTARIÉ
  # ================================================================
  /v1/commandes/acte:
    post:
      tags: [Copie d'acte]
      summary: Commander une copie d'acte notarié
      description: |
        Crée une commande de copie d'acte notarié (acte de vente, donation,
        succession, partage…) à partir de sa référence d'enliassement.
      operationId: createCommandeActe
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ActeRequest"
            example:
              numero_commande_partenaire: "CMD-2026-0010"
              reference_partenaire: "REF-XYZ"
              localisation_bien: "01000 SAINT-DENIS-LÈS-BOURG"
              date_formalite: "15/09/2003"
              volume: "2003P"
              numero: "5678"
              numero_sages: "9404P01"
      responses:
        "200":
          description: Résultat de la création.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SimpleResponse"

  # ================================================================
  # DOCUMENT DE COPROPRIÉTÉ
  # ================================================================
  /v1/commandes/doc-copro:
    post:
      tags: [Document de copropriété]
      summary: Commander un document de copropriété (RDC / EDD)
      description: |
        Crée une commande de règlement de copropriété (RDC) ou d'état descriptif
        de division (EDD) à partir de sa référence d'enliassement.
      operationId: createCommandeDocCopro
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DocCoproRequest"
            example:
              numero_commande_partenaire: "CMD-2026-0011"
              reference_partenaire: "REF-XYZ"
              localisation_bien: "01090 FRANCHELEINS"
              document_demande: "RDC"
              date_formalite: "10/10/2019"
              volume: "2019P"
              numero: "345"
              numero_sages: "9404P01"
      responses:
        "200":
          description: Résultat de la création.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SimpleResponse"

  # ================================================================
  # SUIVI DE COMMANDE
  # ================================================================
  /v1/commandes/{jobId}:
    get:
      tags: [Suivi]
      summary: Suivre une commande
      description: |
        Récupère le statut, l'avancement et le lien de dossier partagé
        d'une commande existante.
      operationId: getCommande
      parameters:
        - name: jobId
          in: path
          required: true
          description: Identifiant de la commande (retourné par les endpoints POST).
          schema:
            type: string
            example: "500Aa000001xYzZIAU"
      responses:
        "200":
          description: Statut de la commande.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CommandeStatusResponse"
              example:
                success: true
                jobId: "500Aa000001xYzZIAU"
                status: "In Progress"
                share_folder: "https://docs.hypotheques-en-ligne.fr/aZerTy_io-PqsdZg"
                errors: []

# ================================================================
# COMPOSANTS
# ================================================================
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Token obtenu via `POST /oauth`.

  schemas:
    # ============================================================
    # ERREUR
    # ============================================================
    ErrorItem:
      type: object
      description: Détail d'une erreur de validation.
      properties:
        code:
          type: string
          description: Code machine de l'erreur.
          enum:
            - AUTH_REQUIRED
            - INVALID_TOKEN
            - INVALID_JSON
            - MISSING_FIELD
            - INVALID_VALUE
            - INVALID_COMBINATION
            - LIMIT_EXCEEDED
            - ALSACE_MOSELLE_RESTRICTION
            - DUPLICATE_VALUE
            - LOT_INCOMPLETE
            - SERVER_ERROR
          example: "MISSING_FIELD"
        message:
          type: string
          description: Message explicatif en français.
          example: "localisation_bien obligatoire."
        field:
          type: string
          nullable: true
          description: Chemin du champ concerné (ex `parcelles[2].section_cadastrale`).
          example: "localisation_bien"

    # ============================================================
    # RÉPONSE EHF-MULTI
    # ============================================================
    EhfMultiResponse:
      type: object
      properties:
        success:
          type: boolean
          description: "`true` si la commande a été créée, `false` en cas d'erreur."
        jobId:
          type: string
          nullable: true
          description: Identifiant Salesforce de la commande. `null` en cas d'erreur.
          example: "500Aa000001xYzZIAU"
        debours:
          type: number
          format: decimal
          nullable: true
          description: Montant des débours calculés en euros. `null` en cas d'erreur.
          example: 36
        errors:
          type: array
          items:
            $ref: "#/components/schemas/ErrorItem"
          description: Liste des erreurs détectées (vide si succès).

    # ============================================================
    # RÉPONSE SIMPLE (acte, doc-copro)
    # ============================================================
    SimpleResponse:
      type: object
      properties:
        success:
          type: boolean
        jobId:
          type: string
          nullable: true
          example: "500Aa000001xYzZIAU"
        errors:
          type: array
          items:
            $ref: "#/components/schemas/ErrorItem"

    # ============================================================
    # RÉPONSE SUIVI
    # ============================================================
    CommandeStatusResponse:
      type: object
      properties:
        success:
          type: boolean
        jobId:
          type: string
          example: "500Aa000001xYzZIAU"
        status:
          type: string
          description: Statut de la commande.
          enum:
            - New
            - In Progress
            - Awaiting Customer
            - Closed
            - Cancelled
        share_folder:
          type: string
          format: uri
          nullable: true
          description: Lien vers le dossier partagé contenant les documents.
          example: "https://docs.hypotheques-en-ligne.fr/aZerTy_io-PqsdZg"
        errors:
          type: array
          items:
            $ref: "#/components/schemas/ErrorItem"

    # ============================================================
    # REQUÊTE EHF-MULTI
    # ============================================================
    EhfMultiRequest:
      type: object
      required:
        - numero_commande_partenaire
        - localisation_bien
        - type_recherche
      properties:
        numero_commande_partenaire:
          type: string
          description: |
            Identifiant unique de la commande côté partenaire.
            Sert de clé d'idempotence : un même numéro ne peut être utilisé qu'une fois.
          example: "CMD-2026-0001"
        localisation_bien:
          type: string
          description: |
            Commune au format "CP VILLE" (code postal 5 chiffres + ville en majuscules).
            Doit correspondre au référentiel Hypothèques en ligne.
          example: "01000 BOURG-EN-BRESSE"
        type_recherche:
          type: string
          enum: [cadastrale, identite, combinee]
          description: |
            Mode de recherche :
            - `cadastrale` : par références cadastrales (parcelles ou assises+lots).
            - `identite` : par personnes physiques et/ou morales.
            - `combinee` : croisement parcelles × personnes.
          example: "cadastrale"
        type_combinee:
          type: string
          enum: [exhaustive, ciblee]
          description: |
            Obligatoire si `type_recherche = "combinee"`.
            - `exhaustive` : tous les résultats liés aux parcelles ET aux personnes (union).
            - `ciblee` : uniquement les résultats à l'intersection parcelles × personnes.
          example: "exhaustive"
        copropriete:
          type: boolean
          default: false
          description: |
            Si `true`, remplace `parcelles[]` par `assises[]` + `lots[]`.
            Disponible pour `cadastrale` et `combinee` uniquement.
        filtre_dernier_proprietaire_connu:
          type: boolean
          default: false
          description: |
            Limite la délivrance au dernier propriétaire connu.
            Disponible uniquement pour `type_recherche = "cadastrale"`.
        analyse_ehf:
          type: boolean
          default: false
          description: |
            Si `true`, déclenche l'analyse automatique de l'état hypothécaire
            par Intellig'IA une fois le document reçu.
        reference_partenaire:
          type: string
          description: Référence interne libre du partenaire (numéro de dossier, etc.).
          example: "DOSSIER-ABC"
        parcelles:
          type: array
          description: |
            Liste de parcelles cadastrales (1 à 12).
            Requis si `type_recherche` ∈ {`cadastrale`, `combinee`} et `copropriete = false`.
          minItems: 1
          maxItems: 12
          items:
            $ref: "#/components/schemas/Parcelle"
        assises:
          type: array
          description: |
            Assiette cadastrale de la copropriété (1 à 5).
            Requis si `copropriete = true`.
          minItems: 1
          maxItems: 5
          items:
            $ref: "#/components/schemas/Assise"
        lots:
          type: array
          description: |
            Numéros de lots de la copropriété (1 à 12).
            Requis si `copropriete = true`. Chaque lot doit avoir au moins `numero_lot` ou `volume`.
          minItems: 1
          maxItems: 12
          items:
            $ref: "#/components/schemas/Lot"
        personnes_physiques:
          type: array
          description: |
            Personnes physiques (0 à 3).
            Requis si `type_recherche` ∈ {`identite`, `combinee`}.
          maxItems: 3
          items:
            $ref: "#/components/schemas/PersonnePhysique"
        personnes_morales:
          type: array
          description: |
            Personnes morales (0 à 3).
            Requis si `type_recherche` ∈ {`identite`, `combinee`}.
            Total personnes physiques + morales ≤ 3.
          maxItems: 3
          items:
            $ref: "#/components/schemas/PersonneMorale"

    # ============================================================
    # SOUS-OBJETS EHF-MULTI
    # ============================================================
    Parcelle:
      type: object
      required: [section_cadastrale, numero_parcelle]
      description: Référence cadastrale d'une parcelle.
      properties:
        section_cadastrale:
          type: string
          pattern: "^[a-zA-Z0-9]{1,2}$"
          description: "Section cadastrale (1-2 caractères alphanumériques)."
          example: "BJ"
        numero_parcelle:
          type: string
          pattern: "^[0-9]{1,4}$"
          description: "Numéro de parcelle (1 à 4 chiffres)."
          example: "42"
        prefixe:
          type: string
          pattern: "^[0-9]{3}$"
          description: "Préfixe cadastral (3 chiffres). Nécessaire pour certaines communes."
          example: "000"

    Assise:
      type: object
      required: [section_cadastrale, numero_parcelle]
      description: Assiette cadastrale d'une copropriété.
      properties:
        section_cadastrale:
          type: string
          pattern: "^[a-zA-Z0-9]{1,2}$"
          description: "Section cadastrale de l'assise."
          example: "AB"
        numero_parcelle:
          type: string
          pattern: "^[0-9]{1,4}$"
          description: "Numéro de parcelle de l'assise."
          example: "15"
        prefixe:
          type: string
          pattern: "^[0-9]{3}$"
          description: "Préfixe cadastral de l'assise."
          example: "000"

    Lot:
      type: object
      description: |
        Lot de copropriété. Au moins `numero_lot` ou `volume` doit être renseigné.
      properties:
        numero_lot:
          type: string
          pattern: "^[0-9]{1,7}$"
          description: "Numéro de lot (1 à 7 chiffres)."
          example: "42"
        volume:
          type: string
          pattern: "^[0-9]{1,7}$"
          description: "Numéro de lot volumétrique (1 à 7 chiffres)."
          example: "3"

    PersonnePhysique:
      type: object
      required: [nom_personne, prenom_1, date_naissance, lieu_naissance, pays_naissance]
      description: Identité d'une personne physique.
      properties:
        nom_personne:
          type: string
          description: Nom de famille.
          example: "DUPONT"
        prenom_1:
          type: string
          description: Premier prénom.
          example: "Jean"
        prenom_2:
          type: string
          description: Deuxième prénom (facultatif).
          example: "Pierre"
        date_naissance:
          type: string
          pattern: "^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/(19|20)\\d{2}$"
          description: "Date de naissance au format JJ/MM/AAAA."
          example: "15/03/1975"
        lieu_naissance:
          type: string
          description: |
            Lieu de naissance au format "CP VILLE".
            Doit correspondre au référentiel Hypothèques en ligne.
          example: "01090 MONTMERLE-SUR-SAÔNE"
        pays_naissance:
          type: string
          description: "Pays de naissance."
          example: "France"

    PersonneMorale:
      type: object
      required: [raison_sociale, siren, code_postal_ville]
      description: Identité d'une personne morale (entreprise, SCI, association…).
      properties:
        raison_sociale:
          type: string
          description: Raison sociale.
          example: "SCI LES OLIVIERS"
        siren:
          type: string
          pattern: "^[0-9]{9}$"
          description: "Numéro SIREN (9 chiffres)."
          example: "123456789"
        code_postal_ville:
          type: string
          description: |
            Siège social au format "CP VILLE".
            Doit correspondre au référentiel Hypothèques en ligne.
          example: "01100 BELLIGNAT"

    # ============================================================
    # REQUÊTE ACTE
    # ============================================================
    ActeRequest:
      type: object
      required:
        - numero_commande_partenaire
        - localisation_bien
        - date_formalite
        - volume
        - numero
        - numero_sages
      properties:
        numero_commande_partenaire:
          type: string
          description: Identifiant unique de la commande côté partenaire.
          example: "CMD-2026-0010"
        localisation_bien:
          type: string
          description: "Commune au format \"CP VILLE\"."
          example: "01000 SAINT-DENIS-LÈS-BOURG"
        date_formalite:
          type: string
          pattern: "^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/(19|20)\\d{2}$"
          description: "Date de la formalité au format JJ/MM/AAAA."
          example: "15/09/2003"
        volume:
          type: string
          description: Volume d'enliassement.
          example: "2003P"
        numero:
          type: string
          description: Numéro d'enliassement.
          example: "5678"
        numero_sages:
          type: string
          description: Numéro SAGES du service de publicité foncière.
          example: "9404P01"
        reference_partenaire:
          type: string
          description: Référence interne libre du partenaire.
          example: "REF-XYZ"

    # ============================================================
    # REQUÊTE DOC-COPRO
    # ============================================================
    DocCoproRequest:
      type: object
      required:
        - numero_commande_partenaire
        - localisation_bien
        - document_demande
        - date_formalite
        - volume
        - numero
        - numero_sages
      properties:
        numero_commande_partenaire:
          type: string
          description: Identifiant unique de la commande côté partenaire.
          example: "CMD-2026-0011"
        localisation_bien:
          type: string
          description: "Commune au format \"CP VILLE\"."
          example: "01090 FRANCHELEINS"
        document_demande:
          type: string
          enum: [RDC, EDD]
          description: |
            Type de document :
            - `RDC` : Règlement De Copropriété.
            - `EDD` : État Descriptif de Division.
          example: "RDC"
        date_formalite:
          type: string
          pattern: "^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/(19|20)\\d{2}$"
          description: "Date de la formalité au format JJ/MM/AAAA."
          example: "10/10/2019"
        volume:
          type: string
          description: Volume d'enliassement.
          example: "2019P"
        numero:
          type: string
          description: Numéro d'enliassement.
          example: "345"
        numero_sages:
          type: string
          description: Numéro SAGES du service de publicité foncière.
          example: "9404P01"
        reference_partenaire:
          type: string
          description: Référence interne libre du partenaire.
          example: "REF-XYZ"
