{
  "openapi": "3.1.0",
  "info": {
    "title": "Marcel API",
    "version": "1.0.0",
    "description": "Infrastructure de calcul patrimonial explicable. Marcel transforme un patrimoine en trajectoire financière versionnée et rejouable. Chaque résultat est certifié CGI/BOFiP et traçable jusqu'à sa règle réglementaire.",
    "contact": {
      "name": "Marcel Pro",
      "email": "quilletalexandre5@gmail.com",
      "url": "https://marcel-retraite.fr/developer"
    },
    "license": {
      "name": "Commercial",
      "url": "https://marcel-retraite.fr/legal"
    }
  },
  "servers": [
    {
      "url": "https://marcel-retraite.fr",
      "description": "Production"
    }
  ],
  "security": [
    { "BearerAuth": [] }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Clé API Marcel. Format : mk_live_xxx (production) ou mk_test_xxx (sandbox). Obtenir une clé : quilletalexandre5@gmail.com"
      }
    },
    "schemas": {
      "Profile": {
        "type": "object",
        "required": ["birthYear"],
        "properties": {
          "birthYear": { "type": "integer", "example": 1980, "description": "Année de naissance" },
          "retirementTargetAge": { "type": "integer", "example": 67, "default": 67, "description": "Âge cible de départ à la retraite" },
          "monthlyIncome": { "type": "number", "example": 5000, "description": "Revenu mensuel net actuel" },
          "annualTaxableIncome": { "type": "number", "example": 57600, "description": "Revenu net imposable annuel (pour calcul TMI)" },
          "estimatedPension": { "type": "number", "example": 2140, "description": "Pension nette mensuelle estimée. Si absent, estimée à 45% du salaire mensuel." },
          "targetRetirementIncome": { "type": "number", "example": 3500, "description": "Objectif de revenu mensuel à la retraite. Si absent, estimé à 70% du salaire." }
        }
      },
      "Asset": {
        "type": "object",
        "required": ["type", "id", "inputs"],
        "properties": {
          "type": {
            "type": "string",
            "enum": ["LMNP", "RP", "PER", "AV"],
            "description": "Type d'actif"
          },
          "id": { "type": "string", "example": "lmnp-paris", "description": "Identifiant unique de l'actif" },
          "inputs": {
            "type": "object",
            "description": "Paramètres de l'actif selon son type",
            "example": {
              "prixAcquisition": 250000,
              "loyersAnnuels": 12000,
              "creditInitialAmount": 200000
            }
          }
        }
      },
      "RetirementSource": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "example": "pension" },
          "label": { "type": "string", "example": "Pension de retraite" },
          "monthly": { "type": "number", "example": 2140 }
        }
      },
      "TimelineEvent": {
        "type": "object",
        "properties": {
          "year": { "type": "integer", "example": 2042 },
          "type": { "type": "string", "example": "END_MORTGAGE" },
          "label": { "type": "string", "example": "Fin crédit LMNP" },
          "impactMonthly": { "type": "number", "example": 1986 },
          "assetId": { "type": "string", "example": "lmnp-paris" }
        }
      },
      "Audit": {
        "type": "object",
        "properties": {
          "ruleIds": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["FR.LMNP.2026.004", "FR.PER.2026.001", "FR.RETRAITE.2026.001"]
          },
          "rulesVersion": { "type": "string", "example": "2026_v1" },
          "snapshotId": { "type": "string", "example": "twin_abc123" },
          "explanationUrl": { "type": "string", "example": "/api/v1/explain-score/twin_abc123" },
          "replayable": { "type": "boolean", "example": true },
          "deterministic": { "type": "boolean", "example": true }
        }
      },
      "FinancialTwinResponse": {
        "type": "object",
        "properties": {
          "snapshotId": { "type": "string", "example": "twin_abc123" },
          "confidence": { "type": "number", "example": 0.72, "description": "0-1 selon complétude des données" },
          "dataCompleteness": { "type": "string", "enum": ["minimal", "standard", "full"] },
          "retirementIncome": {
            "type": "object",
            "properties": {
              "monthly": { "type": "number", "example": 3040 },
              "sources": { "type": "array", "items": { "$ref": "#/components/schemas/RetirementSource" } }
            }
          },
          "gap": {
            "type": "object",
            "properties": {
              "monthly": { "type": "number", "example": 460 },
              "hasGap": { "type": "boolean", "example": true },
              "objectiveMonthly": { "type": "number", "example": 3500 },
              "drivers": { "type": "array", "items": { "type": "string" }, "example": ["PER insuffisant ou absent"] }
            }
          },
          "timeline": { "type": "array", "items": { "$ref": "#/components/schemas/TimelineEvent" } },
          "scenarios": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": { "type": "string" },
                "modification": { "type": "string" },
                "retirementIncomeMonthly": { "type": "number" },
                "deltaMonthly": { "type": "number" }
              }
            }
          },
          "scores": {
            "type": "object",
            "properties": {
              "marcel": { "type": "number", "example": 3040 },
              "wrs": { "type": "string", "enum": ["ROBUSTE", "SOLIDE", "PRECAIRE", "FRAGILE"] },
              "wrsScore": { "type": "integer", "example": 72 }
            }
          },
          "audit": { "$ref": "#/components/schemas/Audit" },
          "avertissements": { "type": "array", "items": { "type": "string" } }
        }
      },
      "GapResponse": {
        "type": "object",
        "properties": {
          "hasGap": { "type": "boolean", "example": true },
          "gapMonthly": { "type": "number", "example": 460 },
          "objectiveMonthly": { "type": "number", "example": 3500 },
          "currentIncomeMonthly": { "type": "number", "example": 3040 },
          "sources": {
            "type": "object",
            "properties": {
              "pension": { "type": "number", "example": 2140 },
              "assets": { "type": "number", "example": 900 },
              "total": { "type": "number", "example": 3040 }
            }
          },
          "toFillGap": {
            "type": "object",
            "nullable": true,
            "properties": {
              "perMonthlyEffort": { "type": "number", "nullable": true, "example": 350 },
              "avMonthlyRequired": { "type": "number", "nullable": true, "example": 391 },
              "horizon": { "type": "integer", "example": 23 },
              "note": { "type": "string" }
            }
          },
          "drivers": { "type": "array", "items": { "type": "string" } },
          "confidence": { "type": "number", "example": 0.60 },
          "avertissements": { "type": "array", "items": { "type": "string" } }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "example": "Clé API invalide" },
          "endpoint": { "type": "string", "example": "v1/financial-twin" },
          "documentation": { "type": "string", "example": "https://marcel-retraite.fr/developer" }
        }
      }
    }
  },
  "paths": {
    "/api/v1/financial-twin": {
      "post": {
        "operationId": "financialTwin",
        "summary": "Jumeau financier complet",
        "description": "Endpoint principal. Un seul appel retourne l'ensemble du contexte patrimonial : revenu retraite, gap, timeline, scénarios, scores et audit réglementaire. Recommandé pour les agents IA.",
        "tags": ["Core"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["profile"],
                "properties": {
                  "profile": { "$ref": "#/components/schemas/Profile" },
                  "assets": {
                    "type": "array",
                    "items": { "$ref": "#/components/schemas/Asset" },
                    "description": "Actifs patrimoniaux. Optionnel — sans actifs, calcul basé sur pension seule."
                  },
                  "scenarioLabel": { "type": "string", "example": "Simulation Finary Q3 2026" }
                }
              },
              "example": {
                "profile": {
                  "birthYear": 1980,
                  "monthlyIncome": 5000,
                  "annualTaxableIncome": 57600,
                  "targetRetirementIncome": 3500
                },
                "assets": [
                  {
                    "type": "LMNP",
                    "id": "lmnp-paris",
                    "inputs": { "prixAcquisition": 250000, "loyersAnnuels": 12000, "creditInitialAmount": 200000 }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Jumeau financier calculé",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FinancialTwinResponse" } } }
          },
          "401": { "description": "Clé API manquante ou invalide", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Quota mensuel atteint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/find-gap": {
      "post": {
        "operationId": "findGap",
        "summary": "Identifier le gap retraite",
        "description": "Calcule le gap entre le revenu retraite projeté et l'objectif. 3 inputs suffisent. Calcule également l'effort d'épargne (PER/AV) pour combler le gap.",
        "tags": ["Core"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["birthYear", "monthlyIncome"],
                "properties": {
                  "birthYear": { "type": "integer", "example": 1980 },
                  "monthlyIncome": { "type": "number", "example": 5000 },
                  "annualTaxableIncome": { "type": "number", "example": 57600 },
                  "estimatedPension": { "type": "number", "example": 2140 },
                  "targetRetirementIncome": { "type": "number", "example": 3500 },
                  "retirementAge": { "type": "integer", "example": 67 },
                  "existingAssets": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": { "type": "string" },
                        "estimatedMonthlyIncome": { "type": "number" }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Gap calculé", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GapResponse" } } } },
          "401": { "description": "Non autorisé", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/revenue-at-retirement": {
      "post": {
        "operationId": "revenueAtRetirement",
        "summary": "Calcul du revenu net à la retraite",
        "description": "Calcule le revenu mensuel net à la retraite par source (pension, PER, LMNP, AV, SCPI). Applique la fiscalité réelle : IR, PS 17,2%, PFL 7,5%, abattements.",
        "tags": ["Core"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["pension"],
                "properties": {
                  "pension": {
                    "type": "object",
                    "required": ["monthlyNet"],
                    "properties": { "monthlyNet": { "type": "number", "example": 2140 } }
                  },
                  "per": {
                    "type": "object",
                    "properties": {
                      "capitalAtRetirement": { "type": "number", "example": 189485 },
                      "yearsOfFractionation": { "type": "integer", "example": 10 },
                      "taxableIncomeAtRetirement": { "type": "number", "example": 30000 }
                    }
                  },
                  "lmnp": {
                    "type": "object",
                    "properties": { "annualNetIncome": { "type": "number", "example": 10800 } }
                  },
                  "av": {
                    "type": "object",
                    "properties": {
                      "capitalAtRetirement": { "type": "number", "example": 200000 },
                      "yearsOfDrawdown": { "type": "integer", "example": 10 }
                    }
                  },
                  "scpi": {
                    "type": "object",
                    "properties": { "annualDistribution": { "type": "number", "example": 5000 } }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revenu calculé par source",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "monthlyRetirementIncome": { "type": "number", "example": 4260 },
                    "sources": { "type": "array", "items": { "$ref": "#/components/schemas/RetirementSource" } },
                    "ruleIds": { "type": "array", "items": { "type": "string" } },
                    "confidence": { "type": "number" },
                    "avertissements": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          },
          "401": { "description": "Non autorisé", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/compare-scenarios": {
      "post": {
        "operationId": "compareScenarios",
        "summary": "Comparer des scénarios patrimoniaux",
        "description": "Compare jusqu'à N scénarios. Aucune recommandation — neutralité AMF/CIF. Retourne le tableau des résultats avec deltas et le meilleur scénario identifié.",
        "tags": ["Core"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["base", "scenarios"],
                "properties": {
                  "base": {
                    "type": "object",
                    "required": ["monthlyIncome", "annualTaxableIncome", "estimatedPension", "birthYear"],
                    "properties": {
                      "monthlyIncome": { "type": "number", "example": 5000 },
                      "annualTaxableIncome": { "type": "number", "example": 57600 },
                      "estimatedPension": { "type": "number", "example": 2140 },
                      "birthYear": { "type": "integer", "example": 1980 },
                      "retirementAge": { "type": "integer", "example": 67 },
                      "existingPerMonthly": { "type": "number", "example": 0 }
                    }
                  },
                  "scenarios": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": ["label", "modification"],
                      "properties": {
                        "label": { "type": "string", "example": "Départ 64 ans" },
                        "modification": {
                          "type": "object",
                          "required": ["type", "value"],
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": ["RETIREMENT_AGE", "PER_CONTRIBUTION", "AV_CONTRIBUTION", "LMNP_LOYERS"]
                            },
                            "value": { "type": "number", "example": 64 }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scénarios comparés",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "base": { "type": "object", "properties": { "retirementIncomeMonthly": { "type": "number" } } },
                    "scenarios": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": { "type": "string" },
                          "modification": { "type": "string" },
                          "retirementIncomeMonthly": { "type": "number" },
                          "deltaMonthly": { "type": "number" },
                          "deltaPercent": { "type": "number" },
                          "note": { "type": "string" }
                        }
                      }
                    },
                    "bestScenario": { "type": "object" },
                    "avertissements": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          },
          "401": { "description": "Non autorisé", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Core",
      "description": "Endpoints principaux — orientés intentions métier. Chaque endpoint répond à une question patrimoniale précise."
    }
  ]
}
