{
  "openapi": "3.1.0",
  "info": {
    "title": "Animations API — Contrat Pédagogique · Agents IA",
    "version": "1.0.0",
    "description": "Interface programmatique complète de la plateforme Animations. Expose le catalogue de briques d'animation scoutes, le répertoire de chants avec notations ABC, les collections thématiques, le forum communautaire, les profils et le système de réputation. Conçue pour une interaction directe sans surcouche par les agents d'IA (OpenAI Function Calling, LangChain, Antigravity) et pour alimenter le serveur MCP dérivé.",
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/licenses/MIT"
    }
  },
  "servers": [
    {
      "url": "/api",
      "description": "Serveur relatif (Cloudflare Pages Functions local ou déployé)"
    },
    {
      "url": "http://localhost:4321/api",
      "description": "Environnement de développement local Astro + Cloudflare"
    },
    {
      "url": "https://animations.pages.dev/api",
      "description": "Production Cloudflare Pages"
    }
  ],
  "tags": [
    {
      "name": "Wiki - Briques",
      "description": "Catalogue vivant des briques d'activités, jeux, veillées, soumission et cycle de vie."
    },
    {
      "name": "Wiki - Recherche",
      "description": "Recherche plein texte et filtres multicritères sur les activités."
    },
    {
      "name": "Chants",
      "description": "Répertoire de chants, paroles, partitions au format vectoriel ABC et méta-données."
    },
    {
      "name": "Collections",
      "description": "Packs d'activités thématiques, sélections publiques et signets personnels."
    },
    {
      "name": "Forum Communautaire",
      "description": "Espaces d'échanges, questions pédagogiques, retours d'expérience et réponses."
    },
    {
      "name": "Membres",
      "description": "Fiches de contributeurs, réputation calculée, badges et historique de contributions."
    },
    {
      "name": "Modération",
      "description": "Signalement de contenus inappropriés et retours d'expérience utilisateurs."
    },
    {
      "name": "Statistiques",
      "description": "Indicateurs publics consultables sur /stats : santé du catalogue, usage produit et acquisition des membres. Toute donnée visible par un humain sur une page publique est exposée ici (règle de parité Humain / API)."
    },
    {
      "name": "Système",
      "description": "Taxonomie des types et synchronisation incrémentale du catalogue."
    },
    {
      "name": "Rédaction",
      "description": "Diagnostics linguistiques, règles typographiques, vocabulaire GIFAS et pré-lecture JEV."
    },
    {
      "name": "Guides",
      "description": "Guides pédagogiques, conventions rédactionnelles et règles de contribution."
    },
    {
      "name": "Compte",
      "description": "Gestion du compte connecté : profil wiki, notifications, onboarding, export et suppression RGPD, codes de secours."
    },
    {
      "name": "Développeurs",
      "description": "Applications d'API personnelles et jetons porteurs (Bearer an_live_...) rattachés au compte connecté."
    },
    {
      "name": "Banque d'images",
      "description": "Recherche de photographies libres de droits Pixabay pour l'illustration des briques ou collections."
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Jeton d'autorisation pour agents autonomes et scripts CLI (transmis dans l'en-tête Authorization: Bearer <token>)."
      },
      "CookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "better-auth.session_token",
        "description": "Session Better Auth active pour les requêtes émises depuis un navigateur web."
      }
    },
    "schemas": {
      "ErreurReponse": {
        "type": "object",
        "required": [
          "erreur"
        ],
        "properties": {
          "erreur": {
            "type": "string",
            "description": "Message explicatif de l'erreur.",
            "example": "Brique introuvable."
          }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "required": [
          "total",
          "page",
          "limite"
        ],
        "properties": {
          "total": {
            "type": "integer",
            "description": "Nombre total d'éléments disponibles."
          },
          "page": {
            "type": "integer",
            "description": "Numéro de page courante (1-indexé)."
          },
          "limite": {
            "type": "integer",
            "description": "Nombre d'éléments par page."
          }
        }
      },
      "PhotoBanque": {
        "type": "object",
        "required": [
          "id",
          "miniatureUrl",
          "grandeUrl",
          "photographe",
          "pageUrl"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identifiant Pixabay."
          },
          "miniatureUrl": {
            "type": "string",
            "description": "URL de la vignette d'aperçu."
          },
          "miniatureLargeur": {
            "type": "integer"
          },
          "miniatureHauteur": {
            "type": "integer"
          },
          "grandeUrl": {
            "type": "string",
            "description": "URL de l'image haute résolution."
          },
          "largeurOriginale": {
            "type": "integer"
          },
          "hauteurOriginale": {
            "type": "integer"
          },
          "photographe": {
            "type": "string",
            "description": "Nom du photographe."
          },
          "pageUrl": {
            "type": "string",
            "description": "Lien de la page Pixabay."
          },
          "tags": {
            "type": "string",
            "description": "Mots-clés associés."
          }
        }
      },
      "ResultatRechercheBanque": {
        "type": "object",
        "required": [
          "photos",
          "total",
          "page",
          "parPage"
        ],
        "properties": {
          "photos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PhotoBanque"
            }
          },
          "total": {
            "type": "integer",
            "description": "Nombre total de photos trouvées."
          },
          "page": {
            "type": "integer",
            "description": "Page courante."
          },
          "parPage": {
            "type": "integer",
            "description": "Nombre de photos par page."
          }
        }
      },
      "Brique": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "titre",
          "type",
          "statut"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "bourse-aux-epices"
          },
          "slug": {
            "type": "string",
            "example": "bourse-aux-epices"
          },
          "titre": {
            "type": "string",
            "example": "Bourse aux Épices"
          },
          "type": {
            "type": "string",
            "description": "Type d'activité (chaîne libre et extensible). Les types réellement disponibles sont ceux de la taxonomie gérée par la modération (voir listerTypesBriques).",
            "example": "Jeu"
          },
          "statut": {
            "type": "string",
            "enum": [
              "brouillon",
              "en_relecture",
              "publie",
              "archive"
            ],
            "example": "publie"
          },
          "version": {
            "type": "integer",
            "example": 2
          },
          "noteMoyenne": {
            "type": "number",
            "example": 4.8
          },
          "nbVotes": {
            "type": "integer",
            "example": 15
          },
          "dureeMinutes": {
            "type": "integer",
            "example": 90
          },
          "nbParticipantsMin": {
            "type": "integer",
            "example": 12
          },
          "nbParticipantsMax": {
            "type": "integer",
            "example": 40
          },
          "trancheAge": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer",
                "example": 8
              },
              "max": {
                "type": "integer",
                "example": 14
              }
            }
          },
          "materiel": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "Foulards",
              "Cartes de denrées",
              "Sifflet"
            ]
          },
          "description": {
            "type": "string",
            "description": "Description et déroulement complet de l'animation."
          },
          "securite": {
            "type": "string",
            "description": "Points de vigilance et consignes de sécurité."
          },
          "champs_specifiques": {
            "type": "object",
            "additionalProperties": true,
            "description": "Blocs dynamiques du type d'activité (définis par la modération), indexés par identifiant de bloc."
          }
        }
      },
      "BrouillonBrique": {
        "type": "object",
        "required": [
          "titre",
          "type"
        ],
        "properties": {
          "titre": {
            "type": "string",
            "example": "Grand Relais des Messagers"
          },
          "type": {
            "type": "string",
            "example": "Jeu"
          },
          "resume": {
            "type": "string",
            "example": "Jeu de vitesse et de stratégie par patrouilles."
          },
          "dureeMinutes": {
            "type": "integer",
            "example": 45
          },
          "nbParticipantsMin": {
            "type": "integer",
            "example": 8
          },
          "nbParticipantsMax": {
            "type": "integer",
            "example": 30
          },
          "description": {
            "type": "string",
            "example": "Règles et organisation détaillées..."
          },
          "materiel": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "Bâtons témoins",
              "Chronomètre"
            ]
          },
          "securite": {
            "type": "string",
            "example": "Délimiter un terrain plat non glissant."
          },
          "champs_specifiques": {
            "type": "object",
            "additionalProperties": true,
            "description": "Contenu des blocs spécifiques au type d'activité, indexé par identifiant de bloc (voir listerTypesBriques)."
          }
        }
      },
      "TypeBrique": {
        "type": "object",
        "required": [
          "id",
          "nom",
          "champs"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "type-jeu"
          },
          "nom": {
            "type": "string",
            "example": "Jeu"
          },
          "description": {
            "type": "string",
            "example": "Jeux de plaine, grands jeux de rôle, relais."
          },
          "icone": {
            "type": "string",
            "example": "Dices"
          },
          "champs": {
            "type": "array",
            "description": "Définition ordonnée des blocs du type.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "label",
                "typeChamp",
                "renduFiche",
                "obligatoire"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "but_du_jeu"
                },
                "label": {
                  "type": "string",
                  "example": "But du jeu"
                },
                "typeChamp": {
                  "type": "string",
                  "enum": [
                    "texte_court",
                    "texte_long",
                    "markdown_riche",
                    "liste_etapes",
                    "liste_puces",
                    "liste_paires",
                    "alerte_encadree"
                  ]
                },
                "renduFiche": {
                  "type": "string",
                  "enum": [
                    "standard",
                    "exergue",
                    "chronologie",
                    "alerte"
                  ]
                },
                "obligatoire": {
                  "type": "boolean"
                },
                "descriptionCartes": {
                  "type": "boolean",
                  "description": "Bloc « description des cartes » : son contenu alimente la description courte affichée sur les cartes. Exactement un par type, obligatoire et de type texte court."
                },
                "description": {
                  "type": "string"
                },
                "placeholder": {
                  "type": "string"
                },
                "longueurMin": {
                  "type": "integer"
                },
                "tonImpersonnel": {
                  "type": "boolean"
                },
                "actif": {
                  "type": "boolean"
                }
              }
            }
          },
          "creeLe": {
            "type": "string",
            "description": "Date de création ISO 8601."
          },
          "nbBriques": {
            "type": "integer",
            "description": "Nombre de briques publiées de ce type."
          }
        }
      },
      "Chant": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "titre",
          "energie",
          "paroles"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "chant-01"
          },
          "slug": {
            "type": "string",
            "example": "le-vieux-chalet"
          },
          "titre": {
            "type": "string",
            "example": "Le Vieux Chalet"
          },
          "energie": {
            "type": "string",
            "enum": [
              "Calme",
              "Entrainant",
              "Dynamique"
            ],
            "example": "Calme"
          },
          "auteurParoles": {
            "type": "string",
            "example": "Traditionnel"
          },
          "auteurMusique": {
            "type": "string",
            "example": "Traditionnel"
          },
          "statutDroits": {
            "type": "string",
            "enum": [
              "domaine_public",
              "traditionnel",
              "licence_libre",
              "oeuvre_protegee",
              "creation_originale"
            ],
            "description": "Statut juridique déclaré. Preuves obligatoires : décès + source pour le domaine public, type + acte de licence pour une licence libre, recueil pour un chant traditionnel.",
            "example": "traditionnel"
          },
          "preuvesDroits": {
            "type": "object",
            "description": "Preuves de droits structurées accompagnant le statut déclaré.",
            "properties": {
              "anneeDecesAuteur": {
                "type": "integer",
                "description": "Domaine public : année de décès de l'auteur.",
                "example": 1954
              },
              "sourceVerificationUrl": {
                "type": "string",
                "description": "Domaine public : lien de vérification (Wikidata, BnF, IMSLP…).",
                "example": "https://www.wikidata.org/wiki/Q1234"
              },
              "licenceType": {
                "type": "string",
                "enum": [
                  "CC0",
                  "CC-BY",
                  "CC-BY-SA",
                  "CC-BY-NC",
                  "CC-BY-NC-SA",
                  "autre"
                ],
                "description": "Licence libre : identifiant de licence.",
                "example": "CC-BY-SA"
              },
              "licenceUrl": {
                "type": "string",
                "description": "Licence libre : lien de l'acte de licence.",
                "example": "https://creativecommons.org/licenses/by-sa/4.0/"
              },
              "recueilAchatUrl": {
                "type": "string",
                "description": "Œuvre protégée : lien vers le recueil officiel."
              },
              "note": {
                "type": "string",
                "description": "Note sur l'origine de la déclaration."
              }
            }
          },
          "paroles": {
            "type": "string",
            "description": "Paroles complètes avec couplets et refrains."
          },
          "abcNotation": {
            "type": "string",
            "description": "Partition au format standard ABC music notation pour abcjs.",
            "example": "X:1\\nT:Le Vieux Chalet\\nM:3/4\\nL:1/4\\nK:G\\nG A B | d2 B |..."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "Montagne",
              "Soirée",
              "Tradition"
            ]
          },
          "tonalite": {
            "type": "string",
            "example": "Sol Majeur"
          }
        }
      },
      "Collection": {
        "type": "object",
        "required": [
          "id",
          "nom",
          "briqueIds"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "col-camp-2026"
          },
          "nom": {
            "type": "string",
            "example": "Camp d'Été 2026 — Veillées · Nuit"
          },
          "descriptionCourte": {
            "type": "string",
            "example": "Sélection d'activités nocturnes."
          },
          "briqueIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "bourse-aux-epices",
              "douaniers-et-contrebandiers"
            ]
          },
          "chantIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "frere-loup-dans-notre-sizaine"
            ]
          },
          "estPublique": {
            "type": "boolean",
            "example": true
          },
          "estFavoris": {
            "type": "boolean",
            "description": "Collection « Mes favoris » du membre (une seule par membre, créée par le serveur)."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ForumThread": {
        "type": "object",
        "required": [
          "id",
          "titre",
          "corps",
          "categorie"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "thread-01"
          },
          "titre": {
            "type": "string",
            "example": "Comment adapter le jeu des douaniers sous la pluie ?"
          },
          "corps": {
            "type": "string",
            "example": "Nous sommes en camp sous tente et..."
          },
          "categorie": {
            "type": "string",
            "example": "Pédagogie"
          },
          "scoreVotes": {
            "type": "integer",
            "example": 8
          },
          "nbReponses": {
            "type": "integer",
            "example": 3
          }
        }
      },
      "MembreProfil": {
        "type": "object",
        "required": [
          "userId",
          "surnom",
          "role",
          "rang",
          "reputationPoints"
        ],
        "properties": {
          "userId": {
            "type": "string",
            "example": "usr-01"
          },
          "surnom": {
            "type": "string",
            "example": "Guillaume_SGDF"
          },
          "role": {
            "type": "string",
            "example": "animateur"
          },
          "rang": {
            "type": "string",
            "enum": [
              "novice",
              "editeur",
              "relecteur",
              "admin"
            ],
            "example": "editeur"
          },
          "reputationPoints": {
            "type": "integer",
            "example": 340
          }
        }
      },
      "StatsWiki": {
        "type": "object",
        "required": [
          "nbBriquesPubliees",
          "nbRevisionsAcceptees",
          "nbVotes",
          "nbContributeurs"
        ],
        "properties": {
          "nbBriquesPubliees": {
            "type": "integer",
            "example": 128
          },
          "nbBriquesEnRelecture": {
            "type": "integer",
            "example": 7
          },
          "nbRevisionsAcceptees": {
            "type": "integer",
            "example": 312
          },
          "nbVotes": {
            "type": "integer",
            "example": 540
          },
          "nbContributeurs": {
            "type": "integer",
            "example": 42
          },
          "nbSignalementsOuverts": {
            "type": "integer",
            "example": 1
          },
          "activite30Jours": {
            "type": "integer",
            "example": 23
          },
          "topContributeurs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "userId": {
                  "type": "string"
                },
                "surnom": {
                  "type": "string"
                },
                "points": {
                  "type": "integer"
                }
              }
            }
          },
          "dernieresBriques": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Brique"
            }
          }
        }
      },
      "SiteInfoWiki": {
        "type": "object",
        "properties": {
          "moteur": {
            "type": "string",
            "example": "animations-wiki"
          },
          "nom": {
            "type": "string",
            "example": "Animations"
          },
          "version": {
            "type": "integer",
            "example": 1
          },
          "url": {
            "type": "string",
            "example": "https://animations.pages.dev"
          },
          "date": {
            "type": "string",
            "example": "2026-09-25T10:00:00.000Z"
          },
          "nbBriquesPubliees": {
            "type": "integer"
          },
          "nbRevisions": {
            "type": "integer"
          },
          "nbContributeurs": {
            "type": "integer"
          },
          "nbVotes": {
            "type": "integer"
          },
          "nbTypes": {
            "type": "integer"
          },
          "nbSignalementsOuverts": {
            "type": "integer"
          }
        }
      },
      "RapportAnalytics": {
        "type": "object",
        "required": [
          "jours",
          "depuis",
          "totaux",
          "serie"
        ],
        "properties": {
          "jours": {
            "type": "integer",
            "example": 30
          },
          "depuis": {
            "type": "string",
            "example": "2026-08-26T10:00:00.000Z"
          },
          "totaux": {
            "type": "object",
            "properties": {
              "recherches": {
                "type": "integer",
                "example": 320
              },
              "ouvertures": {
                "type": "integer",
                "example": 860
              },
              "partages": {
                "type": "integer",
                "example": 45
              },
              "exportsPdf": {
                "type": "integer",
                "example": 22
              },
              "relances": {
                "type": "integer",
                "example": 38
              },
              "inscriptions": {
                "type": "integer",
                "example": 9
              },
              "acquisitions": {
                "type": "integer",
                "example": 14
              },
              "evenements": {
                "type": "integer",
                "example": 1247
              }
            }
          },
          "relancesParSource": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "example": {
              "affichage": 24,
              "clic_compte": 9,
              "clic_connexion": 3,
              "fermeture": 12
            }
          },
          "inscriptionsParSource": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "example": {
              "relance_visiteur": 6,
              "direct": 3
            }
          },
          "acquisitionsParSource": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "example": {
              "partage": 11,
              "recherche": 3
            }
          },
          "serie": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "jour": {
                  "type": "string",
                  "example": "2026-09-24"
                },
                "recherches": {
                  "type": "integer"
                },
                "ouvertures": {
                  "type": "integer"
                },
                "partages": {
                  "type": "integer"
                },
                "exportsPdf": {
                  "type": "integer"
                }
              }
            }
          },
          "topRecherches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntreeTopAnalytics"
            }
          },
          "topOuvertures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntreeTopAnalytics"
            }
          },
          "topPartages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntreeTopAnalytics"
            }
          },
          "topExportsPdf": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntreeTopAnalytics"
            }
          }
        }
      },
      "EntreeTopAnalytics": {
        "type": "object",
        "required": [
          "cible",
          "total"
        ],
        "properties": {
          "cible": {
            "type": "string",
            "example": "bourse-aux-epices"
          },
          "cibleLabel": {
            "type": "string",
            "example": "Bourse aux Épices"
          },
          "total": {
            "type": "integer",
            "example": 42
          },
          "sources": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "example": {
              "catalogue": 20,
              "palette": 12,
              "direct": 10
            }
          }
        }
      },
      "EngagementMembre": {
        "type": "object",
        "required": [
          "actes",
          "consommations",
          "niveau",
          "relancePassivite"
        ],
        "properties": {
          "actes": {
            "type": "object",
            "properties": {
              "votes": {
                "type": "integer",
                "example": 3
              },
              "suivis": {
                "type": "integer",
                "example": 2
              },
              "contributions": {
                "type": "integer",
                "example": 1
              },
              "messagesForum": {
                "type": "integer",
                "example": 4
              },
              "collectionsPubliees": {
                "type": "integer",
                "example": 1
              }
            }
          },
          "consommations": {
            "type": "object",
            "properties": {
              "ouvertures": {
                "type": "integer",
                "example": 42
              },
              "recherches": {
                "type": "integer",
                "example": 9
              },
              "partages": {
                "type": "integer",
                "example": 2
              }
            }
          },
          "niveau": {
            "type": "string",
            "enum": [
              "lecteur",
              "participant",
              "contributeur",
              "pilier"
            ],
            "example": "participant"
          },
          "prochaineAction": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "enum": [
                  "noter",
                  "suivre",
                  "ameliorer",
                  "forum"
                ]
              },
              "libelle": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "href": {
                "type": "string"
              }
            }
          },
          "relancePassivite": {
            "type": "boolean",
            "example": false
          }
        }
      },
      "AgregatOnboarding": {
        "type": "object",
        "required": [
          "totalMembres",
          "totalRenseignes",
          "sources",
          "objectifs"
        ],
        "properties": {
          "totalMembres": {
            "type": "integer",
            "example": 42
          },
          "totalRenseignes": {
            "type": "integer",
            "example": 31
          },
          "moyenneObjectifs": {
            "type": "number",
            "example": 2.3
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "example": "scout_movement"
                },
                "total": {
                  "type": "integer",
                  "example": 14
                }
              }
            }
          },
          "objectifs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "example": "catalogue"
                },
                "total": {
                  "type": "integer",
                  "example": 28
                }
              }
            }
          },
          "croisements": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "sourceId": {
                  "type": "string",
                  "example": "scout_movement"
                },
                "totalUtilisateurs": {
                  "type": "integer",
                  "example": 14
                },
                "objectifs": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "objectifId": {
                        "type": "string"
                      },
                      "count": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/redaction/analyser": {
      "post": {
        "operationId": "analyserRedaction",
        "summary": "Analyser la rédaction et évaluer les garde-fous",
        "description": "Analyse linguistique complète (typographie, vocabulaire GIFAS, grammaire, orthographe) et pré-lecture JEV (éthique et sécurité).",
        "tags": [
          "Rédaction"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "texte"
                ],
                "properties": {
                  "texte": {
                    "type": "string",
                    "description": "Texte à analyser (max 50 000 caractères)."
                  },
                  "titre": {
                    "type": "string",
                    "description": "Titre du contenu (facultatif)."
                  },
                  "contexte": {
                    "type": "string",
                    "enum": [
                      "brique",
                      "chant",
                      "forum_sujet",
                      "forum_message"
                    ],
                    "description": "Contexte de publication."
                  },
                  "profil": {
                    "type": "string",
                    "enum": [
                      "standard",
                      "technique",
                      "litteraire",
                      "pedagogique"
                    ],
                    "description": "Profil rédactionnel pour l'analyse."
                  },
                  "verification": {
                    "type": "string",
                    "enum": [
                      "complete",
                      "rapide"
                    ],
                    "description": "Type de vérification ('complete' avec Grammalecte, 'rapide' avec règles locales)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bilan rédactionnel et avis JEV.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "statut": {
                      "type": "string",
                      "example": "ok"
                    },
                    "gardeFous": {
                      "type": "object",
                      "properties": {
                        "redaction": {
                          "type": "object"
                        },
                        "jev": {
                          "type": "object"
                        },
                        "grammalecteDisponible": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "422": {
            "description": "Texte manquant ou trop long."
          },
          "429": {
            "description": "Limite de requêtes atteinte."
          }
        }
      }
    },
    "/wiki/briques": {
      "get": {
        "operationId": "listerBriques",
        "summary": "Lister les briques d'animation publiées",
        "description": "Renvoie le catalogue vivant des briques validées avec pagination.",
        "tags": [
          "Wiki - Briques"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Numéro de la page (défaut : 1).",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limite",
            "in": "query",
            "description": "Nombre d'éléments par page (défaut : 50, max : 200).",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catalogue paginé récupéré avec succès.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "briques": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Brique"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limite": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "soumettreBrique",
        "summary": "Soumettre une nouvelle brique d'animation",
        "description": "Enregistre une nouvelle activité. Si l'auteur dispose du rang requis (relecteur/admin), la publication est immédiate ; sinon, elle passe en file de relecture.",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrouillonBrique"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Brique soumise avec succès.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "briqueId": {
                      "type": "string"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "statutBrique": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "422": {
            "description": "Champs invalides.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErreurReponse"
                }
              }
            }
          }
        }
      }
    },
    "/wiki/briques/{slug}": {
      "get": {
        "operationId": "obtenirBriqueParSlug",
        "summary": "Obtenir le détail complet d'une brique",
        "description": "Renvoie la fiche complète de l'activité identifiée par son slug pérenne.",
        "tags": [
          "Wiki - Briques"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug unique de la brique (ex: bourse-aux-epices).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fiche de la brique.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "brique": {
                      "$ref": "#/components/schemas/Brique"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Brique introuvable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErreurReponse"
                }
              }
            }
          }
        }
      }
    },
    "/wiki/recherche": {
      "get": {
        "operationId": "rechercherBriques",
        "summary": "Rechercher des briques d'animation",
        "description": "Recherche textuelle optimisée avec index SQLite FTS5 dans les titres, descriptions et tags.",
        "tags": [
          "Wiki - Recherche"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Mots-clés de recherche (ex: loup, veillée, relais).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limite",
            "in": "query",
            "description": "Nombre maximum de résultats (défaut : 20).",
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Résultats correspondants.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "briques": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Brique"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/wiki/votes": {
      "post": {
        "operationId": "voterBrique",
        "summary": "Noter une brique d'animation (1 à 5 étoiles)",
        "description": "Enregistre ou met à jour la note attribuée par un animateur connecté.",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug",
                  "note"
                ],
                "properties": {
                  "slug": {
                    "type": "string"
                  },
                  "note": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Note enregistrée avec agrégats recalculés.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "noteMoyenne": {
                      "type": "number"
                    },
                    "nbVotes": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/wiki/types": {
      "get": {
        "operationId": "listerTypesBriques",
        "summary": "Lister la taxonomie des types d'animation",
        "description": "Source unique des types et de leurs blocs (gérée par la modération). Chaque type expose ses `champs`.",
        "tags": [
          "Système"
        ],
        "responses": {
          "200": {
            "description": "Taxonomie des types.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "types": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TypeBrique"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "creerTypeBrique",
        "summary": "Créer un type d'activité (admin)",
        "description": "Crée une catégorie de fiches et ses blocs. Réservé aux administrateurs. Un type naît sans bloc si `champs` est omis.",
        "tags": [
          "Système"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "nom"
                ],
                "properties": {
                  "nom": {
                    "type": "string",
                    "example": "Grand jeu"
                  },
                  "description": {
                    "type": "string"
                  },
                  "icone": {
                    "type": "string",
                    "example": "Dices"
                  },
                  "champs": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "description": "Définition des blocs du type."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Type créé.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "$ref": "#/components/schemas/TypeBrique"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "403": {
            "description": "Action réservée aux administrateurs."
          },
          "409": {
            "description": "Un type portant ce nom existe déjà."
          },
          "422": {
            "description": "Nom manquant ou invalide."
          }
        }
      },
      "patch": {
        "operationId": "modifierTypeBrique",
        "summary": "Modifier un type d'activité et ses blocs (admin)",
        "description": "Renomme, redécrit ou reconfigure les blocs d'un type. Un renommage propage le nouveau nom à toutes les briques associées. Réservé aux administrateurs.",
        "tags": [
          "Système"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Identifiant du type à modifier."
                  },
                  "nom": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "icone": {
                    "type": "string"
                  },
                  "champs": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Type modifié.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "$ref": "#/components/schemas/TypeBrique"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "403": {
            "description": "Action réservée aux administrateurs."
          },
          "404": {
            "description": "Type introuvable."
          },
          "409": {
            "description": "Un autre type porte déjà ce nom."
          }
        }
      },
      "delete": {
        "operationId": "supprimerTypeBrique",
        "summary": "Supprimer un type d'activité (admin)",
        "description": "Supprime un type. Refusé si des briques publiées l'utilisent encore. Réservé aux administrateurs.",
        "tags": [
          "Système"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Identifiant (ou nom) du type à supprimer.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Type supprimé.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "supprime": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "403": {
            "description": "Action réservée aux administrateurs."
          },
          "404": {
            "description": "Type introuvable."
          },
          "409": {
            "description": "Des briques publiées utilisent encore ce type."
          }
        }
      }
    },
    "/wiki/stats": {
      "get": {
        "operationId": "obtenirStatsWiki",
        "summary": "Statistiques publiques du catalogue",
        "description": "Chiffres clés visibles sur /stats : briques publiées, révisions, votes, contributeurs, activité 30 jours, top contributeurs et dernières publications.",
        "tags": [
          "Statistiques"
        ],
        "responses": {
          "200": {
            "description": "Statistiques de la plateforme.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatsWiki"
                }
              }
            }
          }
        }
      }
    },
    "/wiki/stats/onboarding": {
      "get": {
        "operationId": "obtenirStatsOnboarding",
        "summary": "Agrégats publics du parcours d'inscription",
        "description": "Comptages anonymes (canaux de découverte, objectifs prioritaires, croisements) visibles sur la vue Acquisition de /stats. Aucune donnée nominative, aucun email.",
        "tags": [
          "Statistiques"
        ],
        "responses": {
          "200": {
            "description": "Agrégats d'acquisition.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgregatOnboarding"
                }
              }
            }
          }
        }
      }
    },
    "/analytics/rapport": {
      "get": {
        "operationId": "obtenirRapportAnalytics",
        "summary": "Rapport d'usage public",
        "description": "Totaux, série journalière et tops (recherches, ouvertures, partages, exports) visibles sur la vue Usage de /stats. Événements anonymisés ; les collections sont exclues du rapport.",
        "tags": [
          "Statistiques"
        ],
        "parameters": [
          {
            "name": "jours",
            "in": "query",
            "description": "Profondeur du rapport en jours (7, 30 ou 90 ; défaut : 30).",
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30,
                90
              ],
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rapport d'usage agrégé.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RapportAnalytics"
                }
              }
            }
          }
        }
      }
    },
    "/wiki/activite": {
      "get": {
        "operationId": "listerActiviteWiki",
        "summary": "Flux d'activité récent du wiki",
        "description": "Contributions, relectures et votes récents visibles sur /activite. Lecture ouverte à tous : les contributions issues d'une décision de modération (`ajustement_manuel`, `signalement`) sont servies en projection anonymisée — l'identité du membre sanctionné et le montant des points ne sortent pas, et ces deux valeurs sont refusées comme filtre `type`. Le rang relecteur et supérieur reçoit la ligne complète. Le champ `projection` indique la projection réellement servie.",
        "tags": [
          "Statistiques"
        ],
        "parameters": [
          {
            "name": "limite",
            "in": "query",
            "description": "Nombre d'entrées (défaut : 30, plafond : 100).",
            "schema": {
              "type": "integer",
              "default": 30,
              "maximum": 100
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Filtrer par type d'événement. Hors rang relecteur, `ajustement_manuel` et `signalement` sont ignorés.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entrées d'activité et projection servie.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entrees": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "projection": {
                      "type": "string",
                      "enum": [
                        "publique",
                        "moderation"
                      ],
                      "description": "`publique` : contributions de modération anonymisées. `moderation` : entrée complète (rang relecteur ou administrateur)."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/wiki/rapports": {
      "get": {
        "operationId": "obtenirRapportCatalogue",
        "summary": "Rapports de santé du catalogue",
        "description": "Sans `nom`, renvoie la liste des rapports disponibles ; avec `nom`, le rapport demandé (recentes, populaires, anciennes, sans_variantes, orphelines, courtes).",
        "tags": [
          "Statistiques"
        ],
        "parameters": [
          {
            "name": "nom",
            "in": "query",
            "description": "Nom du rapport.",
            "schema": {
              "type": "string",
              "enum": [
                "recentes",
                "populaires",
                "anciennes",
                "sans_variantes",
                "orphelines",
                "courtes"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rapport catalogue ou liste des rapports disponibles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/wiki/siteinfo": {
      "get": {
        "operationId": "obtenirSiteinfoWiki",
        "summary": "Informations d'intégration du wiki",
        "description": "Version du moteur, compteurs globaux et date (pattern ApiQuerySiteinfo).",
        "tags": [
          "Statistiques"
        ],
        "responses": {
          "200": {
            "description": "Informations du site.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteInfoWiki"
                }
              }
            }
          }
        }
      }
    },
    "/jev/rapport": {
      "get": {
        "operationId": "obtenirRapportJev",
        "summary": "Rapport de supervision JEV (super-admin)",
        "description": "Précision de la pré-lecture face aux décisions humaines et état des seuils.",
        "tags": [
          "Statistiques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "jours",
            "in": "query",
            "description": "Fenêtre d'analyse en jours.",
            "schema": {
              "type": "integer",
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rapport JEV."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé au super-administrateur."
          }
        }
      }
    },
    "/jev/reglages": {
      "get": {
        "operationId": "obtenirReglagesJev",
        "summary": "Réglages des seuils JEV (super-admin)",
        "description": "Seuils orange / rouge par critère, interrupteur d'apprentissage et aperçu des bascules. Les paramètres `attention_<critère>` / `blocage_<critère>` simulent des valeurs candidates sans écriture.",
        "tags": [
          "Statistiques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "plancherDimension",
            "in": "query",
            "description": "Plancher de dimension candidat (0-100).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "confianceMinReport",
            "in": "query",
            "description": "Confiance minimale de report candidate, lignes sans vecteur (0-100).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "modeAuto",
            "in": "query",
            "description": "Interrupteur d'apprentissage candidat (1 ou 0).",
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "0"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Réglages actifs, candidat validé et aperçu des bascules."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé au super-administrateur."
          }
        }
      },
      "put": {
        "operationId": "enregistrerReglagesJev",
        "summary": "Enregistrer les seuils JEV (super-admin)",
        "description": "Enregistre les seuils par critère et relance la réévaluation rétroactive des paliers depuis les scores déjà stockés (aucun appel au modèle).",
        "tags": [
          "Statistiques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plancherDimension": {
                    "type": "integer"
                  },
                  "confianceMinReport": {
                    "type": "integer"
                  },
                  "modeAuto": {
                    "type": "boolean"
                  },
                  "dimensions": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "dimension": {
                          "type": "string"
                        },
                        "attention": {
                          "type": "integer"
                        },
                        "blocage": {
                          "type": "integer"
                        },
                        "auto": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "dimension",
                        "attention",
                        "blocage"
                      ]
                    }
                  }
                },
                "required": [
                  "dimensions"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Réglages enregistrés et réévaluation rétroactive."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé au super-administrateur."
          },
          "422": {
            "description": "Réglages invalides (bornes ou écart orange / rouge)."
          }
        }
      }
    },
    "/jev/reglages/auto": {
      "post": {
        "operationId": "ajusterReglagesJev",
        "summary": "Forcer un run d'apprentissage JEV (super-admin)",
        "description": "Ignore le délai de stabilisation et applique immédiatement les mouvements dus, un pas par critère, dans la limite des bornes de sécurité.",
        "tags": [
          "Statistiques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Mouvements décidés, réévaluation éventuelle et réglages à jour."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé au super-administrateur."
          }
        }
      }
    },
    "/atelier": {
      "post": {
        "operationId": "enregistrerAjustementAtelier",
        "summary": "Enregistrer une session d'ajustement de l'Atelier",
        "description": "Observation anonyme : une session validée en mode Peinture où au moins une valeur s'écarte du défaut courant. Les sessions sans réglage touché sont refusées. Aucun identifiant, rate-limit par IP.",
        "tags": [
          "Statistiques"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "origine",
                  "valeurs"
                ],
                "properties": {
                  "origine": {
                    "type": "string",
                    "enum": [
                      "atelier_libre",
                      "banniere_fiche",
                      "banniere_collection",
                      "image_texte"
                    ]
                  },
                  "valeurs": {
                    "type": "object",
                    "description": "Valeurs choisies (en %), seules celles qui diffèrent du défaut.",
                    "properties": {
                      "intensite": {
                        "type": "integer"
                      },
                      "geste": {
                        "type": "integer"
                      },
                      "matiere": {
                        "type": "integer"
                      },
                      "couleur": {
                        "type": "integer"
                      },
                      "relief": {
                        "type": "integer"
                      },
                      "ondulation": {
                        "type": "integer"
                      },
                      "grain": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session observée et enregistrée."
          },
          "202": {
            "description": "Rien à observer : aucune valeur ne diffère du défaut."
          },
          "422": {
            "description": "Origine ou valeur invalide."
          },
          "429": {
            "description": "Trop d'ajustements envoyés."
          }
        }
      }
    },
    "/atelier/reglages": {
      "get": {
        "operationId": "obtenirReglagesAtelier",
        "summary": "Valeurs par défaut de l'Atelier",
        "description": "Configuration publique : valeurs de départ, interrupteurs d'apprentissage, mode auto et provenance. Aucune donnée personnelle.",
        "tags": [
          "Statistiques"
        ],
        "responses": {
          "200": {
            "description": "Réglages actifs de l'Atelier."
          }
        }
      },
      "put": {
        "operationId": "enregistrerReglagesAtelier",
        "summary": "Enregistrer les valeurs par défaut de l'Atelier (super-admin)",
        "description": "Enregistre les sept valeurs (bornes du moteur), les interrupteurs d'apprentissage par champ et le mode auto.",
        "tags": [
          "Statistiques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "valeurs"
                ],
                "properties": {
                  "valeurs": {
                    "type": "object",
                    "required": [
                      "intensite",
                      "geste",
                      "matiere",
                      "couleur",
                      "relief",
                      "ondulation",
                      "grain"
                    ],
                    "properties": {
                      "intensite": {
                        "type": "integer"
                      },
                      "geste": {
                        "type": "integer"
                      },
                      "matiere": {
                        "type": "integer"
                      },
                      "couleur": {
                        "type": "integer"
                      },
                      "relief": {
                        "type": "integer"
                      },
                      "ondulation": {
                        "type": "integer"
                      },
                      "grain": {
                        "type": "integer"
                      }
                    }
                  },
                  "autoParChamp": {
                    "type": "object",
                    "description": "Interrupteur d'apprentissage par champ (défaut : actif).",
                    "properties": {
                      "intensite": {
                        "type": "boolean"
                      },
                      "geste": {
                        "type": "boolean"
                      },
                      "matiere": {
                        "type": "boolean"
                      },
                      "couleur": {
                        "type": "boolean"
                      },
                      "relief": {
                        "type": "boolean"
                      },
                      "ondulation": {
                        "type": "boolean"
                      },
                      "grain": {
                        "type": "boolean"
                      }
                    }
                  },
                  "modeAuto": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Réglages enregistrés."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé au super-administrateur."
          },
          "422": {
            "description": "Valeurs hors bornes du moteur."
          }
        }
      }
    },
    "/atelier/reglages/auto": {
      "post": {
        "operationId": "ajusterReglagesAtelier",
        "summary": "Forcer un run d'apprentissage de l'Atelier (super-admin)",
        "description": "Ignore le délai de stabilisation et applique immédiatement les mouvements dus : médiane des valeurs modifiées, déplacement borné par champ, journal immuable.",
        "tags": [
          "Statistiques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Mouvements décidés et réglages à jour."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé au super-administrateur."
          }
        }
      }
    },
    "/atelier/rapport": {
      "get": {
        "operationId": "obtenirRapportAtelier",
        "summary": "Rapport de supervision de l'Atelier (super-admin)",
        "description": "Distributions des valeurs réellement modifiées (sessions sans réglage touché exclues), suggestions de défauts, série journalière et journal des mouvements du moteur.",
        "tags": [
          "Statistiques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "jours",
            "in": "query",
            "description": "Fenêtre d'analyse en jours.",
            "schema": {
              "type": "integer",
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rapport de l'Atelier."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé au super-administrateur."
          }
        }
      }
    },
    "/chants": {
      "get": {
        "operationId": "listerChants",
        "summary": "Lister et rechercher les chants du répertoire",
        "description": "Catalogue de chants avec filtres par mot-clé et niveau d'énergie.",
        "tags": [
          "Chants"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Recherche par titre ou extrait de paroles.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "energie",
            "in": "query",
            "description": "Filtrer par dynamisme (Calme, Entrainant, Dynamique).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des chants correspondants.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chants": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Chant"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "creerChant",
        "summary": "Ajouter un chant au répertoire",
        "description": "Crée une nouvelle fiche de chant avec paroles et partition ABC optionnelle.",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "titre",
                  "energie",
                  "paroles"
                ],
                "properties": {
                  "titre": {
                    "type": "string"
                  },
                  "energie": {
                    "type": "string",
                    "enum": [
                      "Calme",
                      "Entrainant",
                      "Dynamique"
                    ]
                  },
                  "paroles": {
                    "type": "string"
                  },
                  "abcNotation": {
                    "type": "string"
                  },
                  "tonalite": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Chant créé avec succès.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "slug": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/chants/{slug}": {
      "get": {
        "operationId": "obtenirChantParSlug",
        "summary": "Obtenir un chant complet et sa partition ABC",
        "description": "Renvoie les paroles intégrales et la tablature/portée vectorielle ABC.",
        "tags": [
          "Chants"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug du chant.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Détail du chant.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chant": {
                      "$ref": "#/components/schemas/Chant"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Chant introuvable."
          }
        }
      },
      "put": {
        "operationId": "modifierChant",
        "summary": "Proposer une amélioration d'un chant",
        "description": "Soumet une révision d'un chant : publication immédiate selon le rang de l'auteur, sinon passage en file de relecture (miroir de proposerAmelioration).",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug ou identifiant du chant.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "titre",
                  "paroles"
                ],
                "properties": {
                  "titre": {
                    "type": "string"
                  },
                  "paroles": {
                    "type": "string"
                  },
                  "categorie": {
                    "type": "string"
                  },
                  "energie": {
                    "type": "string"
                  },
                  "abc": {
                    "type": "string",
                    "description": "Notation ABC de la mélodie."
                  },
                  "resume": {
                    "type": "string",
                    "description": "Résumé pour l'historique."
                  },
                  "versionBase": {
                    "type": "integer",
                    "description": "Version attendue (détection de conflit)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Révision soumise."
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "403": {
            "description": "Non autorisé."
          },
          "404": {
            "description": "Chant introuvable."
          },
          "409": {
            "description": "Conflit de version."
          }
        }
      },
      "delete": {
        "operationId": "supprimerChant",
        "summary": "Supprimer un chant (auteur ou admin)",
        "description": "Supprime définitivement un chant. Réservé à son auteur ou à un administrateur.",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug ou identifiant du chant.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chant supprimé."
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "403": {
            "description": "Réservé à l'auteur ou à un administrateur."
          },
          "404": {
            "description": "Chant introuvable."
          }
        }
      }
    },
    "/chants/depublier": {
      "post": {
        "operationId": "retirerChantDroits",
        "summary": "Retirer un chant du catalogue public (signalement de droits)",
        "description": "Réservé aux administrateurs : passe le chant en statut archive (retrait réversible, historique conservé) dans le cadre de la procédure de retrait sous 48 h.",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "chantId"
                ],
                "properties": {
                  "chantId": {
                    "type": "string",
                    "description": "Identifiant du chant à retirer."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chant retiré (statut archive)."
          },
          "403": {
            "description": "Action réservée aux administrateurs."
          },
          "404": {
            "description": "Chant introuvable."
          }
        }
      }
    },
    "/collections": {
      "get": {
        "operationId": "listerCollections",
        "summary": "Lister les collections publiques ou personnelles",
        "description": "Renvoie les sélections thématiques partagées ou celles du compte connecté. Avec `mine=1`, la collection « Mes favoris » du membre est créée au besoin et `collectionsSuiviesIds` liste les collections publiques suivies.",
        "tags": [
          "Collections"
        ],
        "parameters": [
          {
            "name": "mine",
            "in": "query",
            "description": "Si '1', renvoie uniquement les collections de l'utilisateur.",
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collections trouvées.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "collections": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Collection"
                      }
                    },
                    "collectionsSuiviesIds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Identifiants des collections publiques suivies (mine=1)."
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "creerCollection",
        "summary": "Créer une nouvelle collection thématique",
        "description": "Création idempotente : un `id` client (UUID) peut être fourni pour rejouer la même création sans doublon depuis un autre appareil. La pré-lecture JEV ne s'exécute que pour une publication (`estPublique: true`).",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "nom"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Identifiant client (optionnel, idempotence)."
                  },
                  "nom": {
                    "type": "string"
                  },
                  "descriptionCourte": {
                    "type": "string"
                  },
                  "estPublique": {
                    "type": "boolean"
                  },
                  "briqueIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "chantIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Collection créée.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/forum/threads": {
      "get": {
        "operationId": "listerSujetsForum",
        "summary": "Lister les sujets de discussion du forum",
        "tags": [
          "Forum Communautaire"
        ],
        "responses": {
          "200": {
            "description": "Liste des sujets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "threads": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForumThread"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "creerSujetForum",
        "summary": "Créer un sujet de discussion",
        "description": "Ouvre un nouveau sujet. Connexion requise. Les garde-fous rédactionnels et la pré-lecture JEV s'appliquent (masquage automatique si bloqué).",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "titre",
                  "corps"
                ],
                "properties": {
                  "titre": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "corps": {
                    "type": "string"
                  },
                  "categorie": {
                    "type": "string",
                    "example": "Autre"
                  },
                  "verification": {
                    "type": "string",
                    "enum": [
                      "complete",
                      "rapide"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sujet créé.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/ForumThread"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion ou jeton Bearer requis."
          },
          "422": {
            "description": "Titre ou corps manquant."
          },
          "429": {
            "description": "Limite de création atteinte."
          }
        }
      }
    },
    "/wiki/membres": {
      "get": {
        "operationId": "listerMembres",
        "summary": "Lister l'annuaire des membres et animateurs",
        "tags": [
          "Membres"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Numéro de page (défaut : 1).",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limite",
            "in": "query",
            "description": "Nombre d'éléments par page (défaut : 50, max : 200).",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Annuaire public.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "membres": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MembreProfil"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limite": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/wiki/membres/{id}": {
      "get": {
        "operationId": "obtenirMembre",
        "summary": "Obtenir la fiche profil d'un membre",
        "tags": [
          "Membres"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant unique de l'utilisateur.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fiche membre.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MembreProfil"
                }
              }
            }
          },
          "404": {
            "description": "Membre introuvable."
          }
        }
      },
      "patch": {
        "operationId": "ajusterPointsMembre",
        "summary": "Modifier un membre : points (super-admin), rôle ou statut (admin)",
        "description": "Trois usages selon le corps : `reputationPoints` ajuste le total de points et journalise un « Changement manuel » (super-admin uniquement) ; `role` et/ou `statut` modifient le rôle et le statut du membre (administrateur). Le compte super-administrateur fondateur est protégé.",
        "tags": [
          "Membres"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant unique de l'utilisateur.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reputationPoints": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100000,
                    "description": "Nouveau total de points (super-admin)."
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "admin",
                      "relecteur",
                      "editeur",
                      "contributeur",
                      "novice",
                      "animateur"
                    ],
                    "description": "Nouveau rôle du membre (admin)."
                  },
                  "statut": {
                    "type": "string",
                    "description": "Nouveau statut affiché du membre (admin)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Membre mis à jour avec traçabilité."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Action réservée à l'administrateur ou au super-administrateur."
          },
          "404": {
            "description": "Membre introuvable."
          },
          "422": {
            "description": "Valeur invalide."
          }
        }
      }
    },
    "/wiki/ameliorations": {
      "post": {
        "operationId": "proposerAmelioration",
        "summary": "Proposer une amélioration sur une brique existante",
        "description": "Crée une révision en attente de relecture avec les modifications suggérées.",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug"
                ],
                "properties": {
                  "slug": {
                    "type": "string"
                  },
                  "resume": {
                    "type": "string"
                  },
                  "versionBase": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Proposition enregistrée."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/wiki/revisions": {
      "get": {
        "operationId": "listerRevisionsEnAttente",
        "summary": "Lister les révisions en attente de modération",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des révisions."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      },
      "post": {
        "operationId": "deciderRevision",
        "summary": "Accepter ou refuser une révision en relecture",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revisionId",
                  "decision"
                ],
                "properties": {
                  "revisionId": {
                    "type": "string"
                  },
                  "decision": {
                    "type": "string",
                    "enum": [
                      "acceptee",
                      "refusee"
                    ]
                  },
                  "motifRefus": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Décision appliquée."
          },
          "401": {
            "description": "Action réservée aux relecteurs/administrateurs."
          }
        }
      }
    },
    "/wiki/historique": {
      "get": {
        "operationId": "historiqueBrique",
        "summary": "Consulter l'historique des révisions d'une brique",
        "tags": [
          "Wiki - Briques"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "description": "Slug de la brique.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Historique des versions."
          }
        }
      }
    },
    "/wiki/suivi": {
      "post": {
        "operationId": "suivreBrique",
        "summary": "Suivre les modifications d'une brique",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug"
                ],
                "properties": {
                  "slug": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brique suivie."
          }
        }
      },
      "delete": {
        "operationId": "nePlusSuivreBrique",
        "summary": "Ne plus suivre les modifications d'une brique",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Brique retirée des suivis."
          }
        }
      }
    },
    "/wiki/suivies": {
      "get": {
        "operationId": "listerBriquesSuivies",
        "summary": "Lister les briques suivies par l'utilisateur connecté",
        "tags": [
          "Wiki - Briques"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des briques suivies."
          }
        }
      }
    },
    "/wiki/contributeurs": {
      "get": {
        "operationId": "listerContributeursBrique",
        "summary": "Lister les contributeurs ayant participé à une brique",
        "tags": [
          "Membres"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contributeurs."
          }
        }
      }
    },
    "/wiki/contributions": {
      "get": {
        "operationId": "listerContributionsMembre",
        "summary": "Lister les contributions d'un utilisateur",
        "tags": [
          "Membres"
        ],
        "parameters": [
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Journal de contributions."
          }
        }
      }
    },
    "/wiki/engagement": {
      "get": {
        "operationId": "obtenirEngagementMembre",
        "summary": "Agrégats d'engagement du compte connecté",
        "description": "Actes réels (votes, suivis, contributions, forum, collections publiées), consommations (ouvertures, recherches, partages) et niveau dérivé. Alimente la carte de progression et la relance de passivité ; données privées, jamais servies hors session.",
        "tags": [
          "Membres"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Agrégats d'engagement du membre.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementMembre"
                }
              }
            }
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/collections/{id}": {
      "get": {
        "operationId": "obtenirCollection",
        "summary": "Obtenir une collection et ses briques associées",
        "tags": [
          "Collections"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Détail de la collection."
          },
          "404": {
            "description": "Collection introuvable."
          }
        }
      },
      "put": {
        "operationId": "modifierCollection",
        "summary": "Mettre à jour une collection",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collection mise à jour."
          }
        }
      },
      "delete": {
        "operationId": "supprimerCollection",
        "summary": "Supprimer une collection",
        "description": "La collection « Mes favoris » est indestructible (vidage entrée par entrée).",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collection supprimée."
          },
          "403": {
            "description": "Collection « Mes favoris » protégée."
          }
        }
      }
    },
    "/collections/{id}/briques": {
      "post": {
        "operationId": "ajouterBriqueCollection",
        "summary": "Ajouter une brique à une collection personnelle",
        "description": "Idempotent ; accepte un identifiant ou un slug de brique.",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Id ou slug de la brique."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brique présente dans la collection."
          },
          "403": {
            "description": "Non propriétaire."
          },
          "404": {
            "description": "Collection ou brique introuvable."
          }
        }
      },
      "delete": {
        "operationId": "retirerBriqueCollection",
        "summary": "Retirer une brique d'une collection personnelle",
        "description": "Idempotent ; accepte un identifiant ou un slug de brique.",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Brique absente de la collection."
          },
          "403": {
            "description": "Non propriétaire."
          },
          "404": {
            "description": "Collection ou brique introuvable."
          }
        }
      }
    },
    "/collections/{id}/chants": {
      "post": {
        "operationId": "ajouterChantCollection",
        "summary": "Ajouter un chant à une collection personnelle",
        "description": "Idempotent ; accepte un identifiant ou un slug de chant.",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Id ou slug du chant."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chant présent dans la collection."
          },
          "403": {
            "description": "Non propriétaire."
          },
          "404": {
            "description": "Collection ou chant introuvable."
          }
        }
      },
      "delete": {
        "operationId": "retirerChantCollection",
        "summary": "Retirer un chant d'une collection personnelle",
        "description": "Idempotent ; accepte un identifiant ou un slug de chant.",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chant absent de la collection."
          },
          "403": {
            "description": "Non propriétaire."
          },
          "404": {
            "description": "Collection ou chant introuvable."
          }
        }
      }
    },
    "/collections/{id}/suivi": {
      "post": {
        "operationId": "suivreCollection",
        "summary": "Suivre une collection publique",
        "description": "Idempotent (motif watchlist), la collection doit être publique.",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collection suivie."
          },
          "403": {
            "description": "Collection non publique."
          },
          "404": {
            "description": "Collection introuvable."
          }
        }
      },
      "delete": {
        "operationId": "nePlusSuivreCollection",
        "summary": "Ne plus suivre une collection",
        "description": "Idempotent.",
        "tags": [
          "Collections"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suivi retiré."
          }
        }
      }
    },
    "/forum/threads/{id}/messages": {
      "get": {
        "operationId": "listerMessagesForum",
        "summary": "Lister les réponses d'un sujet du forum",
        "tags": [
          "Forum Communautaire"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du thread.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages du sujet."
          }
        }
      },
      "post": {
        "operationId": "posterMessageForum",
        "summary": "Répondre à un sujet de discussion",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "corps"
                ],
                "properties": {
                  "corps": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Message publié."
          }
        }
      }
    },
    "/forum/votes": {
      "post": {
        "operationId": "voterMessageOuSujetForum",
        "summary": "Voter (+1 / -1) sur un sujet ou une réponse du forum",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cibleType",
                  "cibleId",
                  "valeur"
                ],
                "properties": {
                  "cibleType": {
                    "type": "string",
                    "enum": [
                      "thread",
                      "message"
                    ]
                  },
                  "cibleId": {
                    "type": "string"
                  },
                  "valeur": {
                    "type": "integer",
                    "enum": [
                      1,
                      -1
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Vote pris en compte."
          }
        }
      }
    },
    "/signalements": {
      "get": {
        "operationId": "listerSignalements",
        "summary": "Lister tous les signalements de briques (modération)",
        "description": "Réservé aux relecteurs et administrateurs.",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des signalements.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "signalements": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Accès modération réservé."
          }
        }
      },
      "post": {
        "operationId": "creerSignalement",
        "summary": "Signaler un contenu inapproprié ou un problème de sécurité",
        "tags": [
          "Modération"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "briqueId",
                  "briqueTitre",
                  "briqueSlug",
                  "motif"
                ],
                "properties": {
                  "briqueId": {
                    "type": "string"
                  },
                  "briqueTitre": {
                    "type": "string"
                  },
                  "briqueSlug": {
                    "type": "string"
                  },
                  "motif": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Signalement enregistré."
          }
        }
      }
    },
    "/retours": {
      "get": {
        "operationId": "listerRetours",
        "summary": "Lister tous les retours utilisateurs (admin)",
        "description": "Boîte de réception des retours, bugs, signalements de droits et accès compte. Réservé aux administrateurs.",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des retours.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "retours": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Action réservée aux administrateurs."
          }
        }
      },
      "post": {
        "operationId": "envoyerRetour",
        "summary": "Envoyer un retour utilisateur (suggestion ou rapport de bug)",
        "tags": [
          "Modération"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "titre",
                  "description"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "bug",
                      "amelioration",
                      "signalement_droits",
                      "acces_compte"
                    ]
                  },
                  "titre": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "pageUrl": {
                    "type": "string"
                  },
                  "chantId": {
                    "type": "string",
                    "description": "Chant visé par un signalement de droits (facultatif, procédure de retrait 48 h)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Retour transmis à l'équipe."
          }
        }
      }
    },
    "/sync": {
      "get": {
        "operationId": "synchroniserCatalogue",
        "summary": "Synchronisation incrémentale du catalogue (backup)",
        "description": "Renvoie les briques et suppressions intervenues depuis un horodatage ISO (`since`), permettant au backup différentiel (GitHub Actions) de rester à jour efficacement.",
        "tags": [
          "Système"
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": true,
            "description": "Horodatage ISO de la dernière synchronisation réussie.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Différentiel de synchronisation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "briquesModifiees": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Brique"
                      }
                    },
                    "suppressions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "supprimeLe": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "maintenant": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/docs": {
      "get": {
        "operationId": "listerGuides",
        "summary": "Lister les guides de référence",
        "description": "Retourne le sommaire ordonné de tous les guides pédagogiques, règles de contribution et conventions éditoriales.",
        "tags": [
          "Guides"
        ],
        "responses": {
          "200": {
            "description": "Sommaire des guides de référence récupéré avec succès.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "documents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "titre": {
                            "type": "string"
                          },
                          "categorie": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "ordre": {
                            "type": "integer"
                          },
                          "majLe": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "slug",
                          "titre",
                          "categorie",
                          "ordre",
                          "majLe"
                        ]
                      }
                    }
                  },
                  "required": [
                    "documents"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/docs/{slug}": {
      "get": {
        "operationId": "obtenirGuideParSlug",
        "summary": "Obtenir un guide de référence par son slug",
        "description": "Retourne le contenu markdown complet d'un guide pédagogique ou d'une convention éditoriale.",
        "tags": [
          "Guides"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Identifiant textuel du document (ex: regles_briques, charte_contributions)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Guide complet récupéré.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "document": {
                      "type": "object",
                      "properties": {
                        "slug": {
                          "type": "string"
                        },
                        "titre": {
                          "type": "string"
                        },
                        "categorie": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "contenu": {
                          "type": "string"
                        },
                        "ordre": {
                          "type": "integer"
                        },
                        "majLe": {
                          "type": "string"
                        },
                        "majPar": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "slug",
                        "titre",
                        "categorie",
                        "contenu",
                        "ordre",
                        "majLe"
                      ]
                    }
                  },
                  "required": [
                    "document"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Guide introuvable."
          }
        }
      }
    },
    "/docs-ia": {
      "get": {
        "operationId": "listerDocumentsIa",
        "summary": "Lister les documents de référence (alias de compatibilité)",
        "description": "Alias de compatibilité pointant vers /docs.",
        "tags": [
          "Guides"
        ],
        "responses": {
          "200": {
            "description": "Sommaire des documents récupéré avec succès.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "documents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "titre": {
                            "type": "string"
                          },
                          "categorie": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "ordre": {
                            "type": "integer"
                          },
                          "majLe": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "slug",
                          "titre",
                          "categorie",
                          "ordre",
                          "majLe"
                        ]
                      }
                    }
                  },
                  "required": [
                    "documents"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/docs-ia/{slug}": {
      "get": {
        "operationId": "obtenirDocumentIa",
        "summary": "Obtenir un document de référence (alias de compatibilité)",
        "description": "Alias de compatibilité pointant vers /docs/{slug}.",
        "tags": [
          "Guides"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Identifiant textuel du document",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Document complet récupéré.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "document": {
                      "type": "object",
                      "properties": {
                        "slug": {
                          "type": "string"
                        },
                        "titre": {
                          "type": "string"
                        },
                        "categorie": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "contenu": {
                          "type": "string"
                        },
                        "ordre": {
                          "type": "integer"
                        },
                        "majLe": {
                          "type": "string"
                        },
                        "majPar": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "slug",
                        "titre",
                        "categorie",
                        "contenu",
                        "ordre",
                        "majLe"
                      ]
                    }
                  },
                  "required": [
                    "document"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Document introuvable."
          }
        }
      }
    },
    "/link-preview": {
      "get": {
        "operationId": "obtenirApercuLien",
        "summary": "Obtenir l'aperçu riche d'un lien",
        "description": "Renvoie titre, description, image, site et provider d'une URL (cache D1 30 jours, fetch OpenGraph sinon). Public, avec garde-fous SSRF.",
        "tags": [
          "Système"
        ],
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "URL http(s) à prévisualiser.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aperçu du lien."
          },
          "400": {
            "description": "URL invalide, interne ou hôte non autorisé."
          },
          "429": {
            "description": "Trop d'aperçus demandés."
          }
        }
      }
    },
    "/analytics": {
      "post": {
        "operationId": "enregistrerAnalytics",
        "summary": "Enregistrer un lot d'événements d'usage anonymisés",
        "description": "Collecte produit anonyme (recherches, ouvertures, partages, exports). Sans cookie de suivi ni IP ; lot plafonné à 60 événements.",
        "tags": [
          "Statistiques"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "evenements"
                ],
                "properties": {
                  "evenements": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "type",
                        "cible"
                      ],
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "recherche",
                            "ouverture",
                            "collection",
                            "partage",
                            "export_pdf",
                            "incitation",
                            "inscription",
                            "acquisition",
                            "filtre",
                            "lecture_complete"
                          ]
                        },
                        "cible": {
                          "type": "string"
                        },
                        "cibleLabel": {
                          "type": "string"
                        },
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Lot enregistré."
          },
          "422": {
            "description": "Aucun événement ou événement invalide."
          },
          "429": {
            "description": "Limite d'envoi atteinte."
          }
        }
      }
    },
    "/chants/historique": {
      "get": {
        "operationId": "historiqueChant",
        "summary": "Historique des révisions d'un chant",
        "tags": [
          "Chants"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "description": "Slug ou identifiant du chant.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Historique du chant."
          },
          "404": {
            "description": "Chant introuvable."
          }
        }
      }
    },
    "/chants/versions/{id}": {
      "get": {
        "operationId": "obtenirVersionChant",
        "summary": "Obtenir une version (permalink) d'un chant",
        "tags": [
          "Chants"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant de révision.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Version du chant."
          },
          "404": {
            "description": "Version introuvable."
          }
        }
      }
    },
    "/chants/compare": {
      "get": {
        "operationId": "comparerChants",
        "summary": "Comparer deux versions d'un chant",
        "tags": [
          "Chants"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "description": "Slug ou identifiant du chant.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "de",
            "in": "query",
            "required": true,
            "description": "Version de départ.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "vers",
            "in": "query",
            "required": true,
            "description": "Version d'arrivée.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Diff des deux versions."
          }
        }
      }
    },
    "/chants/contributeurs": {
      "get": {
        "operationId": "listerContributeursChant",
        "summary": "Lister les contributeurs d'un chant",
        "tags": [
          "Chants"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "description": "Slug ou identifiant du chant.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contributeurs du chant."
          }
        }
      }
    },
    "/chants/revisions": {
      "get": {
        "operationId": "listerRevisionsChantsEnAttente",
        "summary": "Lister les révisions de chants en attente (modération)",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Révisions en attente."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé aux relecteurs et administrateurs."
          }
        }
      },
      "post": {
        "operationId": "deciderRevisionChant",
        "summary": "Décider d'une révision de chant (accepter / refuser)",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revisionId",
                  "decision"
                ],
                "properties": {
                  "revisionId": {
                    "type": "string"
                  },
                  "decision": {
                    "type": "string",
                    "enum": [
                      "acceptee",
                      "refusee"
                    ]
                  },
                  "motifRefus": {
                    "type": "string"
                  },
                  "verification": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Décision enregistrée."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Réservé aux relecteurs et administrateurs."
          },
          "422": {
            "description": "Paramètres invalides."
          }
        }
      }
    },
    "/chants/restaurer": {
      "post": {
        "operationId": "restaurerVersionChant",
        "summary": "Restaurer une version de chant (admin)",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revisionId"
                ],
                "properties": {
                  "revisionId": {
                    "type": "string"
                  },
                  "resume": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Version restaurée."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          }
        }
      }
    },
    "/chants/masquer": {
      "post": {
        "operationId": "masquerRevisionChant",
        "summary": "Masquer une révision de chant (modération)",
        "tags": [
          "Chants"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revisionId",
                  "champs"
                ],
                "properties": {
                  "revisionId": {
                    "type": "string"
                  },
                  "champs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Révision masquée."
          },
          "403": {
            "description": "Réservé aux relecteurs et administrateurs."
          }
        }
      }
    },
    "/forum/threads/{id}": {
      "get": {
        "operationId": "obtenirSujetForum",
        "summary": "Lire un sujet du forum et ses réponses",
        "tags": [
          "Forum Communautaire"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du sujet.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sujet et réponses."
          },
          "404": {
            "description": "Discussion introuvable."
          }
        }
      },
      "patch": {
        "operationId": "modererSujetForum",
        "summary": "Modérer un sujet (épingler, verrouiller, masquer)",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du sujet.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "epingle": {
                    "type": "boolean"
                  },
                  "verrouille": {
                    "type": "boolean"
                  },
                  "masque": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sujet mis à jour."
          },
          "401": {
            "description": "Connexion requise."
          },
          "404": {
            "description": "Discussion introuvable."
          }
        }
      },
      "delete": {
        "operationId": "supprimerSujetForum",
        "summary": "Supprimer un sujet du forum (auteur ou modérateur)",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du sujet.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sujet supprimé."
          },
          "403": {
            "description": "Non autorisé."
          },
          "404": {
            "description": "Discussion introuvable."
          }
        }
      }
    },
    "/forum/messages/{id}": {
      "delete": {
        "operationId": "supprimerMessageForum",
        "summary": "Supprimer une réponse du forum (auteur ou modérateur)",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du message.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message supprimé."
          },
          "403": {
            "description": "Non autorisé."
          },
          "404": {
            "description": "Message introuvable."
          }
        }
      }
    },
    "/forum/signalements": {
      "get": {
        "operationId": "listerSignalementsForum",
        "summary": "Lister les signalements du forum (modération)",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Signalements forum."
          },
          "401": {
            "description": "Connexion requise."
          },
          "403": {
            "description": "Accès modération réservé."
          }
        }
      },
      "post": {
        "operationId": "creerSignalementForum",
        "summary": "Signaler un contenu du forum",
        "tags": [
          "Forum Communautaire"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cibleType",
                  "cibleId",
                  "threadId",
                  "threadTitre",
                  "motif"
                ],
                "properties": {
                  "cibleType": {
                    "type": "string",
                    "enum": [
                      "thread",
                      "message"
                    ]
                  },
                  "cibleId": {
                    "type": "string"
                  },
                  "threadId": {
                    "type": "string"
                  },
                  "threadTitre": {
                    "type": "string"
                  },
                  "extrait": {
                    "type": "string"
                  },
                  "motif": {
                    "type": "string"
                  },
                  "details": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Signalement enregistré."
          },
          "422": {
            "description": "Champs manquants."
          },
          "429": {
            "description": "Limite atteinte."
          }
        }
      }
    },
    "/forum/signalements/{id}": {
      "post": {
        "operationId": "traiterSignalementForum",
        "summary": "Décider d'un signalement forum (modération)",
        "tags": [
          "Forum Communautaire"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du signalement.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "decision"
                ],
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "abus_confirme",
                      "rejete"
                    ]
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "suppression",
                      "masquage",
                      "conserver",
                      "aucune"
                    ]
                  },
                  "sanction": {
                    "type": "string",
                    "enum": [
                      "aucune",
                      "avertissement",
                      "ban_temporaire",
                      "ban_definitif"
                    ]
                  },
                  "dureeBanHeures": {
                    "type": "integer"
                  },
                  "messageAuteur": {
                    "type": "string"
                  },
                  "motifLog": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Décision appliquée."
          },
          "403": {
            "description": "Accès modération réservé."
          },
          "404": {
            "description": "Signalement introuvable."
          }
        }
      }
    },
    "/wiki/journal": {
      "get": {
        "operationId": "listerJournalModeration",
        "summary": "Consulter le journal d'activité",
        "description": "Journal des actions de la communauté (créations, relectures, rôles, blocages, signalements) visible sur /activite. Lecture ouverte à tous : hors rang relecteur, les entrées sont expurgées de toute donnée personnelle (email, surnom de la cible) et de tout détail de modération (motif, ajustements, statuts). Le champ `projection` indique la projection réellement servie.",
        "tags": [
          "Modération"
        ],
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "description": "Filtrer par type d'action.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateDebut",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFin",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recherche",
            "in": "query",
            "description": "Recherche plein texte (motifs exclus hors rang relecteur).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ordre",
            "in": "query",
            "description": "Ordre chronologique.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limite",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entrées du journal et projection servie.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entrees": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limite": {
                      "type": "integer"
                    },
                    "projection": {
                      "type": "string",
                      "enum": [
                        "publique",
                        "moderation"
                      ],
                      "description": "`publique` : entrées expurgées. `moderation` : entrée complète (rang relecteur ou administrateur)."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/wiki/export": {
      "get": {
        "operationId": "exporterWiki",
        "summary": "Exporter l'intégralité du wiki (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "description": "Format d'export.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dump du wiki."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          }
        }
      }
    },
    "/wiki/import": {
      "post": {
        "operationId": "importerWiki",
        "summary": "Importer un dump du wiki (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "description": "Format d'entrée.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "description": "Dump du wiki à importer."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Import effectué."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          }
        }
      }
    },
    "/wiki/archives": {
      "get": {
        "operationId": "listerArchives",
        "summary": "Lister les briques archivées (modération)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Briques archivées."
          },
          "403": {
            "description": "Réservé aux relecteurs et administrateurs."
          }
        }
      },
      "post": {
        "operationId": "restaurerArchive",
        "summary": "Restaurer une brique archivée (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug"
                ],
                "properties": {
                  "slug": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brique restaurée."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          }
        }
      }
    },
    "/wiki/blocages": {
      "get": {
        "operationId": "listerBlocages",
        "summary": "Lister les blocages actifs (modération)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Blocages actifs."
          },
          "403": {
            "description": "Réservé aux relecteurs et administrateurs."
          }
        }
      },
      "post": {
        "operationId": "creerBlocage",
        "summary": "Créer un blocage utilisateur ou IP (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "motif"
                ],
                "properties": {
                  "cibleUserId": {
                    "type": "string"
                  },
                  "cibleIp": {
                    "type": "string"
                  },
                  "motif": {
                    "type": "string"
                  },
                  "expireLe": {
                    "type": "string",
                    "description": "Échéance ISO (vide = définitif)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Blocage créé."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          }
        }
      },
      "delete": {
        "operationId": "leverBlocage",
        "summary": "Lever un blocage (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Identifiant du blocage.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Blocage levé."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          },
          "422": {
            "description": "Identifiant manquant."
          }
        }
      }
    },
    "/wiki/promouvoir": {
      "post": {
        "operationId": "promouvoirMembre",
        "summary": "Promouvoir un membre administrateur (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "userId"
                ],
                "properties": {
                  "userId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Membre promu ou déjà administrateur."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          },
          "404": {
            "description": "Membre introuvable."
          }
        }
      }
    },
    "/wiki/supprimer": {
      "post": {
        "operationId": "supprimerBrique",
        "summary": "Archiver une brique (relecteurs / admins)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug",
                  "motif"
                ],
                "properties": {
                  "slug": {
                    "type": "string"
                  },
                  "motif": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brique archivée."
          },
          "403": {
            "description": "Réservé aux relecteurs et administrateurs."
          }
        }
      }
    },
    "/wiki/restaurer": {
      "post": {
        "operationId": "restaurerVersionBrique",
        "summary": "Restaurer une version de brique (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revisionId"
                ],
                "properties": {
                  "revisionId": {
                    "type": "string"
                  },
                  "resume": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Version restaurée."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          }
        }
      }
    },
    "/wiki/masquer": {
      "post": {
        "operationId": "masquerRevisionBrique",
        "summary": "Masquer une révision de brique (relecteurs / admins)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revisionId",
                  "champs"
                ],
                "properties": {
                  "revisionId": {
                    "type": "string"
                  },
                  "champs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Révision masquée."
          },
          "403": {
            "description": "Réservé aux relecteurs et administrateurs."
          }
        }
      }
    },
    "/wiki/versions/{id}": {
      "get": {
        "operationId": "obtenirVersionBrique",
        "summary": "Obtenir une version (permalink) d'une brique",
        "tags": [
          "Wiki - Briques"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant de révision.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Version de la brique."
          },
          "404": {
            "description": "Version introuvable."
          }
        }
      }
    },
    "/wiki/compare": {
      "get": {
        "operationId": "comparerVersionsBrique",
        "summary": "Comparer deux versions d'une brique",
        "tags": [
          "Wiki - Briques"
        ],
        "parameters": [
          {
            "name": "de",
            "in": "query",
            "required": true,
            "description": "Version de départ.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "vers",
            "in": "query",
            "required": true,
            "description": "Version d'arrivée.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Diff des deux versions."
          }
        }
      }
    },
    "/wiki/avatars": {
      "get": {
        "operationId": "listerAvatars",
        "summary": "Annuaire des configurations d'avatars",
        "tags": [
          "Système"
        ],
        "responses": {
          "200": {
            "description": "Configurations d'avatars par utilisateur."
          }
        }
      }
    },
    "/wiki/mes-propositions": {
      "get": {
        "operationId": "listerMesPropositions",
        "summary": "Lister mes propositions en attente",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Propositions du compte connecté."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/wiki/mes-statistiques": {
      "get": {
        "operationId": "obtenirMesStatistiques",
        "summary": "Obtenir mon impact (statistiques d'auteur)",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Impact du compte connecté."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/wiki/profil": {
      "get": {
        "operationId": "obtenirMonProfil",
        "summary": "Obtenir mon profil wiki (réputation, rang, propositions 24 h)",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Profil wiki du compte connecté."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      },
      "patch": {
        "operationId": "mettreAJourMonProfil",
        "summary": "Mettre à jour mon profil (surnom, avatar)",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "surnom": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 40
                  },
                  "avatarConfig": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Profil mis à jour."
          },
          "401": {
            "description": "Connexion requise."
          },
          "422": {
            "description": "Valeur invalide."
          }
        }
      }
    },
    "/wiki/notifications": {
      "get": {
        "operationId": "listerNotifications",
        "summary": "Lister mes notifications",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limite",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 60
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Notifications du compte connecté."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      },
      "post": {
        "operationId": "marquerNotificationsLues",
        "summary": "Marquer toutes mes notifications comme lues",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Notifications marquées comme lues."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/wiki/onboarding": {
      "get": {
        "operationId": "obtenirOnboarding",
        "summary": "Lire mes réponses d'onboarding",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Réponses d'onboarding."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      },
      "post": {
        "operationId": "enregistrerOnboarding",
        "summary": "Enregistrer mes réponses d'onboarding",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "discoverySource": {
                    "type": "string"
                  },
                  "goals": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "ageAtteste": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Réponses enregistrées."
          },
          "401": {
            "description": "Connexion requise."
          },
          "422": {
            "description": "Réponse invalide."
          }
        }
      }
    },
    "/signalements/{id}": {
      "post": {
        "operationId": "traiterSignalement",
        "summary": "Décider d'un signalement de brique (modération)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du signalement.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "decision"
                ],
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "abus_confirme",
                      "rejete"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Décision enregistrée."
          },
          "403": {
            "description": "Accès modération réservé."
          },
          "404": {
            "description": "Signalement introuvable."
          }
        }
      }
    },
    "/retours/{id}": {
      "post": {
        "operationId": "traiterRetour",
        "summary": "Changer le statut d'un retour (admin)",
        "tags": [
          "Modération"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du retour.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "statut"
                ],
                "properties": {
                  "statut": {
                    "type": "string",
                    "enum": [
                      "nouveau",
                      "en_cours",
                      "resolu",
                      "rejete"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Statut mis à jour."
          },
          "403": {
            "description": "Réservé aux administrateurs."
          },
          "404": {
            "description": "Retour introuvable."
          }
        }
      }
    },
    "/profil/export": {
      "get": {
        "operationId": "exporterMesDonnees",
        "summary": "Exporter mes données personnelles (RGPD Art. 20)",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Archive de portabilité des données."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/profil/supprimer": {
      "post": {
        "operationId": "supprimerMonCompte",
        "summary": "Supprimer mon compte et mes données (RGPD Art. 17)",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirmation"
                ],
                "properties": {
                  "confirmation": {
                    "type": "string",
                    "description": "Confirmation explicite de la suppression."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Compte supprimé."
          },
          "401": {
            "description": "Connexion requise."
          },
          "422": {
            "description": "Confirmation manquante."
          }
        }
      }
    },
    "/recuperation/codes": {
      "get": {
        "operationId": "etatCodesRecuperation",
        "summary": "État de mes codes de secours",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Nombre et état des codes de secours."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      },
      "post": {
        "operationId": "genererCodesRecuperation",
        "summary": "Générer un lot de codes de secours",
        "tags": [
          "Compte"
        ],
        "security": [
          {
            "BearerAuth": []
          },
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Nouveaux codes de secours."
          },
          "401": {
            "description": "Connexion requise."
          }
        }
      }
    },
    "/recuperation/demande": {
      "post": {
        "operationId": "demanderRecuperation",
        "summary": "Demander une récupération d'accès assistée",
        "description": "Crée une demande dans la boîte admin (retour `acces_compte`). Réponse toujours générique, sans énumération de comptes.",
        "tags": [
          "Compte"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "recontact"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "recontact": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Demande transmise."
          },
          "422": {
            "description": "Champs invalides."
          },
          "429": {
            "description": "Limite atteinte."
          }
        }
      }
    },
    "/recuperation/reinitialiser": {
      "post": {
        "operationId": "reinitialiserParCode",
        "summary": "Réinitialiser un mot de passe par code de secours",
        "tags": [
          "Compte"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "code",
                  "nouveauMotDePasse"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string"
                  },
                  "nouveauMotDePasse": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mot de passe réinitialisé."
          },
          "400": {
            "description": "Email ou code invalide."
          },
          "429": {
            "description": "Limite atteinte."
          }
        }
      }
    },
    "/developpeurs/applications": {
      "get": {
        "operationId": "listerApplicationsApi",
        "summary": "Lister mes applications d'API",
        "description": "Connexion web (cookie) requise : les jetons Bearer ne peuvent pas administrer les applications.",
        "tags": [
          "Développeurs"
        ],
        "security": [
          {
            "CookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Applications du compte connecté."
          },
          "401": {
            "description": "Connexion web requise."
          }
        }
      },
      "post": {
        "operationId": "creerApplicationApi",
        "summary": "Créer une application d'API",
        "tags": [
          "Développeurs"
        ],
        "security": [
          {
            "CookieAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "nom",
                  "contactEmail"
                ],
                "properties": {
                  "nom": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "contactEmail": {
                    "type": "string"
                  },
                  "callbackUrl": {
                    "type": "string"
                  },
                  "homepageUrl": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Application créée."
          },
          "401": {
            "description": "Connexion web requise."
          },
          "429": {
            "description": "Limite de création atteinte."
          }
        }
      }
    },
    "/developpeurs/applications/{id}": {
      "delete": {
        "operationId": "revoquerApplicationApi",
        "summary": "Révoquer une application d'API",
        "tags": [
          "Développeurs"
        ],
        "security": [
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant de l'application.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Application révoquée."
          },
          "401": {
            "description": "Connexion web requise."
          }
        }
      }
    },
    "/developpeurs/applications/{id}/jetons": {
      "post": {
        "operationId": "creerJetonApi",
        "summary": "Générer un jeton porteur pour une application",
        "description": "Le secret `an_live_...` n'est retourné qu'une seule fois.",
        "tags": [
          "Développeurs"
        ],
        "security": [
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant de l'application.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "nom": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Jeton créé (secret affiché une seule fois)."
          },
          "401": {
            "description": "Connexion web requise."
          },
          "429": {
            "description": "Limite de génération atteinte."
          }
        }
      }
    },
    "/developpeurs/jetons/{id}": {
      "delete": {
        "operationId": "revoquerJetonApi",
        "summary": "Révoquer un jeton porteur",
        "tags": [
          "Développeurs"
        ],
        "security": [
          {
            "CookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant du jeton.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Jeton révoqué."
          },
          "401": {
            "description": "Connexion web requise."
          }
        }
      }
    },
    "/banque-images/recherche": {
      "get": {
        "operationId": "rechercherPhotosBanque",
        "summary": "Rechercher des photos libres de droits",
        "description": "Recherche paginée de photographies sur Pixabay avec mise en cache 24h.",
        "tags": [
          "Banque d'images"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Terme de recherche.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Numéro de page.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "parPage",
            "in": "query",
            "required": false,
            "description": "Résultats par page.",
            "schema": {
              "type": "integer",
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Résultats de recherche Pixabay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResultatRechercheBanque"
                }
              }
            }
          },
          "503": {
            "description": "Clé API non configurée."
          }
        }
      }
    },
    "/banque-images/telecharger": {
      "get": {
        "operationId": "telechargerPhotoBanque",
        "summary": "Télécharger une photo de la banque d'images",
        "description": "Proxy same-origin de flux binaire pour l'import d'une photographie dans l'Atelier.",
        "tags": [
          "Banque d'images"
        ],
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "URL source Pixabay.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Flux binaire de l'image (JPEG, PNG ou WebP)."
          },
          "400": {
            "description": "URL invalide ou manquante."
          },
          "403": {
            "description": "Domaine non autorisé."
          },
          "502": {
            "description": "Échec du téléchargement amont."
          }
        }
      }
    },
    "/banque-images/auto": {
      "post": {
        "operationId": "obtenirPhotoAutomatique",
        "summary": "Obtenir automatiquement une photo libre",
        "description": "Recherche et sélectionne la meilleure photographie Pixabay pour un contexte donné (titre, type, tags).",
        "tags": [
          "Banque d'images"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "titre": {
                    "type": "string",
                    "description": "Titre de l'activité ou du chant."
                  },
                  "type": {
                    "type": "string",
                    "description": "Nature ou type de contenu."
                  },
                  "lieu": {
                    "type": "string",
                    "description": "Lieu privilégié."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Mots-clés associés."
                  },
                  "description": {
                    "type": "string",
                    "description": "Description textuelle."
                  }
                },
                "required": [
                  "titre"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Photo recommandée ou repli canonique.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "photo": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/PhotoBanque"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "banniereUrl": {
                      "type": "string",
                      "description": "URL de la bannière recommandée."
                    }
                  },
                  "required": [
                    "banniereUrl"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Paramètres manquants ou invalides."
          }
        }
      }
    }
  }
}