{
  "security": [
    {
      "personalAccessToken": []
    }
  ],
  "servers": [
    {
      "url": "https://api.qalimo.fr",
      "description": "Production"
    }
  ],
  "info": {
    "description": "API REST de Qalimo, la plateforme de gestion locative. Lecture et \u00e9criture sur\nles biens, baux, \u00e9critures comptables et documents du compte authentifi\u00e9.\n\n## Authentification\n\nToutes les requ\u00eates s'authentifient par un **Personal Access Token** pass\u00e9 en\nen-t\u00eate `Authorization: Bearer <token>`. Le token (pr\u00e9fixe `qmcp_`) se g\u00e9n\u00e8re\ndepuis l'application Qalimo : **Mon compte \u2192 Agents IA (MCP)**. Il porte les\ndroits du compte qui l'a cr\u00e9\u00e9 : profils poss\u00e9d\u00e9s et profils partag\u00e9s, en\nlecture comme en \u00e9criture selon le partage. Un token r\u00e9voqu\u00e9 cesse de\nfonctionner imm\u00e9diatement.\n\n```bash\ncurl -H \"Authorization: Bearer qmcp_...\" https://api.qalimo.fr/api/v1/properties\n```\n\n## Conventions\n\n- Les montants d'\u00e9critures comptables sont **sign\u00e9s** : n\u00e9gatifs pour un appel\n  (d\u00fb par le locataire), positifs pour un paiement. La somme des montants d'un\n  bail donne son solde.\n- Les dates sont au format ISO 8601 (`YYYY-MM-DD`).\n- Les erreurs renvoient un corps JSON avec un `message` ; 401 = token absent ou\n  invalide, 404 = ressource hors du p\u00e9rim\u00e8tre du compte, 400/422 = requ\u00eate\n  invalide.\n- Le spec OpenAPI machine est servi sur `GET /api/v1/openapi.json`.\n",
    "title": "Qalimo API",
    "version": "v1"
  },
  "paths": {
    "/api/v1/properties": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "description": "\u00c9tats \u00e0 inclure, s\u00e9par\u00e9s par des virgules (ex. `active,archived`). Par d\u00e9faut : `active`.",
            "schema": {
              "type": "array",
              "items": {
                "enum": [
                  "active",
                  "archived"
                ]
              }
            },
            "required": false,
            "explode": false,
            "style": "form"
          }
        ],
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PropertySummary"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Liste les biens locatifs accessibles au compte authentifi\u00e9.",
        "description": "Inclut les biens d\u00e9tenus par les profils bailleurs du compte ainsi que\nles biens partag\u00e9s avec lui. Seuls les biens actifs sont renvoy\u00e9s, sauf\nindication contraire via le filtre `status`.",
        "tags": [
          "properties"
        ]
      },
      "post": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyCreateInput"
              }
            }
          }
        },
        "summary": "Cr\u00e9e un bien simple (hors immeuble et colocation \u00e0 baux individuels).",
        "description": "Le profil bailleur doit \u00eatre accessible en \u00e9criture. Le loyer transmis est\nle loyer de base hors compl\u00e9ment ; en lecture, `rent_excluding_charges`\nrenvoie loyer + compl\u00e9ment.",
        "tags": [
          "properties"
        ]
      }
    },
    "/api/v1/properties/{property_id}": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "R\u00e9cup\u00e8re un bien locatif.",
        "tags": [
          "properties"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "property_id",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 0
          }
        }
      ],
      "patch": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PropertyUpdateInput"
              }
            }
          }
        },
        "summary": "Modifie un bien : seuls les champs renseign\u00e9s sont appliqu\u00e9s.",
        "tags": [
          "properties"
        ]
      }
    },
    "/api/v1/tenants": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "description": "\u00c9tats \u00e0 inclure, s\u00e9par\u00e9s par des virgules (ex. `active,archived`). Par d\u00e9faut : `active`.",
            "schema": {
              "type": "array",
              "items": {
                "enum": [
                  "active",
                  "candidate",
                  "archived",
                  "archived_candidate"
                ]
              }
            },
            "required": false,
            "explode": false,
            "style": "form"
          },
          {
            "in": "query",
            "name": "property_id",
            "description": "Restreint aux baux du bien donn\u00e9.",
            "schema": {
              "type": "integer",
              "default": null,
              "nullable": true
            },
            "required": false
          }
        ],
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TenantSummary"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Liste les baux des biens accessibles au compte authentifi\u00e9.",
        "description": "Seuls les baux actifs sont renvoy\u00e9s, sauf indication contraire via le\nfiltre `status`. Le filtre `property_id` restreint aux baux d'un bien.",
        "tags": [
          "tenants"
        ]
      },
      "post": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TenantCreateInput"
              }
            }
          }
        },
        "summary": "Cr\u00e9e un bail sur un bien, avec ses occupants.",
        "description": "Pens\u00e9 pour la retranscription d'un bail existant : pas de g\u00e9n\u00e9ration de\nPDF ni d'envoi en signature (flux applicatifs). Le mod\u00e8le de bail est\nd\u00e9duit du bien et de `lease_type`, sauf `lease_template_id` explicite.\n`generate_initial_balance` reconstruit les appels depuis le d\u00e9but du bail.",
        "tags": [
          "tenants"
        ]
      }
    },
    "/api/v1/tenants/{tenant_id}": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "R\u00e9cup\u00e8re un bail.",
        "tags": [
          "tenants"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "tenant_id",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 0
          }
        }
      ],
      "patch": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TenantUpdateInput"
              }
            }
          }
        },
        "summary": "Modifie un bail : seuls les champs fournis sont appliqu\u00e9s.",
        "description": "Le passage de `notice_given` \u00e0 true ouvre le processus de d\u00e9part (fin de\nbail) ; son retour \u00e0 false l'annule.",
        "tags": [
          "tenants"
        ]
      }
    },
    "/api/v1/landlord-profiles": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProfileSummary"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Liste les profils bailleurs accessibles au compte authentifi\u00e9.",
        "description": "Inclut les profils d\u00e9tenus par le compte et ceux partag\u00e9s avec lui. Les\nbiens et baux r\u00e9f\u00e9rencent ces profils via `owner_profile_id`.",
        "tags": [
          "landlord-profiles"
        ]
      },
      "post": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProfileCreateInput"
              }
            }
          }
        },
        "summary": "Cr\u00e9e un profil bailleur rattach\u00e9 au compte authentifi\u00e9.",
        "description": "Les biens et baux cr\u00e9\u00e9s ensuite r\u00e9f\u00e9rencent ce profil via\n`owner_profile_id`. Le profil est toujours cr\u00e9\u00e9 sur le compte du token,\njamais sur un compte partag\u00e9.",
        "tags": [
          "landlord-profiles"
        ]
      }
    },
    "/api/v1/landlord-profiles/{profile_id}": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "R\u00e9cup\u00e8re un profil bailleur.",
        "tags": [
          "landlord-profiles"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "profile_id",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 0
          }
        }
      ]
    },
    "/api/v1/tenants/{tenant_id}/balances": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BalanceSummary"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Liste les \u00e9critures comptables d'un bail, de la plus r\u00e9cente \u00e0 la plus ancienne.",
        "description": "Les appels (loyers, charges) sont en montants n\u00e9gatifs, les paiements en\npositif : la somme des montants donne le solde du locataire (n\u00e9gatif = il\ndoit de l'argent).",
        "tags": [
          "balances"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "tenant_id",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 0
          }
        }
      ],
      "post": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BalanceWithoutTargetCreateInput"
              }
            }
          }
        },
        "summary": "Cr\u00e9e une \u00e9criture comptable sur un bail (paiement, appel, d\u00e9p\u00f4t de garantie\u2026).",
        "description": "Le montant est sign\u00e9 : n\u00e9gatif pour un appel (d\u00fb par le locataire), positif\npour un paiement. Le type doit appartenir \u00e0 la partie locataire du plan.",
        "tags": [
          "balances"
        ]
      }
    },
    "/api/v1/balances/{balance_id}": {
      "patch": {
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceUpdateResult"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BalanceUpdateInput"
              }
            }
          }
        },
        "summary": "Corrige une \u00e9criture : montant, date, type, commentaire, TVA ou p\u00e9riode couverte.",
        "description": "Une \u00e9criture brouillon est modifi\u00e9e en place. Une \u00e9criture comptabilis\u00e9e\ndans une p\u00e9riode close est contrepass\u00e9e puis remplac\u00e9e par une \u00e9criture\ncorrig\u00e9e dat\u00e9e du jour \u2014 les deux sont renvoy\u00e9es.",
        "tags": [
          "balances"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "balance_id",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 0
          }
        }
      ]
    },
    "/api/v1/files": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "property_id",
            "description": "Documents du bien donn\u00e9.",
            "schema": {
              "type": "integer",
              "default": null,
              "nullable": true
            },
            "required": false
          },
          {
            "in": "query",
            "name": "tenant_id",
            "description": "Documents du bail donn\u00e9.",
            "schema": {
              "type": "integer",
              "default": null,
              "nullable": true
            },
            "required": false
          }
        ],
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FileSummary"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Liste les documents d'un bien et/ou d'un bail.",
        "description": "Au moins un des filtres `property_id` ou `tenant_id` est requis ; avec les\ndeux, renvoie l'union des documents du bien et de ceux du bail.",
        "tags": [
          "files"
        ]
      },
      "post": {
        "parameters": [
          {
            "in": "formData",
            "name": "property_id",
            "description": "Bien auquel rattacher le document.",
            "schema": {
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "formData",
            "name": "tenant_id",
            "description": "Bail auquel rattacher le document.",
            "schema": {
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "formData",
            "name": "tag",
            "description": "Cat\u00e9gorie du document.",
            "schema": {
              "enum": [
                "bail",
                "edl",
                "avenant",
                "conge",
                "courrier",
                "assurance_habitation",
                "entretien_chauffage",
                "caution",
                "visale",
                "gli",
                "quittance",
                "avis_echeance",
                "recu",
                "decompte",
                "rib",
                "identite",
                "domicile",
                "pro",
                "taxe_fonciere",
                "taxe_om",
                "liasse",
                "tableau_amortissement",
                "tableau_immobilisation",
                "copro",
                "DPE",
                "mesurage",
                "elec",
                "gaz",
                "plomb",
                "amiante",
                "pollution",
                "risque_naturel",
                "termite",
                "autre"
              ]
            },
            "required": false
          },
          {
            "in": "formData",
            "name": "valid_until",
            "description": "Date de fin de validit\u00e9 \u2014 requise pour une assurance habitation ou un entretien chaudi\u00e8re.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "required": false
          },
          {
            "in": "formData",
            "name": "shared",
            "description": "Partager le document avec les locataires du bien.",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "required": false
          }
        ],
        "responses": {
          "422": {
            "$ref": "#/components/responses/UNPROCESSABLE_CONTENT"
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/_FileUpload"
              }
            }
          }
        },
        "summary": "D\u00e9pose un document (multipart) sur un bien ou un bail.",
        "description": "Au moins un des champs `property_id` ou `tenant_id` est requis. Extensions\naccept\u00e9es : pdf, png, jpg, jpeg. La r\u00e9ponse contient l'URL de\nt\u00e9l\u00e9chargement sign\u00e9e du document cr\u00e9\u00e9.",
        "tags": [
          "files"
        ]
      }
    },
    "/api/v1/files/{file_id}": {
      "get": {
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "R\u00e9cup\u00e8re un document, avec une URL de t\u00e9l\u00e9chargement sign\u00e9e valable quelques minutes.",
        "tags": [
          "files"
        ]
      },
      "parameters": [
        {
          "in": "path",
          "name": "file_id",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 0
          }
        }
      ]
    },
    "/public/v1/rent-revision/checkout": {
      "post": {
        "responses": {
          "400": {
            "description": "Payload invalide (d\u00e9tail par champ dans le corps), ou indice IRL du trimestre demand\u00e9 pas encore publi\u00e9 par l'INSEE."
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RentRevisionCheckoutResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "Cr\u00e9er le devis de r\u00e9vision IRL et la session de paiement.",
        "description": "Courrier de r\u00e9vision annuelle du loyer conforme \u00e0 l'article 17-1 de la loi n\u00b0 89-462\ndu 6 juillet 1989, calcul\u00e9 sur les indices de r\u00e9f\u00e9rence des loyers (IRL) publi\u00e9s par\nl'INSEE et synchronis\u00e9s quotidiennement par Qalimo (m\u00e9tropole, Corse, outre-mer).\nLe choix du bon trimestre et de la bonne ann\u00e9e d'indice selon la date anniversaire\ndu bail est fait par Qalimo. Le calcul est fourni d\u00e8s cette r\u00e9ponse : pr\u00e9sentez-le \u00e0\nl'utilisateur, puis proposez-lui le courrier certifi\u00e9 via `checkout_url` (page de\npaiement Stripe \u00e0 faire r\u00e9gler par un humain).",
        "tags": [
          "public-documents"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RentRevisionInput"
              }
            }
          }
        }
      }
    },
    "/public/v1/rent-revision/document": {
      "get": {
        "responses": {
          "404": {
            "description": "Session de paiement inconnue."
          },
          "402": {
            "description": "Paiement non confirm\u00e9 : faire r\u00e9gler `checkout_url` puis r\u00e9essayer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredResponse"
                }
              }
            }
          },
          "200": {
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "T\u00e9l\u00e9charger le courrier de r\u00e9vision (PDF).",
        "tags": [
          "public-documents"
        ],
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "session_id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identifiant de la Checkout Session Stripe (`cs_\u2026`) renvoy\u00e9 par le checkout."
          }
        ]
      }
    },
    "/public/v1/rent-revision/invoice": {
      "get": {
        "responses": {
          "404": {
            "description": "Session de paiement inconnue."
          },
          "402": {
            "description": "Paiement non confirm\u00e9 : faire r\u00e9gler `checkout_url` puis r\u00e9essayer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredResponse"
                }
              }
            }
          },
          "200": {
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/DEFAULT_ERROR"
          }
        },
        "summary": "T\u00e9l\u00e9charger la facture (PDF).",
        "tags": [
          "public-documents"
        ],
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "session_id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identifiant de la Checkout Session Stripe (`cs_\u2026`) renvoy\u00e9 par le checkout."
          }
        ]
      }
    }
  },
  "tags": [
    {
      "name": "properties",
      "description": "Biens locatifs"
    },
    {
      "name": "tenants",
      "description": "Baux et locataires"
    },
    {
      "name": "landlord-profiles",
      "description": "Profils bailleurs"
    },
    {
      "name": "balances",
      "description": "\u00c9critures comptables"
    },
    {
      "name": "files",
      "description": "Documents"
    },
    {
      "name": "public-documents",
      "description": "Documents locatifs conformes vendus \u00e0 l'acte, **sans compte Qalimo** \u2014 pens\u00e9s pour \u00eatre achet\u00e9s par un agent IA pour le compte de son utilisateur. Aucune authentification : le paiement (Stripe Checkout, carte, porteur humain) est la seule condition de livraison. S\u00e9mantique : **HTTP 402** = paiement non confirm\u00e9 (r\u00e9essayer apr\u00e8s r\u00e8glement). Le calcul est fig\u00e9 au moment du devis : le document livr\u00e9 correspond toujours \u00e0 ce qui a \u00e9t\u00e9 pay\u00e9."
    }
  ],
  "openapi": "3.0.3",
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "code": {
            "type": "integer",
            "description": "Error code"
          },
          "status": {
            "type": "string",
            "description": "Error name"
          },
          "message": {
            "type": "string",
            "description": "Error message"
          },
          "errors": {
            "type": "object",
            "description": "Errors",
            "additionalProperties": {}
          }
        }
      },
      "PaginationMetadata": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Total number of items."
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages."
          },
          "first_page": {
            "type": "integer",
            "description": "First available page number."
          },
          "last_page": {
            "type": "integer",
            "description": "Last available page number."
          },
          "page": {
            "type": "integer",
            "description": "Current page number."
          },
          "previous_page": {
            "type": "integer",
            "description": "Previous page number."
          },
          "next_page": {
            "type": "integer",
            "description": "Next page number."
          }
        }
      },
      "PropertySummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du bien."
          },
          "name": {
            "type": "string",
            "description": "Nom d'affichage du bien."
          },
          "type": {
            "type": "string",
            "enum": [
              "habitation",
              "commercial",
              "garage"
            ],
            "description": "Type de bien."
          },
          "city": {
            "type": "string",
            "description": "Ville."
          },
          "postal_code": {
            "type": "string",
            "description": "Code postal."
          },
          "rent_excluding_charges": {
            "type": "number",
            "description": "Loyer hors charges, compl\u00e9ment de loyer inclus."
          },
          "charges": {
            "type": "number",
            "description": "Charges."
          },
          "status": {
            "description": "\u00c9tat du bien (actif ou archiv\u00e9).",
            "enum": [
              "active",
              "archived"
            ]
          }
        },
        "required": [
          "charges",
          "city",
          "id",
          "name",
          "postal_code",
          "rent_excluding_charges",
          "status",
          "type"
        ]
      },
      "PropertyDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du bien."
          },
          "name": {
            "type": "string",
            "description": "Nom d'affichage du bien."
          },
          "type": {
            "type": "string",
            "enum": [
              "habitation",
              "commercial",
              "garage"
            ],
            "description": "Type de bien."
          },
          "city": {
            "type": "string",
            "description": "Ville."
          },
          "postal_code": {
            "type": "string",
            "description": "Code postal."
          },
          "rent_excluding_charges": {
            "type": "number",
            "description": "Loyer hors charges, compl\u00e9ment de loyer inclus."
          },
          "charges": {
            "type": "number",
            "description": "Charges."
          },
          "status": {
            "description": "\u00c9tat du bien (actif ou archiv\u00e9).",
            "enum": [
              "active",
              "archived"
            ]
          },
          "street": {
            "type": "string",
            "default": "",
            "description": "Num\u00e9ro et rue."
          },
          "address_line_2": {
            "type": "string",
            "default": null,
            "description": "Compl\u00e9ment d'adresse.",
            "nullable": true
          },
          "housing_type": {
            "type": "string",
            "default": null,
            "enum": [
              "individuel",
              "collectif",
              null
            ],
            "description": "Type d'habitat.",
            "nullable": true
          },
          "surface_area": {
            "type": "number",
            "default": null,
            "description": "Surface habitable en m\u00e8tres carr\u00e9s.",
            "nullable": true
          },
          "main_room_count": {
            "type": "integer",
            "default": null,
            "description": "Nombre de pi\u00e8ces principales.",
            "nullable": true
          },
          "is_shared_housing": {
            "type": "boolean",
            "default": false,
            "description": "Vrai si le bien est lou\u00e9 en colocation."
          },
          "shared_housing_capacity": {
            "type": "integer",
            "default": null,
            "description": "Nombre de chambres en colocation.",
            "nullable": true
          },
          "deposit": {
            "type": "number",
            "default": null,
            "description": "Montant du d\u00e9p\u00f4t de garantie.",
            "nullable": true
          },
          "payment_day": {
            "type": "integer",
            "default": null,
            "description": "Jour du mois auquel le loyer est d\u00fb.",
            "nullable": true
          },
          "additional_rent": {
            "type": "number",
            "default": null,
            "description": "Compl\u00e9ment de loyer, d\u00e9j\u00e0 inclus dans rent_excluding_charges.",
            "nullable": true
          },
          "charge_type": {
            "type": "string",
            "default": null,
            "enum": [
              "provision",
              "forfait",
              null
            ],
            "description": "Mode de facturation des charges.",
            "nullable": true
          },
          "energy_performance_grade": {
            "type": "string",
            "default": null,
            "enum": [
              "A",
              "B",
              "C",
              "D",
              "E",
              "F",
              "G",
              null
            ],
            "description": "Classe \u00e9nerg\u00e9tique (DPE).",
            "nullable": true
          },
          "purchase_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date d'achat.",
            "nullable": true
          },
          "purchase_price": {
            "type": "integer",
            "default": null,
            "description": "Prix d'achat en euros.",
            "nullable": true
          },
          "owner_profile_id": {
            "type": "integer",
            "default": null,
            "description": "Identifiant du profil bailleur propri\u00e9taire.",
            "nullable": true
          }
        },
        "required": [
          "charges",
          "city",
          "id",
          "name",
          "postal_code",
          "rent_excluding_charges",
          "status",
          "type"
        ]
      },
      "PropertyCreateInput": {
        "type": "object",
        "properties": {
          "owner_profile_id": {
            "type": "integer",
            "description": "Profil bailleur propri\u00e9taire (accessible en \u00e9criture)."
          },
          "name": {
            "type": "string",
            "maxLength": 255,
            "description": "Nom du bien."
          },
          "type": {
            "description": "Type de bien.",
            "enum": [
              "habitation",
              "commercial",
              "garage"
            ]
          },
          "street": {
            "type": "string",
            "description": "Num\u00e9ro et rue."
          },
          "postal_code": {
            "type": "string",
            "maxLength": 11,
            "description": "Code postal."
          },
          "city": {
            "type": "string",
            "maxLength": 255,
            "description": "Ville."
          },
          "rent_excluding_charges": {
            "type": "number",
            "description": "Loyer hors charges, hors compl\u00e9ment (additional_rent)."
          },
          "charges": {
            "type": "number",
            "description": "Charges."
          },
          "deposit": {
            "type": "number",
            "description": "Montant du d\u00e9p\u00f4t de garantie."
          },
          "address_line_2": {
            "type": "string",
            "maxLength": 37,
            "description": "Compl\u00e9ment d'adresse."
          },
          "housing_type": {
            "description": "Type d'habitat.",
            "enum": [
              "individuel",
              "collectif"
            ]
          },
          "surface_area": {
            "type": "number",
            "description": "Surface habitable en m\u00e8tres carr\u00e9s."
          },
          "main_room_count": {
            "type": "integer",
            "description": "Nombre de pi\u00e8ces principales."
          },
          "payment_day": {
            "type": "integer",
            "description": "Jour du mois auquel le loyer est d\u00fb (d\u00e9faut : 1)."
          },
          "charge_type": {
            "description": "Mode de facturation des charges (d\u00e9faut : provision).",
            "enum": [
              "provision",
              "forfait"
            ]
          },
          "additional_rent": {
            "type": "number",
            "description": "Compl\u00e9ment de loyer."
          },
          "energy_performance_grade": {
            "description": "Classe \u00e9nerg\u00e9tique (DPE).",
            "enum": [
              "A",
              "B",
              "C",
              "D",
              "E",
              "F",
              "G"
            ]
          },
          "purchase_date": {
            "type": "string",
            "format": "date",
            "description": "Date d'achat."
          },
          "purchase_price": {
            "type": "integer",
            "description": "Prix d'achat en euros."
          }
        },
        "required": [
          "charges",
          "city",
          "deposit",
          "name",
          "owner_profile_id",
          "postal_code",
          "rent_excluding_charges",
          "street",
          "type"
        ]
      },
      "PropertyUpdateInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255,
            "description": "Nom du bien."
          },
          "street": {
            "type": "string",
            "description": "Num\u00e9ro et rue."
          },
          "address_line_2": {
            "type": "string",
            "maxLength": 37,
            "description": "Compl\u00e9ment d'adresse.",
            "nullable": true
          },
          "postal_code": {
            "type": "string",
            "maxLength": 11,
            "description": "Code postal."
          },
          "city": {
            "type": "string",
            "maxLength": 255,
            "description": "Ville."
          },
          "housing_type": {
            "description": "Type d'habitat.",
            "enum": [
              "individuel",
              "collectif",
              null
            ],
            "nullable": true
          },
          "surface_area": {
            "type": "number",
            "description": "Surface habitable en m\u00e8tres carr\u00e9s.",
            "nullable": true
          },
          "main_room_count": {
            "type": "integer",
            "description": "Nombre de pi\u00e8ces principales.",
            "nullable": true
          },
          "rent_excluding_charges": {
            "type": "number",
            "description": "Loyer hors charges, hors compl\u00e9ment (additional_rent)."
          },
          "additional_rent": {
            "type": "number",
            "description": "Compl\u00e9ment de loyer.",
            "nullable": true
          },
          "charges": {
            "type": "number",
            "description": "Charges."
          },
          "deposit": {
            "type": "number",
            "description": "Montant du d\u00e9p\u00f4t de garantie."
          },
          "payment_day": {
            "type": "integer",
            "description": "Jour du mois auquel le loyer est d\u00fb."
          },
          "charge_type": {
            "description": "Mode de facturation des charges.",
            "enum": [
              "provision",
              "forfait"
            ]
          },
          "energy_performance_grade": {
            "description": "Classe \u00e9nerg\u00e9tique (DPE).",
            "enum": [
              "A",
              "B",
              "C",
              "D",
              "E",
              "F",
              "G",
              null
            ],
            "nullable": true
          },
          "purchase_date": {
            "type": "string",
            "format": "date",
            "description": "Date d'achat.",
            "nullable": true
          },
          "purchase_price": {
            "type": "integer",
            "description": "Prix d'achat en euros.",
            "nullable": true
          }
        }
      },
      "TenantSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du bail."
          },
          "name": {
            "type": "string",
            "description": "Libell\u00e9 du bail."
          },
          "occupants": {
            "type": "string",
            "description": "Nom d'affichage des occupants."
          },
          "property_id": {
            "type": "integer",
            "description": "Identifiant du bien lou\u00e9."
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Date de d\u00e9but du bail."
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date de fin du bail (null si non renseign\u00e9e).",
            "nullable": true
          },
          "rent_excluding_charges": {
            "type": "number",
            "default": null,
            "description": "Loyer hors charges, compl\u00e9ment de loyer inclus.",
            "nullable": true
          },
          "status": {
            "description": "\u00c9tat du bail (actif, candidat, archiv\u00e9, candidat archiv\u00e9).",
            "enum": [
              "active",
              "candidate",
              "archived",
              "archived_candidate"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "occupants",
          "property_id",
          "start_date",
          "status"
        ]
      },
      "PersonSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du profil."
          },
          "kind": {
            "description": "Personne physique (individual) ou morale (company).",
            "enum": [
              "individual",
              "company"
            ]
          },
          "last_name": {
            "type": "string",
            "default": null,
            "description": "Nom de famille.",
            "nullable": true
          },
          "first_name": {
            "type": "string",
            "default": null,
            "description": "Pr\u00e9nom.",
            "nullable": true
          },
          "company_name": {
            "type": "string",
            "default": null,
            "description": "Raison sociale (personne morale).",
            "nullable": true
          },
          "email": {
            "type": "string",
            "default": null,
            "description": "Adresse email de contact.",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "default": null,
            "description": "T\u00e9l\u00e9phone.",
            "nullable": true
          },
          "siret": {
            "type": "string",
            "default": null,
            "description": "SIRET (personne morale).",
            "nullable": true
          }
        },
        "required": [
          "id",
          "kind"
        ]
      },
      "TenantDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du bail."
          },
          "name": {
            "type": "string",
            "description": "Libell\u00e9 du bail."
          },
          "occupants": {
            "type": "string",
            "description": "Nom d'affichage des occupants."
          },
          "property_id": {
            "type": "integer",
            "description": "Identifiant du bien lou\u00e9."
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Date de d\u00e9but du bail."
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date de fin du bail (null si non renseign\u00e9e).",
            "nullable": true
          },
          "rent_excluding_charges": {
            "type": "number",
            "default": null,
            "description": "Loyer hors charges, compl\u00e9ment de loyer inclus.",
            "nullable": true
          },
          "status": {
            "description": "\u00c9tat du bail (actif, candidat, archiv\u00e9, candidat archiv\u00e9).",
            "enum": [
              "active",
              "candidate",
              "archived",
              "archived_candidate"
            ]
          },
          "charges": {
            "type": "number",
            "default": null,
            "description": "Charges.",
            "nullable": true
          },
          "additional_rent": {
            "type": "number",
            "default": null,
            "description": "Compl\u00e9ment de loyer, d\u00e9j\u00e0 inclus dans rent_excluding_charges.",
            "nullable": true
          },
          "deposit": {
            "type": "number",
            "default": null,
            "description": "Montant du d\u00e9p\u00f4t de garantie.",
            "nullable": true
          },
          "payment_day": {
            "type": "integer",
            "default": null,
            "description": "Jour du mois auquel le loyer est d\u00fb.",
            "nullable": true
          },
          "payment_recurrence_months": {
            "type": "integer",
            "default": null,
            "description": "P\u00e9riodicit\u00e9 de l'\u00e9ch\u00e9ance en mois (1 = mensuelle, 3 = trimestrielle\u2026).",
            "nullable": true
          },
          "payment_type": {
            "type": "string",
            "default": null,
            "enum": [
              "prelevement",
              "virement",
              "espece",
              "cheque",
              null
            ],
            "description": "Mode de paiement du loyer.",
            "nullable": true
          },
          "signature_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date de signature du bail.",
            "nullable": true
          },
          "contract_duration_months": {
            "type": "integer",
            "default": null,
            "description": "Dur\u00e9e du contrat en mois.",
            "nullable": true
          },
          "lease_type": {
            "type": "string",
            "default": null,
            "enum": [
              "lcd",
              "nu",
              "meuble",
              "commercial",
              "garage",
              "habitation",
              "mobilite",
              "professionnel",
              "etudiant",
              "commercial_derogatoire",
              "sous-location",
              "civil",
              "civil_habitation",
              "stockage",
              "parking",
              null
            ],
            "description": "Type de bail.",
            "nullable": true
          },
          "charge_type": {
            "type": "string",
            "default": null,
            "enum": [
              "provision",
              "forfait",
              null
            ],
            "description": "Mode de facturation des charges.",
            "nullable": true
          },
          "vat_enabled": {
            "type": "boolean",
            "default": false,
            "description": "Vrai si le loyer est soumis \u00e0 TVA."
          },
          "caf_amount": {
            "type": "number",
            "default": null,
            "description": "Montant de l'aide CAF vers\u00e9e directement.",
            "nullable": true
          },
          "last_rent_revision_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date de la derni\u00e8re r\u00e9vision de loyer.",
            "nullable": true
          },
          "notice_given": {
            "type": "boolean",
            "default": null,
            "description": "Vrai si le locataire a pos\u00e9 son pr\u00e9avis.",
            "nullable": true
          },
          "occupant_profiles": {
            "type": "array",
            "description": "Profils occupants du bail (identit\u00e9 et coordonn\u00e9es).",
            "items": {
              "$ref": "#/components/schemas/PersonSummary"
            }
          },
          "guarantor_profiles": {
            "type": "array",
            "description": "Profils garants.",
            "items": {
              "$ref": "#/components/schemas/PersonSummary"
            }
          }
        },
        "required": [
          "id",
          "name",
          "occupants",
          "property_id",
          "start_date",
          "status"
        ]
      },
      "PersonInput": {
        "type": "object",
        "properties": {
          "kind": {
            "default": "individual",
            "description": "Personne physique (individual) ou morale (company).",
            "enum": [
              "individual",
              "company"
            ]
          },
          "last_name": {
            "type": "string",
            "maxLength": 255,
            "description": "Nom de famille (requis pour une personne physique)."
          },
          "first_name": {
            "type": "string",
            "maxLength": 255,
            "description": "Pr\u00e9nom."
          },
          "company_name": {
            "type": "string",
            "maxLength": 255,
            "description": "Raison sociale (requise pour une personne morale)."
          },
          "email": {
            "type": "string",
            "maxLength": 255,
            "description": "Adresse email de contact (quittances, invitations)."
          },
          "phone": {
            "type": "string",
            "maxLength": 255,
            "description": "T\u00e9l\u00e9phone."
          },
          "siret": {
            "type": "string",
            "description": "SIRET (personne morale), 14 chiffres."
          }
        }
      },
      "TenantCreateInput": {
        "type": "object",
        "properties": {
          "property_id": {
            "type": "integer",
            "description": "Bien lou\u00e9 (accessible en \u00e9criture)."
          },
          "occupants": {
            "type": "array",
            "minItems": 1,
            "description": "Occupants du bail (au moins un).",
            "items": {
              "$ref": "#/components/schemas/PersonInput"
            }
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Date de d\u00e9but du bail."
          },
          "rent_excluding_charges": {
            "type": "number",
            "description": "Loyer hors charges, hors compl\u00e9ment."
          },
          "charges": {
            "type": "number",
            "description": "Charges."
          },
          "lease_type": {
            "enum": [
              "lcd",
              "nu",
              "meuble",
              "commercial",
              "garage",
              "habitation",
              "mobilite",
              "professionnel",
              "etudiant",
              "commercial_derogatoire",
              "sous-location",
              "civil",
              "civil_habitation",
              "stockage",
              "parking"
            ],
            "description": "Type de bail. Requis pour un bien d'habitation ; d\u00e9duit pour un bien commercial ou garage."
          },
          "lease_template_id": {
            "type": "integer",
            "description": "Mod\u00e8le de bail pr\u00e9cis (sinon d\u00e9duit du type et du bien)."
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Date de fin du bail. Requise si contract_duration_months est absent, et toujours pour un bail mobilit\u00e9 ou de location courte dur\u00e9e."
          },
          "contract_duration_months": {
            "type": "integer",
            "minimum": 1,
            "description": "Dur\u00e9e du contrat en mois."
          },
          "deposit": {
            "type": "number",
            "default": 0.0,
            "description": "D\u00e9p\u00f4t de garantie (d\u00e9faut : 0)."
          },
          "payment_day": {
            "type": "integer",
            "default": 1,
            "minimum": 1,
            "maximum": 28,
            "description": "Jour du mois auquel le loyer est d\u00fb (d\u00e9faut : 1)."
          },
          "payment_recurrence_months": {
            "type": "integer",
            "default": 1,
            "description": "P\u00e9riodicit\u00e9 de l'\u00e9ch\u00e9ance en mois (d\u00e9faut : 1)."
          },
          "signature_date": {
            "type": "string",
            "format": "date",
            "description": "Date de signature du bail."
          },
          "allow_non_compliant": {
            "type": "boolean",
            "default": false,
            "description": "Retranscription d'un bail existant non conforme aux plafonds l\u00e9gaux (d\u00e9p\u00f4t, dur\u00e9es) : d\u00e9sactive ces contr\u00f4les."
          },
          "generate_initial_balance": {
            "type": "boolean",
            "default": false,
            "description": "G\u00e9n\u00e8re les appels de loyer depuis le d\u00e9but du bail (reconstruction d'historique)."
          },
          "generate_payments": {
            "type": "boolean",
            "default": false,
            "description": "Avec generate_initial_balance : marque aussi les paiements correspondants."
          }
        },
        "required": [
          "charges",
          "occupants",
          "property_id",
          "rent_excluding_charges",
          "start_date"
        ]
      },
      "TenantUpdateInput": {
        "type": "object",
        "properties": {
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Date de d\u00e9but du bail."
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Date de fin du bail (null pour l'effacer).",
            "nullable": true
          },
          "contract_duration_months": {
            "type": "integer",
            "minimum": 1,
            "description": "Dur\u00e9e du contrat en mois.",
            "nullable": true
          },
          "rent_excluding_charges": {
            "type": "number",
            "description": "Loyer hors charges, hors compl\u00e9ment."
          },
          "charges": {
            "type": "number",
            "description": "Charges."
          },
          "deposit": {
            "type": "number",
            "description": "D\u00e9p\u00f4t de garantie."
          },
          "payment_day": {
            "type": "integer",
            "minimum": 1,
            "maximum": 28,
            "description": "Jour du mois auquel le loyer est d\u00fb."
          },
          "payment_recurrence_months": {
            "type": "integer",
            "description": "P\u00e9riodicit\u00e9 de l'\u00e9ch\u00e9ance en mois."
          },
          "signature_date": {
            "type": "string",
            "format": "date",
            "description": "Date de signature du bail.",
            "nullable": true
          },
          "notice_given": {
            "type": "boolean",
            "description": "Pr\u00e9avis pos\u00e9 : le passage \u00e0 true ouvre le processus de d\u00e9part, le retour \u00e0 false l'annule."
          }
        }
      },
      "ProfileSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du profil bailleur."
          },
          "name": {
            "type": "string",
            "description": "Nom du profil (personne physique ou soci\u00e9t\u00e9)."
          },
          "is_company": {
            "type": "boolean",
            "description": "Vrai si le profil est une soci\u00e9t\u00e9 (SCI, SARL\u2026)."
          }
        },
        "required": [
          "id",
          "is_company",
          "name"
        ]
      },
      "ProfileDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du profil bailleur."
          },
          "name": {
            "type": "string",
            "description": "Nom du profil (personne physique ou soci\u00e9t\u00e9)."
          },
          "is_company": {
            "type": "boolean",
            "description": "Vrai si le profil est une soci\u00e9t\u00e9 (SCI, SARL\u2026)."
          },
          "siret": {
            "type": "string",
            "default": null,
            "description": "Num\u00e9ro SIRET.",
            "nullable": true
          },
          "tax_regime": {
            "type": "string",
            "default": null,
            "description": "R\u00e9gime fiscal du profil.",
            "nullable": true
          },
          "vat_enabled": {
            "type": "boolean",
            "default": false,
            "description": "Vrai si le profil est assujetti \u00e0 la TVA."
          },
          "vat_number": {
            "type": "string",
            "default": null,
            "description": "Num\u00e9ro de TVA intracommunautaire.",
            "nullable": true
          },
          "iban": {
            "type": "string",
            "default": null,
            "description": "IBAN de perception des loyers.",
            "nullable": true
          },
          "iban_account_holder": {
            "type": "string",
            "default": null,
            "description": "Nom du titulaire du compte de l'IBAN (\u00e0 d\u00e9faut, le nom du profil).",
            "nullable": true
          },
          "bic": {
            "type": "string",
            "default": null,
            "description": "BIC associ\u00e9 \u00e0 l'IBAN.",
            "nullable": true
          },
          "activity_start_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date de d\u00e9but d'activit\u00e9.",
            "nullable": true
          },
          "is_family_sci": {
            "type": "boolean",
            "default": false,
            "description": "Vrai si le profil est une SCI familiale."
          }
        },
        "required": [
          "id",
          "is_company",
          "name"
        ]
      },
      "ProfileCreateInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255,
            "description": "Nom du profil (personne physique ou soci\u00e9t\u00e9)."
          },
          "is_company": {
            "type": "boolean",
            "default": false,
            "description": "Soci\u00e9t\u00e9 (SCI, SARL\u2026) plut\u00f4t que personne physique."
          },
          "siret": {
            "type": "string",
            "description": "Num\u00e9ro SIRET (soci\u00e9t\u00e9s), 14 chiffres."
          },
          "tax_regime": {
            "type": "string",
            "maxLength": 255,
            "description": "R\u00e9gime fiscal (texte libre, ex. \u00ab reel \u00bb, \u00ab micro \u00bb)."
          },
          "vat_enabled": {
            "type": "boolean",
            "description": "Profil assujetti \u00e0 la TVA."
          },
          "vat_number": {
            "type": "string",
            "maxLength": 20,
            "description": "Num\u00e9ro de TVA intracommunautaire."
          },
          "iban": {
            "type": "string",
            "maxLength": 34,
            "description": "IBAN de perception des loyers."
          },
          "iban_account_holder": {
            "type": "string",
            "maxLength": 255,
            "description": "Nom du titulaire du compte de l'IBAN (\u00e0 d\u00e9faut, le nom du profil)."
          },
          "bic": {
            "type": "string",
            "maxLength": 11,
            "description": "BIC associ\u00e9 \u00e0 l'IBAN."
          },
          "activity_start_date": {
            "type": "string",
            "format": "date",
            "description": "Date de d\u00e9but d'activit\u00e9."
          },
          "is_family_sci": {
            "type": "boolean",
            "description": "SCI familiale."
          },
          "signatories": {
            "type": "array",
            "description": "Signataires du profil (g\u00e9rant d'une soci\u00e9t\u00e9, co-bailleurs\u2026).",
            "items": {
              "$ref": "#/components/schemas/PersonInput"
            }
          }
        },
        "required": [
          "name"
        ]
      },
      "BalanceSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant de l'\u00e9criture."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Date de l'\u00e9criture."
          },
          "type": {
            "type": "string",
            "default": null,
            "enum": [
              "achat_materiel",
              "achat_mobilier",
              "appel_charge",
              "appel_charge_exceptionnelle",
              "appel_complementaire",
              "appel_dg",
              "appel_divers",
              "appel_loyer",
              "appel_remboursement",
              "appel_reparation",
              "apport_cca",
              "assurance",
              "assurance_autres",
              "assurance_gli",
              "assurance_pj",
              "assurance_pno",
              "assurance_pret",
              "autre",
              "autre_nd_nr",
              "autre_recette",
              "autre_recuperable",
              "autre_revenus",
              "avoir_remise",
              "charge",
              "charges_autre",
              "charges_autre_recuperable",
              "comptabilite",
              "construction",
              "copro_non_recuperable",
              "copro_recuperable",
              "deplacement",
              "deplacement_ik",
              "deplacement_peage",
              "deplacement_transport",
              "emprunt_autres",
              "energie_commun",
              "energie_privee",
              "entretien_nr",
              "entretien_r",
              "fournitures",
              "frais_bancaire",
              "frais_eviction",
              "frais_gestion",
              "frais_notaire",
              "frais_procedure_nr",
              "frais_procedure_r",
              "impots",
              "impots_cfe",
              "impots_habitation",
              "impots_is",
              "impots_tf",
              "impots_tva",
              "indemnite",
              "interet",
              "paiement",
              "paiement_bailleur",
              "paiement_caf",
              "paiement_dg",
              "prelevement_honoraires",
              "publicite",
              "recette_autre",
              "recettes_loyer",
              "remboursement",
              "remboursement_cca",
              "remboursement_dg",
              "remboursement_emprunt",
              "remboursement_tva",
              "reparation",
              "repas",
              "retenue_dg",
              "telephone",
              "toem",
              "versement_emprunt",
              null
            ],
            "description": "Type d'\u00e9criture.",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "description": "Montant sign\u00e9 : n\u00e9gatif pour un appel (d\u00fb), positif pour un paiement."
          },
          "domain": {
            "type": "string",
            "enum": [
              "tenant",
              "property",
              "folder",
              "profile_bailleur",
              "compte"
            ],
            "description": "Domaine de rattachement."
          },
          "parent_id": {
            "type": "integer",
            "description": "Identifiant de l'entit\u00e9 de rattachement, \u00e0 interpr\u00e9ter selon domain."
          },
          "comment": {
            "type": "string",
            "default": null,
            "description": "Commentaire libre.",
            "nullable": true
          },
          "correlation_id": {
            "type": "string",
            "default": null,
            "description": "Identifiant de corr\u00e9lation des \u00e9critures cr\u00e9\u00e9es ensemble.",
            "nullable": true
          }
        },
        "required": [
          "amount",
          "date",
          "domain",
          "id",
          "parent_id"
        ]
      },
      "BalanceWithoutTargetCreateInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Type d'\u00e9criture (un type de la partie locataire, cr\u00e9able manuellement)."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Date de l'\u00e9criture."
          },
          "amount": {
            "type": "number",
            "description": "Montant sign\u00e9 : n\u00e9gatif pour un appel (d\u00fb), positif pour un paiement."
          },
          "comment": {
            "type": "string",
            "description": "Commentaire libre."
          },
          "vat_rate": {
            "type": "number",
            "enum": [
              0,
              2.1,
              5.5,
              8.5,
              10,
              20
            ],
            "description": "Taux de TVA."
          },
          "period_start": {
            "type": "string",
            "format": "date",
            "description": "D\u00e9but de la p\u00e9riode couverte."
          },
          "period_end": {
            "type": "string",
            "format": "date",
            "description": "Fin de la p\u00e9riode couverte."
          }
        },
        "required": [
          "amount",
          "date",
          "type"
        ]
      },
      "BalanceDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant de l'\u00e9criture."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Date de l'\u00e9criture."
          },
          "type": {
            "type": "string",
            "default": null,
            "enum": [
              "achat_materiel",
              "achat_mobilier",
              "appel_charge",
              "appel_charge_exceptionnelle",
              "appel_complementaire",
              "appel_dg",
              "appel_divers",
              "appel_loyer",
              "appel_remboursement",
              "appel_reparation",
              "apport_cca",
              "assurance",
              "assurance_autres",
              "assurance_gli",
              "assurance_pj",
              "assurance_pno",
              "assurance_pret",
              "autre",
              "autre_nd_nr",
              "autre_recette",
              "autre_recuperable",
              "autre_revenus",
              "avoir_remise",
              "charge",
              "charges_autre",
              "charges_autre_recuperable",
              "comptabilite",
              "construction",
              "copro_non_recuperable",
              "copro_recuperable",
              "deplacement",
              "deplacement_ik",
              "deplacement_peage",
              "deplacement_transport",
              "emprunt_autres",
              "energie_commun",
              "energie_privee",
              "entretien_nr",
              "entretien_r",
              "fournitures",
              "frais_bancaire",
              "frais_eviction",
              "frais_gestion",
              "frais_notaire",
              "frais_procedure_nr",
              "frais_procedure_r",
              "impots",
              "impots_cfe",
              "impots_habitation",
              "impots_is",
              "impots_tf",
              "impots_tva",
              "indemnite",
              "interet",
              "paiement",
              "paiement_bailleur",
              "paiement_caf",
              "paiement_dg",
              "prelevement_honoraires",
              "publicite",
              "recette_autre",
              "recettes_loyer",
              "remboursement",
              "remboursement_cca",
              "remboursement_dg",
              "remboursement_emprunt",
              "remboursement_tva",
              "reparation",
              "repas",
              "retenue_dg",
              "telephone",
              "toem",
              "versement_emprunt",
              null
            ],
            "description": "Type d'\u00e9criture.",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "description": "Montant sign\u00e9 : n\u00e9gatif pour un appel (d\u00fb), positif pour un paiement."
          },
          "domain": {
            "type": "string",
            "enum": [
              "tenant",
              "property",
              "folder",
              "profile_bailleur",
              "compte"
            ],
            "description": "Domaine de rattachement."
          },
          "parent_id": {
            "type": "integer",
            "description": "Identifiant de l'entit\u00e9 de rattachement, \u00e0 interpr\u00e9ter selon domain."
          },
          "comment": {
            "type": "string",
            "default": null,
            "description": "Commentaire libre.",
            "nullable": true
          },
          "correlation_id": {
            "type": "string",
            "default": null,
            "description": "Identifiant de corr\u00e9lation des \u00e9critures cr\u00e9\u00e9es ensemble.",
            "nullable": true
          },
          "transaction_id": {
            "type": "integer",
            "default": null,
            "description": "Identifiant de la transaction bancaire rapproch\u00e9e.",
            "nullable": true
          },
          "file_id": {
            "type": "integer",
            "default": null,
            "description": "Identifiant du justificatif attach\u00e9.",
            "nullable": true
          },
          "period_start": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "D\u00e9but de la p\u00e9riode couverte.",
            "nullable": true
          },
          "period_end": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Fin de la p\u00e9riode couverte.",
            "nullable": true
          },
          "vat_rate": {
            "type": "number",
            "default": null,
            "description": "Taux de TVA appliqu\u00e9 (0 si non soumis).",
            "nullable": true
          }
        },
        "required": [
          "amount",
          "date",
          "domain",
          "id",
          "parent_id"
        ]
      },
      "BalanceUpdateInput": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "description": "Nouveau montant sign\u00e9."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Nouvelle date de l'\u00e9criture."
          },
          "type": {
            "type": "string",
            "description": "Nouveau type d'\u00e9criture."
          },
          "comment": {
            "type": "string",
            "maxLength": 255,
            "description": "Nouveau commentaire.",
            "nullable": true
          },
          "vat_rate": {
            "type": "number",
            "enum": [
              0,
              2.1,
              5.5,
              8.5,
              10,
              20
            ],
            "description": "Nouveau taux de TVA."
          },
          "period_start": {
            "type": "string",
            "format": "date",
            "description": "Nouveau d\u00e9but de la p\u00e9riode couverte.",
            "nullable": true
          },
          "period_end": {
            "type": "string",
            "format": "date",
            "description": "Nouvelle fin de la p\u00e9riode couverte.",
            "nullable": true
          }
        }
      },
      "BalanceUpdateResult": {
        "type": "object",
        "properties": {
          "balance": {
            "description": "L'\u00e9criture \u00e0 jour (l'originale modifi\u00e9e, ou sa rempla\u00e7ante).",
            "allOf": [
              {
                "$ref": "#/components/schemas/BalanceDetail"
              }
            ]
          },
          "reversal": {
            "default": null,
            "description": "Contrepassation cr\u00e9\u00e9e si l'\u00e9criture originale \u00e9tait comptabilis\u00e9e dans une p\u00e9riode close.",
            "anyOf": [
              {
                "$ref": "#/components/schemas/BalanceDetail"
              },
              {
                "type": "object",
                "nullable": true
              }
            ]
          }
        },
        "required": [
          "balance"
        ]
      },
      "FileSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du document."
          },
          "filename": {
            "type": "string",
            "default": null,
            "description": "Nom du fichier.",
            "nullable": true
          },
          "tag": {
            "type": "string",
            "default": null,
            "description": "Cat\u00e9gorie du document (bail, quittance, edl, dpe\u2026).",
            "nullable": true
          },
          "added_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date d'ajout.",
            "nullable": true
          },
          "tenant_id": {
            "type": "integer",
            "default": null,
            "description": "Identifiant du bail rattach\u00e9.",
            "nullable": true
          },
          "property_id": {
            "type": "integer",
            "default": null,
            "description": "Identifiant du bien rattach\u00e9.",
            "nullable": true
          },
          "size": {
            "type": "integer",
            "default": null,
            "description": "Taille du fichier en octets.",
            "nullable": true
          }
        },
        "required": [
          "id"
        ]
      },
      "FileDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant du document."
          },
          "filename": {
            "type": "string",
            "default": null,
            "description": "Nom du fichier.",
            "nullable": true
          },
          "tag": {
            "type": "string",
            "default": null,
            "description": "Cat\u00e9gorie du document (bail, quittance, edl, dpe\u2026).",
            "nullable": true
          },
          "added_date": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date d'ajout.",
            "nullable": true
          },
          "tenant_id": {
            "type": "integer",
            "default": null,
            "description": "Identifiant du bail rattach\u00e9.",
            "nullable": true
          },
          "property_id": {
            "type": "integer",
            "default": null,
            "description": "Identifiant du bien rattach\u00e9.",
            "nullable": true
          },
          "size": {
            "type": "integer",
            "default": null,
            "description": "Taille du fichier en octets.",
            "nullable": true
          },
          "mimetype": {
            "type": "string",
            "default": null,
            "description": "Type MIME du fichier.",
            "nullable": true
          },
          "valid_until": {
            "type": "string",
            "format": "date",
            "default": null,
            "description": "Date de validit\u00e9 du document (diagnostics\u2026).",
            "nullable": true
          },
          "shared": {
            "type": "boolean",
            "default": false,
            "description": "Vrai si le document est partag\u00e9 avec les locataires."
          },
          "download_url": {
            "type": "string",
            "default": null,
            "description": "URL de t\u00e9l\u00e9chargement sign\u00e9e, valable quelques minutes.",
            "nullable": true
          }
        },
        "required": [
          "id"
        ]
      },
      "_FileUpload": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary"
          }
        },
        "required": [
          "file"
        ]
      },
      "RentRevisionInput": {
        "type": "object",
        "properties": {
          "landlord_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Nom ou raison sociale du bailleur."
          },
          "landlord_address": {
            "type": "string",
            "minLength": 1,
            "maxLength": 400,
            "description": "Adresse du bailleur (une ligne)."
          },
          "tenant_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Nom du locataire."
          },
          "property_address": {
            "type": "string",
            "minLength": 1,
            "maxLength": 400,
            "description": "Adresse du logement lou\u00e9."
          },
          "lease_start_date": {
            "type": "string",
            "format": "date",
            "description": "Date de d\u00e9but du bail (ex. `2022-09-01`)."
          },
          "irl_quarter": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4,
            "description": "Trimestre de l'indice de r\u00e9f\u00e9rence des loyers inscrit au bail (1\u20134)."
          },
          "rent_excluding_charges": {
            "type": "number",
            "minimum": 0.01,
            "description": "Loyer actuel hors charges, en euros."
          },
          "charges": {
            "type": "number",
            "default": 0.0,
            "minimum": 0.0,
            "description": "Charges actuelles (d\u00e9faut 0)."
          },
          "flat_rate_charges": {
            "type": "boolean",
            "default": false,
            "description": "`true` si les charges sont au forfait (elles sont alors r\u00e9vis\u00e9es aussi)."
          },
          "postal_code": {
            "type": "string",
            "default": null,
            "description": "Code postal du logement (Corse/outre-mer \u2192 s\u00e9rie IRL locale).",
            "nullable": true
          },
          "landlord_city": {
            "type": "string",
            "default": null,
            "description": "Ville de signature du courrier.",
            "nullable": true
          },
          "billing_name": {
            "type": "string",
            "default": null,
            "maxLength": 200,
            "description": "Raison sociale pour la facture (achat pour une entreprise).",
            "nullable": true
          },
          "billing_address": {
            "type": "string",
            "default": null,
            "maxLength": 400,
            "description": "Adresse de facturation.",
            "nullable": true
          }
        },
        "required": [
          "irl_quarter",
          "landlord_address",
          "landlord_name",
          "lease_start_date",
          "property_address",
          "rent_excluding_charges",
          "tenant_name"
        ]
      },
      "RentRevisionCalculation": {
        "type": "object",
        "properties": {
          "quarter": {
            "type": "integer",
            "description": "Trimestre IRL utilis\u00e9 (1\u20134)."
          },
          "previous_index": {
            "type": "number",
            "description": "Indice IRL appliqu\u00e9 au loyer actuel."
          },
          "previous_index_year": {
            "type": "integer",
            "description": "Ann\u00e9e de l'ancien indice."
          },
          "new_index": {
            "type": "number",
            "description": "Nouvel indice IRL publi\u00e9 par l'INSEE."
          },
          "new_index_year": {
            "type": "integer",
            "description": "Ann\u00e9e du nouvel indice."
          },
          "new_rent": {
            "type": "number",
            "description": "Nouveau loyer hors charges, en euros."
          },
          "new_charges": {
            "type": "number",
            "description": "Nouvelles charges (r\u00e9vis\u00e9es seulement si forfait)."
          },
          "new_total": {
            "type": "number",
            "description": "Nouveau loyer charges comprises, en euros."
          }
        },
        "required": [
          "new_charges",
          "new_index",
          "new_index_year",
          "new_rent",
          "new_total",
          "previous_index",
          "previous_index_year",
          "quarter"
        ]
      },
      "PublicPrice": {
        "type": "object",
        "properties": {
          "amount_incl_tax": {
            "type": "number",
            "description": "Prix TTC, en euros."
          },
          "currency": {
            "type": "string",
            "description": "Devise (`EUR`)."
          }
        },
        "required": [
          "amount_incl_tax",
          "currency"
        ]
      },
      "RentRevisionCheckoutResponse": {
        "type": "object",
        "properties": {
          "calculation": {
            "description": "Calcul de r\u00e9vision, fig\u00e9 au moment du devis : le document livr\u00e9 correspond toujours \u00e0 ce qui a \u00e9t\u00e9 pay\u00e9.",
            "allOf": [
              {
                "$ref": "#/components/schemas/RentRevisionCalculation"
              }
            ]
          },
          "price": {
            "description": "Prix du courrier certifi\u00e9.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicPrice"
              }
            ]
          },
          "checkout_url": {
            "type": "string",
            "description": "Page de paiement Stripe Checkout, \u00e0 faire r\u00e9gler par un humain (carte uniquement, aucune autre information demand\u00e9e)."
          },
          "session_id": {
            "type": "string",
            "description": "Identifiant de la Checkout Session Stripe (`cs_\u2026`)."
          },
          "document_url": {
            "type": "string",
            "description": "URL de t\u00e9l\u00e9chargement du PDF ; r\u00e9pond 402 tant que le paiement n'est pas confirm\u00e9."
          },
          "invoice_url": {
            "type": "string",
            "description": "URL de t\u00e9l\u00e9chargement de la facture (optionnelle, sur demande)."
          },
          "documentation": {
            "type": "string",
            "description": "Documentation de l'API publique."
          },
          "instructions": {
            "type": "string",
            "description": "Marche \u00e0 suivre, \u00e0 destination d'un agent."
          }
        },
        "required": [
          "calculation",
          "checkout_url",
          "document_url",
          "documentation",
          "instructions",
          "invoice_url",
          "price",
          "session_id"
        ]
      },
      "PaymentRequiredResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "`Payment not confirmed`."
          },
          "checkout_url": {
            "type": "string",
            "description": "Page de paiement Stripe \u00e0 faire r\u00e9gler pour d\u00e9bloquer le t\u00e9l\u00e9chargement."
          }
        },
        "required": [
          "checkout_url",
          "error"
        ]
      }
    },
    "responses": {
      "UNPROCESSABLE_CONTENT": {
        "description": "Unprocessable Content",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "DEFAULT_ERROR": {
        "description": "Default error response",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "personalAccessToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "Personal access token (`qmcp_\u2026`), g\u00e9n\u00e9r\u00e9 depuis Mon compte."
      }
    }
  }
}