{
  "openapi": "3.1.0",
  "info": {
    "title": "API de l'Atelier de la Route du Rhum",
    "version": "1.0.0",
    "summary": "Catalogue de rhums arrangés, ateliers de création et contenus de l'Atelier de la Route du Rhum (Compiègne).",
    "description": "API REST JSON de l'Atelier de la Route du Rhum. Les routes de lecture (catalogue, ateliers, articles) sont publiques et ne demandent aucune clé. Les routes de compte utilisent un jeton JWT (en-tête `Authorization: Bearer <jeton>`). Toutes les erreurs ont la forme `{ error, message, code, status, hint, docs }`. Limites : 120 requêtes par minute et par IP sur l'API générale. Documentation : https://atelier-route-du-rhum.com/docs Versionnement : la version est dans le chemin (/api/v1/...) et rappelée par l'en-tête de réponse API-Version. /api/... sans version est un alias de la version courante. Dépréciation : une route ou un champ retiré est annoncé au moins 6 mois avant par les en-têtes Deprecation, Sunset (RFC 8594) et Link rel=\"successor-version\" ; une rupture de compatibilité donne une nouvelle version majeure (/api/v2) et l'ancienne reste servie pendant la période de transition.",
    "contact": {
      "name": "L'Atelier de la Route du Rhum",
      "url": "https://atelier-route-du-rhum.com/contact",
      "email": "69znabil@gmail.com"
    }
  },
  "servers": [
    {
      "url": "https://atelier-route-du-rhum.com",
      "description": "Production (proxy vers l'API)"
    }
  ],
  "x-api-versioning": {
    "scheme": "url-path",
    "currentVersion": "v1",
    "versionHeader": "API-Version",
    "unversionedAlias": "/api/{path} = /api/v1/{path}",
    "deprecationPolicy": {
      "minimumNoticeMonths": 6,
      "signals": [
        "Deprecation",
        "Sunset",
        "Link: rel=\"successor-version\""
      ],
      "breakingChanges": "Nouvelle version majeure dans le chemin ; l'ancienne reste servie pendant la transition."
    }
  },
  "externalDocs": {
    "description": "Documentation développeur",
    "url": "https://atelier-route-du-rhum.com/docs"
  },
  "tags": [
    {
      "name": "Système",
      "description": "Santé et découverte de l'API."
    },
    {
      "name": "Boutique",
      "description": "Catalogue de rhums arrangés et catégories."
    },
    {
      "name": "Ateliers",
      "description": "Ateliers de dégustation et de conception."
    },
    {
      "name": "Contenu",
      "description": "Articles et métadonnées SEO."
    },
    {
      "name": "Contact",
      "description": "Formulaire de contact et newsletter."
    },
    {
      "name": "Cartes cadeaux",
      "description": "Validation des cartes cadeaux."
    },
    {
      "name": "Compte",
      "description": "Authentification et profil."
    }
  ],
  "paths": {
    "/api/v1/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Système"
        ],
        "summary": "Vérifier que l'API est opérationnelle",
        "description": "Renvoie `status: \"success\"` quand le serveur répond. Utile comme sonde avant d'enchaîner des appels.",
        "responses": {
          "200": {
            "description": "Le serveur est opérationnel.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "message"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "success"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpec",
        "tags": [
          "Système"
        ],
        "summary": "Télécharger cette spécification OpenAPI",
        "description": "Renvoie la spécification OpenAPI 3.1 de l'API au format JSON.",
        "responses": {
          "200": {
            "description": "Spécification OpenAPI.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/products": {
      "get": {
        "operationId": "listProducts",
        "tags": [
          "Boutique"
        ],
        "summary": "Lister les rhums arrangés du catalogue",
        "description": "Renvoie tous les produits de la boutique avec leurs formats (volumes) et prix TTC en euros. Les contenus sont traduits selon `Accept-Language`.",
        "parameters": [
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des produits.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/products/slug/{slug}": {
      "get": {
        "operationId": "getProductBySlug",
        "tags": [
          "Boutique"
        ],
        "summary": "Lire une fiche produit par son slug",
        "description": "Renvoie un produit à partir de son slug d'URL (ex: `rhum-fraise-basilic`). Répond 404 si le slug est inconnu.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug du produit, tel qu'il apparaît dans l'URL /boutique/{slug}.",
            "schema": {
              "type": "string",
              "examples": [
                "rhum-fraise-basilic"
              ]
            }
          },
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Le produit.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "404": {
            "description": "Aucun produit pour ce slug.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/categories": {
      "get": {
        "operationId": "listCategories",
        "tags": [
          "Boutique"
        ],
        "summary": "Lister les catégories de la boutique",
        "description": "Renvoie les 4 collections de rhums (épicés, fruités, aux plantes, mixologie) avec leur description.",
        "parameters": [
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des catégories.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Category"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workshops": {
      "get": {
        "operationId": "listWorkshops",
        "tags": [
          "Ateliers"
        ],
        "summary": "Lister les ateliers",
        "description": "Renvoie tous les ateliers (découverte, cursus de conception niveaux 1 à 4, atelier duo) avec prix, durée et format.",
        "parameters": [
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des ateliers.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Workshop"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workshops/decouverte": {
      "get": {
        "operationId": "getDiscoveryWorkshop",
        "tags": [
          "Ateliers"
        ],
        "summary": "Lire l'atelier découverte",
        "description": "Renvoie l'atelier d'initiation à la dégustation et à la fabrication de rhum arrangé.",
        "parameters": [
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "L'atelier découverte.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workshop"
                }
              }
            }
          },
          "404": {
            "description": "Atelier introuvable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workshops/duo": {
      "get": {
        "operationId": "getDuoWorkshop",
        "tags": [
          "Ateliers"
        ],
        "summary": "Lire l'atelier duo",
        "description": "Renvoie l'atelier à deux, avec son tarif duo.",
        "parameters": [
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "L'atelier duo.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workshop"
                }
              }
            }
          },
          "404": {
            "description": "Atelier introuvable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workshops/conception/{level}": {
      "get": {
        "operationId": "getConceptionWorkshopByLevel",
        "tags": [
          "Ateliers"
        ],
        "summary": "Lire un niveau du cursus de conception",
        "description": "Renvoie l'atelier du cursus en 4 niveaux : 1 Fruits, 2 Épices, 3 Plantes, 4 Mixologie.",
        "parameters": [
          {
            "name": "level",
            "in": "path",
            "required": true,
            "description": "Niveau du cursus, de 1 à 4.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 4
            }
          },
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "L'atelier du niveau demandé.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workshop"
                }
              }
            }
          },
          "400": {
            "description": "Niveau non numérique.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Niveau inconnu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workshops/{id}": {
      "get": {
        "operationId": "getWorkshopById",
        "tags": [
          "Ateliers"
        ],
        "summary": "Lire un atelier par identifiant",
        "description": "Renvoie un atelier à partir de son identifiant UUID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identifiant UUID de l'atelier.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "L'atelier.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workshop"
                }
              }
            }
          },
          "404": {
            "description": "Atelier introuvable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles": {
      "get": {
        "operationId": "listArticles",
        "tags": [
          "Contenu"
        ],
        "summary": "Lister les articles publiés",
        "description": "Renvoie les articles d'actualité publiés, du plus récent au plus ancien.",
        "parameters": [
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des articles.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Article"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/articles/slug/{slug}": {
      "get": {
        "operationId": "getArticleBySlug",
        "tags": [
          "Contenu"
        ],
        "summary": "Lire un article par son slug",
        "description": "Renvoie un article publié à partir de son slug d'URL (/actualites/{slug}).",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Slug de l'article.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Accept-Language",
            "in": "header",
            "required": false,
            "description": "Langue des contenus renvoyés. Valeurs supportées : fr (défaut), en, es.",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "es"
              ],
              "default": "fr"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "L'article.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Article"
                }
              }
            }
          },
          "404": {
            "description": "Article introuvable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/seo/{page}": {
      "get": {
        "operationId": "getPageSeo",
        "tags": [
          "Contenu"
        ],
        "summary": "Lire le titre et la méta-description d'une page",
        "description": "Renvoie le titre et la méta-description SEO configurés pour une page (ex: `home`, `contact`).",
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "description": "Identifiant de la page (ex: home, contact).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Métadonnées SEO.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string"
                    },
                    "metaDescription": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Page inconnue (corps `{ message }` enrichi par l'enveloppe d'erreur).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/contact": {
      "post": {
        "operationId": "sendContactMessage",
        "tags": [
          "Contact"
        ],
        "summary": "Envoyer un message à l'atelier",
        "description": "Envoie le formulaire de contact. L'atelier répond par email, généralement sous 48 h ouvrées.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "nom",
                  "email",
                  "message"
                ],
                "properties": {
                  "nom": {
                    "type": "string",
                    "description": "Nom de la personne."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Adresse de réponse."
                  },
                  "sujet": {
                    "type": "string",
                    "description": "Objet du message."
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "Contenu du message (2000 caractères maximum)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message envoyé.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Champs manquants ou invalides.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/newsletter/subscribe": {
      "post": {
        "operationId": "subscribeToNewsletter",
        "tags": [
          "Contact"
        ],
        "summary": "S'inscrire à la newsletter",
        "description": "Inscrit une adresse email à la newsletter (nouveautés, ateliers, offres). À n'utiliser qu'avec le consentement explicite de la personne.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Adresse à inscrire."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Inscription enregistrée.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Email invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/gift-cards/validate": {
      "post": {
        "operationId": "validateGiftCard",
        "tags": [
          "Cartes cadeaux"
        ],
        "summary": "Vérifier une carte cadeau",
        "description": "Vérifie un code de carte cadeau (format `RHUM-XXXX`) et renvoie son solde restant. Limité à 5 requêtes par minute.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "Code de la carte, ex: RHUM-AB12.",
                    "examples": [
                      "RHUM-AB12"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Carte valide.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "balance": {
                      "type": "number",
                      "description": "Solde restant en euros."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Code manquant ou carte expirée (validité 12 mois).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Code inconnu ou épuisé.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/register": {
      "post": {
        "operationId": "registerUser",
        "tags": [
          "Compte"
        ],
        "summary": "Créer un compte client",
        "description": "Crée un compte et envoie un email de vérification. Limité à 10 requêtes par 15 minutes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password",
                  "firstName",
                  "lastName"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "description": "8 caractères minimum, au moins une lettre et un chiffre."
                  },
                  "firstName": {
                    "type": "string",
                    "minLength": 2
                  },
                  "lastName": {
                    "type": "string",
                    "minLength": 2
                  },
                  "phone": {
                    "type": "string",
                    "description": "Téléphone français (optionnel)."
                  },
                  "companyName": {
                    "type": "string",
                    "description": "Raison sociale (compte PRO, optionnel)."
                  },
                  "siret": {
                    "type": "string",
                    "pattern": "^\\d{14}$",
                    "description": "SIRET à 14 chiffres (optionnel)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Compte créé.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "description": "Données invalides — voir `details`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "operationId": "loginUser",
        "tags": [
          "Compte"
        ],
        "summary": "Se connecter",
        "description": "Authentifie un client. Renvoie un jeton JWT à renvoyer ensuite en `Authorization: Bearer`. Limité à 10 requêtes par 15 minutes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Connexion réussie.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Jeton JWT."
                    },
                    "user": {
                      "$ref": "#/components/schemas/User"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Données invalides.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants incorrects.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/users/me": {
      "get": {
        "operationId": "getCurrentUser",
        "tags": [
          "Compte"
        ],
        "summary": "Lire son profil",
        "description": "Renvoie le profil du client authentifié. Nécessite un jeton Bearer.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Le profil.",
            "headers": {
              "API-Version": {
                "description": "Version de l'API ayant servi la réponse (ex: 1).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "401": {
            "description": "Jeton absent ou invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Trop de requêtes — respectez l'en-tête Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne du serveur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Jeton obtenu via POST /api/auth/login."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message",
          "code",
          "status"
        ],
        "description": "Enveloppe commune à toutes les erreurs de l'API.",
        "properties": {
          "error": {
            "type": "string",
            "description": "Message lisible (alias de `message`)."
          },
          "message": {
            "type": "string",
            "description": "Explication de l'erreur."
          },
          "code": {
            "type": "string",
            "description": "Code stable lisible par machine.",
            "examples": [
              "NOT_FOUND",
              "RATE_LIMITED",
              "BAD_REQUEST"
            ]
          },
          "status": {
            "type": "integer",
            "description": "Statut HTTP."
          },
          "hint": {
            "type": "string",
            "description": "Piste de résolution."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "Lien vers la documentation."
          },
          "details": {
            "type": "array",
            "description": "Erreurs de validation champ par champ (statut 400).",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Message": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "Category": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        }
      },
      "ProductVolume": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "size": {
            "type": "number",
            "description": "Contenance numérique, ex: 35 (avec `unit`)."
          },
          "unit": {
            "type": "string",
            "description": "Unité de la contenance, ex: cl."
          },
          "price": {
            "type": "number",
            "description": "Prix TTC en euros."
          },
          "stock": {
            "type": "integer"
          },
          "isDiscounted": {
            "type": "boolean"
          },
          "originalPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Prix avant remise, si remisé."
          }
        }
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "categoryId": {
            "type": "string",
            "format": "uuid"
          },
          "category": {
            "$ref": "#/components/schemas/Category"
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVolume"
            }
          }
        }
      },
      "Workshop": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "level": {
            "type": "integer",
            "description": "Niveau du cursus (1 à 4), 0 pour découverte/duo."
          },
          "type": {
            "type": "string",
            "enum": [
              "PARTICULIER",
              "ENTREPRISE"
            ]
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "format": {
            "type": "string"
          },
          "duration": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": "number",
            "description": "Prix particulier TTC en euros."
          },
          "priceInstitutional": {
            "type": "number",
            "description": "Prix institutionnel (CE, entreprises) en euros."
          },
          "duoPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Prix pour deux personnes, si proposé."
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        }
      },
      "Article": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "excerpt": {
            "type": [
              "string",
              "null"
            ]
          },
          "content": {
            "type": "string",
            "description": "Contenu au format Markdown."
          },
          "coverImage": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "coverImageAlt": {
            "type": [
              "string",
              "null"
            ]
          },
          "authorName": {
            "type": [
              "string",
              "null"
            ]
          },
          "publishedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "USER",
              "PRO",
              "ADMIN"
            ]
          },
          "memberCode": {
            "type": "string",
            "description": "Code membre, format RR-26-XXXX."
          },
          "isProfileComplete": {
            "type": "boolean"
          },
          "conceptionLevel": {
            "type": "integer",
            "description": "Niveau du cursus validé."
          }
        }
      }
    }
  }
}
