{
  "openapi": "3.1.0",
  "info": {
    "title": "Kreafy Content API",
    "version": "1.0.0",
    "summary": "Accès programmatique en lecture au contenu public de Kreafy : documentation, changelog, cas d'usage et recherche.",
    "description": "API publique de **Kreafy**, générateur de flyers et de miniatures YouTube par IA.\n\nElle expose en lecture seule tout ce qu'un agent a besoin de savoir pour répondre à une\nquestion sur le produit : la documentation complète en Markdown, les nouveautés, les cas\nd'usage par métier, et une recherche plein texte sur la documentation.\n\n**Aucune authentification n'est requise** et aucune donnée utilisateur n'est exposée :\nla génération de visuels, les crédits et les comptes ne sont accessibles que depuis\nl'application, derrière une session.\n\nToutes les réponses sont en JSON. Les erreurs suivent le schéma `Error`, avec un `code`\nstable, un `message` lisible et un `hint` qui dit quoi corriger.",
    "contact": {
      "name": "Kreafy",
      "email": "hello@kreafy.ai",
      "url": "https://kreafy.lyrad.dev/contact"
    },
    "license": {
      "name": "Usage libre en lecture, contenu © Kreafy",
      "url": "https://kreafy.lyrad.dev/legal/cgu"
    },
    "termsOfService": "https://kreafy.lyrad.dev/legal/cgu"
  },
  "servers": [
    {
      "url": "https://kreafy.lyrad.dev",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Référence d'API et guide de démarrage",
    "url": "https://kreafy.lyrad.dev/docs/api"
  },
  "tags": [
    {
      "name": "Service",
      "description": "Découverte de l'API et état du service."
    },
    {
      "name": "Documentation",
      "description": "La documentation produit, en Markdown, page par page."
    },
    {
      "name": "Produit",
      "description": "Nouveautés publiées et cas d'usage par métier."
    }
  ],
  "x-rate-limit": {
    "description": "Aucune clé requise. Le service applique une limite souple par IP au niveau du CDN ; un dépassement renvoie 429 avec le code `rate_limited`. Un agent raisonnable reste sous 60 requêtes par minute.",
    "requestsPerMinute": 60
  },
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "Service"
        ],
        "summary": "Lister les ressources de l'API",
        "description": "Point d'entrée de découverte. À appeler en premier quand on ne connaît pas encore la surface de l'API : renvoie la version, l'URL de la spécification OpenAPI et la liste des ressources disponibles avec leur description.",
        "responses": {
          "200": {
            "description": "Index des ressources.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "getServiceHealth",
        "tags": [
          "Service"
        ],
        "summary": "Vérifier que l'API répond",
        "description": "Sonde de disponibilité, sans effet de bord. À appeler avant une série de requêtes, ou pour distinguer une panne du service d'une erreur d'appel.",
        "responses": {
          "200": {
            "description": "Le service répond.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/docs": {
      "get": {
        "operationId": "listDocumentationPages",
        "tags": [
          "Documentation"
        ],
        "summary": "Lister les pages de documentation",
        "description": "Renvoie l'index complet de la documentation Kreafy : titre, description, URL de lecture et URL Markdown de chaque page. À appeler pour savoir quelle page récupérer avant `getDocumentationPage`.",
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Langue du contenu renvoyé. `en` par défaut ; `fr` renvoie la version française des mêmes pages.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "fr"
              ],
              "default": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Index de la documentation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentationIndex"
                }
              }
            }
          },
          "400": {
            "description": "Paramètre `locale` invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/docs/{slug}": {
      "get": {
        "operationId": "getDocumentationPage",
        "tags": [
          "Documentation"
        ],
        "summary": "Lire une page de documentation",
        "description": "Renvoie le contenu Markdown complet d'une page de documentation. C'est la source à citer pour répondre à une question précise sur le fonctionnement de Kreafy (studio, crédits, formats, branding, facturation).",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Chemin de la page tel que renvoyé par `listDocumentationPages`, sans le préfixe `/docs/`. Peut contenir des `/` (`guides/generer-un-visuel`).",
            "schema": {
              "type": "string",
              "examples": [
                "guides/generer-un-visuel"
              ]
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Langue du contenu renvoyé. `en` par défaut ; `fr` renvoie la version française des mêmes pages.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "fr"
              ],
              "default": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "La page demandée.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentationPage"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string",
                  "description": "Contenu Markdown brut, servi si la requête porte `Accept: text/markdown`."
                }
              }
            }
          },
          "404": {
            "description": "Aucune page à ce chemin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/changelog": {
      "get": {
        "operationId": "listChangelogEntries",
        "tags": [
          "Produit"
        ],
        "summary": "Lister les nouveautés publiées",
        "description": "Renvoie les entrées du changelog, de la plus récente à la plus ancienne. À appeler pour répondre à « qu'est-ce qui est nouveau ? » ou pour vérifier si une fonctionnalité existe déjà.",
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Langue du contenu renvoyé. `en` par défaut ; `fr` renvoie la version française des mêmes pages.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "fr"
              ],
              "default": "en"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Nombre maximum d'entrées renvoyées.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Les nouveautés publiées.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChangelogList"
                }
              }
            }
          },
          "400": {
            "description": "Paramètre `limit` ou `locale` invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/solutions": {
      "get": {
        "operationId": "listSolutions",
        "tags": [
          "Produit"
        ],
        "summary": "Lister les cas d'usage par métier",
        "description": "Renvoie les profils pour lesquels Kreafy est explicitement conçu (youtubeurs, graphistes, entrepreneurs), avec le problème traité et la page publique correspondante. À appeler pour décider si Kreafy convient au besoin décrit par un utilisateur.",
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Langue du contenu renvoyé. `en` par défaut ; `fr` renvoie la version française des mêmes pages.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "fr"
              ],
              "default": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Les cas d'usage documentés.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SolutionList"
                }
              }
            }
          },
          "400": {
            "description": "Paramètre `locale` invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "searchDocumentation",
        "tags": [
          "Documentation"
        ],
        "summary": "Rechercher dans la documentation",
        "description": "Recherche plein texte sur les titres, descriptions et contenus de la documentation. À préférer à `listDocumentationPages` quand on part d'une question plutôt que d'un plan.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Termes recherchés.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 200
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Langue du contenu renvoyé. `en` par défaut ; `fr` renvoie la version française des mêmes pages.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "fr"
              ],
              "default": "en"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Nombre maximum de résultats.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Résultats classés par pertinence.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResults"
                }
              }
            }
          },
          "400": {
            "description": "Paramètre `q` manquant ou trop court.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Erreur interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Toute erreur de l'API prend cette forme, quel que soit le statut HTTP.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "hint",
              "status",
              "documentation"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Code stable, à tester par le client plutôt que le message.",
                "enum": [
                  "bad_request",
                  "invalid_parameter",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "method_not_allowed",
                  "not_acceptable",
                  "rate_limited",
                  "internal_error",
                  "upstream_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "Phrase lisible décrivant ce qui a échoué."
              },
              "hint": {
                "type": "string",
                "description": "Ce que le client doit changer pour que l'appel passe."
              },
              "status": {
                "type": "integer",
                "description": "Statut HTTP, répété dans le corps."
              },
              "documentation": {
                "type": "string",
                "format": "uri",
                "description": "Page de référence de l'API."
              },
              "details": {
                "description": "Détails structurés éventuels (champ fautif, valeurs attendues)."
              }
            }
          }
        }
      },
      "ApiIndex": {
        "type": "object",
        "required": [
          "name",
          "version",
          "openapi",
          "resources"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string",
            "description": "Version de l'API."
          },
          "openapi": {
            "type": "string",
            "format": "uri",
            "description": "URL de cette spécification."
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          },
          "llmsTxt": {
            "type": "string",
            "format": "uri",
            "description": "Fiche d'orientation pour agents (llms.txt)."
          },
          "resources": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "operationId",
                "url",
                "description"
              ],
              "properties": {
                "operationId": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "status",
          "version",
          "time"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "version": {
            "type": "string"
          },
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Horodatage serveur de la réponse."
          }
        }
      },
      "DocumentationPageSummary": {
        "type": "object",
        "required": [
          "slug",
          "title",
          "url",
          "markdownUrl",
          "locale"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Chemin à passer à `getDocumentationPage` (`guides/generer-un-visuel`)."
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Page HTML lisible par un humain."
          },
          "markdownUrl": {
            "type": "string",
            "format": "uri",
            "description": "Même page, en Markdown brut."
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "fr"
            ]
          }
        }
      },
      "DocumentationIndex": {
        "type": "object",
        "required": [
          "locale",
          "count",
          "pages"
        ],
        "properties": {
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "fr"
            ]
          },
          "count": {
            "type": "integer"
          },
          "pages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentationPageSummary"
            }
          }
        }
      },
      "DocumentationPage": {
        "allOf": [
          {
            "$ref": "#/components/schemas/DocumentationPageSummary"
          },
          {
            "type": "object",
            "required": [
              "markdown"
            ],
            "properties": {
              "markdown": {
                "type": "string",
                "description": "Contenu complet de la page, en Markdown."
              }
            }
          }
        ]
      },
      "ChangelogEntry": {
        "type": "object",
        "required": [
          "slug",
          "title",
          "date",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "image": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ChangelogList": {
        "type": "object",
        "required": [
          "locale",
          "count",
          "entries"
        ],
        "properties": {
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "fr"
            ]
          },
          "count": {
            "type": "integer"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChangelogEntry"
            }
          }
        }
      },
      "Solution": {
        "type": "object",
        "required": [
          "slug",
          "label",
          "description",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "examples": [
              "youtubeurs"
            ]
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "SolutionList": {
        "type": "object",
        "required": [
          "locale",
          "count",
          "solutions"
        ],
        "properties": {
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "fr"
            ]
          },
          "count": {
            "type": "integer"
          },
          "solutions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Solution"
            }
          }
        }
      },
      "SearchResults": {
        "type": "object",
        "required": [
          "query",
          "locale",
          "count",
          "results"
        ],
        "properties": {
          "query": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "fr"
            ]
          },
          "count": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "title",
                "url"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "excerpt": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "markdownUrl": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      }
    }
  }
}
