{"openapi":"3.1.0","info":{"title":"Cavarom API","version":"2.0.0","description":"## API Cavarom\n\nBase de données mondiale de rhums + gestion de cave personnelle.\n\n### Authentification\nLes endpoints marqués 🔒 requièrent un cookie de session **better-auth**.\nPour les appels API-to-API, passez le token via `Authorization: Bearer <token>`.\n\n### Endpoints publics (🌐)\nAucune clé requise — accès libre en lecture au catalogue.\n\n### Rate limiting\n100 req/min par IP sur les endpoints publics.","contact":{"url":"https://cavarom.fr","email":"contact@cavarom.fr"},"license":{"name":"MIT"}},"servers":[{"url":"/api/v1","description":"API publique v1"}],"tags":[{"name":"Catalogue","description":"🌐 Parcourir la base de rhums — accès public"},{"name":"Cave","description":"🔒 Gestion de votre cave personnelle"},{"name":"Collections","description":"🔒 Collections personnalisées dans la cave"},{"name":"Notes","description":"🔒/🌐 Notes de dégustation et scores communautaires"},{"name":"Profil","description":"🔒 Profil utilisateur et préférences"},{"name":"Utilisateurs","description":"🌐 Caves et wishlists publiques"},{"name":"Utilitaires","description":"🌐 Scan code-barres, santé API"}],"components":{"securitySchemes":{"sessionCookie":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Cookie de session géré automatiquement après connexion"}},"schemas":{"RumSummary":{"type":"object","required":["id","slug","name","verified","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string","example":"plantation-original-dark"},"name":{"type":"string","example":"Plantation Original Dark"},"brand":{"type":"string","nullable":true},"distillery":{"type":"string","nullable":true,"example":"Maison Ferrand"},"country":{"type":"string","nullable":true,"example":"Barbade"},"region":{"type":"string","nullable":true},"type":{"type":"string","nullable":true,"enum":["agricole","industriel","vieux","hors_dage","blanc","ambré","spiced","overproof"]},"abv":{"type":"string","nullable":true,"example":"40.0","description":"Degré alcool en %"},"ageYears":{"type":"integer","nullable":true,"example":5},"vintageYear":{"type":"integer","nullable":true,"example":2012},"volume":{"type":"integer","nullable":true,"example":700,"description":"Contenance en mL"},"imageUrl":{"type":"string","format":"uri","nullable":true},"verified":{"type":"boolean","description":"Fiche vérifiée par l'équipe"},"createdAt":{"type":"string","format":"date-time"},"communityScore":{"nullable":true,"type":"object","properties":{"avg":{"type":"number","example":84.5},"count":{"type":"integer","example":12}}}}},"Rum":{"allOf":[{"$ref":"#/components/schemas/RumSummary"},{"type":"object","properties":{"description":{"type":"string","nullable":true},"barcode":{"type":"string","nullable":true},"marketPrice":{"type":"string","nullable":true,"example":"34.90"},"marketPriceCurrency":{"type":"string","nullable":true,"example":"EUR"},"marketPriceSource":{"type":"string","nullable":true}}}]},"CellarEntry":{"type":"object","required":["id","rumId","userId","quantity","status","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"rumId":{"type":"string","format":"uuid"},"quantity":{"type":"integer","minimum":1,"example":2},"status":{"type":"string","enum":["sealed","open","empty","wanted"],"description":"sealed=scellée, open=ouverte, empty=vide, wanted=wishlist"},"purchasePrice":{"type":"number","nullable":true,"example":42.5},"purchaseDate":{"type":"string","format":"date","nullable":true},"personalScore":{"type":"integer","minimum":1,"maximum":5,"nullable":true},"tastingNotes":{"type":"string","nullable":true},"openedAt":{"type":"string","format":"date","nullable":true},"priceAlert":{"type":"number","nullable":true},"collectionIds":{"type":"array","items":{"type":"string","format":"uuid"}},"createdAt":{"type":"string","format":"date-time"},"rum":{"$ref":"#/components/schemas/RumSummary"}}},"Collection":{"type":"object","required":["id","name","emoji","color","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string","example":"Rhums agricoles"},"emoji":{"type":"string","example":"🌿"},"color":{"type":"string","example":"#22c55e","description":"Couleur hexadécimale"},"entryCount":{"type":"integer","example":8},"createdAt":{"type":"string","format":"date-time"}}},"Rating":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"score":{"type":"integer","minimum":0,"maximum":100},"comment":{"type":"string","nullable":true},"authorName":{"type":"string","nullable":true},"authorUsername":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Pagination":{"type":"object","properties":{"page":{"type":"integer","example":1},"limit":{"type":"integer","example":24},"total":{"type":"integer","example":377},"hasMore":{"type":"boolean"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","example":"Non autorisé"}}}},"responses":{"Unauthorized":{"description":"Non authentifié","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Ressource introuvable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Données invalides","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/rums":{"get":{"operationId":"listRums","summary":"Lister les rhums","description":"Retourne la liste paginée des rhums avec filtres. Accès public.","tags":["Catalogue"],"parameters":[{"name":"search","in":"query","schema":{"type":"string"},"description":"Recherche par nom, distillerie, pays, marque"},{"name":"country","in":"query","schema":{"type":"string"},"example":"Martinique"},{"name":"type","in":"query","schema":{"type":"string","enum":["agricole","industriel","vieux","hors_dage","blanc","ambré","spiced","overproof"]}},{"name":"sort","in":"query","schema":{"type":"string","enum":["date","name","abv","score"],"default":"date"}},{"name":"minAbv","in":"query","schema":{"type":"number","minimum":0,"maximum":100}},{"name":"maxAbv","in":"query","schema":{"type":"number","minimum":0,"maximum":100}},{"name":"verified","in":"query","schema":{"type":"boolean"},"description":"Filtrer sur les fiches vérifiées uniquement"},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":24}}],"responses":{"200":{"description":"Liste paginée","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RumSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}}}},"post":{"operationId":"contributeRum","summary":"Contribuer un rhum","description":"Soumet une fiche rhum pour modération. Requiert une session.","tags":["Catalogue"],"security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":2,"maxLength":255},"distillery":{"type":"string"},"country":{"type":"string"},"type":{"type":"string","enum":["agricole","industriel","vieux","hors_dage","blanc","ambré","spiced","overproof"]},"abv":{"type":"number","minimum":0,"maximum":100},"ageYears":{"type":"integer","minimum":0,"maximum":100},"volume":{"type":"integer","minimum":50,"maximum":3000,"default":700},"description":{"type":"string","maxLength":2000},"barcode":{"type":"string","minLength":8,"maxLength":30}}}}}},"responses":{"201":{"description":"Rhum soumis, en attente de vérification"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/UnprocessableEntity"}}}},"/rums/facets":{"get":{"operationId":"getRumFacets","summary":"Facettes de filtrage","description":"Retourne les valeurs disponibles pour filtrer (pays, types). Accès public.","tags":["Catalogue"],"parameters":[{"name":"search","in":"query","schema":{"type":"string"}},{"name":"country","in":"query","schema":{"type":"string"}},{"name":"type","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Facettes disponibles","content":{"application/json":{"schema":{"type":"object","properties":{"countries":{"type":"array","items":{"type":"string"}},"types":{"type":"array","items":{"type":"string"}}}}}}}}}},"/rums/{slug}":{"get":{"operationId":"getRum","summary":"Fiche rhum complète","description":"Retourne tous les détails d'un rhum par son slug. Accès public.","tags":["Catalogue"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"plantation-original-dark"}],"responses":{"200":{"description":"Fiche rhum","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rum"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/rums/{slug}/ratings":{"get":{"operationId":"getRumRatings","summary":"Notes communautaires","description":"Retourne le score agrégé et les dernières notes pour un rhum. Accès public.","tags":["Notes"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"plantation-original-dark"}],"responses":{"200":{"description":"Scores","content":{"application/json":{"schema":{"type":"object","properties":{"aggregate":{"type":"object","properties":{"avg":{"type":"number","nullable":true,"example":84.5},"count":{"type":"integer","example":23}}},"ratings":{"type":"array","items":{"$ref":"#/components/schemas/Rating"}}}}}}}}},"post":{"operationId":"createRating","summary":"Ajouter / modifier sa note","description":"Crée ou met à jour la note de l'utilisateur connecté pour ce rhum.","tags":["Notes"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["score"],"properties":{"score":{"type":"integer","minimum":0,"maximum":100},"comment":{"type":"string","maxLength":1000,"nullable":true}}}}}},"responses":{"200":{"description":"Note enregistrée","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rating"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"delete":{"operationId":"deleteRating","summary":"Supprimer sa note","tags":["Notes"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Note supprimée"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/cave":{"get":{"operationId":"getCellar","summary":"Ma cave complète","description":"Retourne toutes les entrées de la cave de l'utilisateur connecté (cave + wishlist).","tags":["Cave"],"security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Liste des entrées","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CellarEntry"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"addToCellar","summary":"Ajouter une bouteille","description":"Ajoute un rhum à la cave. Le statut `wanted` place la bouteille en wishlist.","tags":["Cave"],"security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rumId"],"properties":{"rumId":{"type":"string","format":"uuid"},"quantity":{"type":"integer","minimum":1,"maximum":999,"default":1},"status":{"type":"string","enum":["sealed","open","empty","wanted"],"default":"sealed"},"purchasePrice":{"type":"number","minimum":0,"nullable":true},"purchaseDate":{"type":"string","format":"date","nullable":true},"personalScore":{"type":"integer","minimum":1,"maximum":5,"nullable":true},"tastingNotes":{"type":"string","maxLength":2000,"nullable":true}}}}}},"responses":{"201":{"description":"Entrée créée","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CellarEntry"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/UnprocessableEntity"}}}},"/cave/{id}":{"patch":{"operationId":"updateCellarEntry","summary":"Modifier une entrée","tags":["Cave"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1},"status":{"type":"string","enum":["sealed","open","empty","wanted"]},"purchasePrice":{"type":"number","nullable":true},"purchaseDate":{"type":"string","format":"date","nullable":true},"personalScore":{"type":"integer","minimum":1,"maximum":5,"nullable":true},"tastingNotes":{"type":"string","nullable":true},"openedAt":{"type":"string","format":"date","nullable":true},"priceAlert":{"type":"number","nullable":true}}}}}},"responses":{"200":{"description":"Entrée mise à jour","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CellarEntry"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteCellarEntry","summary":"Supprimer une entrée","tags":["Cave"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Entrée supprimée"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/cave/export":{"get":{"operationId":"exportCellar","summary":"Exporter la cave (CSV)","description":"Télécharge la cave complète au format CSV.","tags":["Cave"],"security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Fichier CSV","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/cave/import":{"post":{"operationId":"importCellar","summary":"Importer depuis CSV","description":"Importe des entrées en masse depuis un fichier CSV.","tags":["Cave"],"security":[{"sessionCookie":[]}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Import terminé","content":{"application/json":{"schema":{"type":"object","properties":{"imported":{"type":"integer"},"errors":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/collections":{"get":{"operationId":"listCollections","summary":"Mes collections","tags":["Collections"],"security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Liste des collections","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Collection"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createCollection","summary":"Créer une collection","tags":["Collections"],"security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"emoji":{"type":"string","maxLength":10,"default":"📂"},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","default":"#BA7517"}}}}}},"responses":{"201":{"description":"Collection créée","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Collection"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/collections/{id}":{"patch":{"operationId":"updateCollection","summary":"Modifier une collection","tags":["Collections"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"emoji":{"type":"string"},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}}}}}},"responses":{"200":{"description":"Collection mise à jour"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"delete":{"operationId":"deleteCollection","summary":"Supprimer une collection","description":"Supprime la collection. Les bouteilles ne sont pas supprimées.","tags":["Collections"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Collection supprimée"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/collections/{id}/entries":{"post":{"operationId":"addToCollection","summary":"Ajouter une bouteille à une collection","tags":["Collections"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID de la collection"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["cellarEntryId"],"properties":{"cellarEntryId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Bouteille ajoutée à la collection"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"delete":{"operationId":"removeFromCollection","summary":"Retirer une bouteille d'une collection","tags":["Collections"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"cellarEntryId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Bouteille retirée"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/profile":{"get":{"operationId":"getProfile","summary":"Mon profil","tags":["Profil"],"security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Profil utilisateur","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"plan":{"type":"string","enum":["free","pro"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"patch":{"operationId":"updateProfile","summary":"Modifier mon profil","tags":["Profil"],"security":[{"sessionCookie":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"username":{"type":"string","pattern":"^[a-z0-9_-]{3,30}$"}}}}}},"responses":{"200":{"description":"Profil mis à jour"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/UnprocessableEntity"}}}},"/users/{username}/wishlist":{"get":{"operationId":"getPublicWishlist","summary":"Wishlist publique d'un utilisateur","tags":["Utilisateurs"],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Wishlist","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CellarEntry"}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/users/{username}/cave":{"get":{"operationId":"getPublicCave","summary":"Cave publique d'un utilisateur","description":"Retourne la cave d'un utilisateur si elle est publique.","tags":["Utilisateurs"],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cave publique","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CellarEntry"}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/barcode":{"get":{"operationId":"lookupBarcode","summary":"Recherche par code-barres","description":"Cherche un rhum par code-barres EAN/UPC dans la base et Open Food Facts.","tags":["Utilitaires"],"parameters":[{"name":"barcode","in":"query","required":true,"schema":{"type":"string","minLength":8,"maxLength":14},"example":"3175681137447"}],"responses":{"200":{"description":"Résultat de la recherche","content":{"application/json":{"schema":{"type":"object","properties":{"rum":{"$ref":"#/components/schemas/RumSummary","nullable":true,"description":"Rhum trouvé dans la base"},"prefill":{"type":"object","nullable":true,"description":"Données Open Food Facts pour pré-remplir la contribution"}}}}}}}}},"/health":{"get":{"operationId":"healthCheck","summary":"Santé de l'API","tags":["Utilitaires"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"}}}}}}}}}}}