{
  "openapi": "3.1.0",
  "info": {
    "title": "HyperQuiz API",
    "version": "1.0.0",
    "summary": "Génération de questionnaires QCM par IA, sur n'importe quel thème.",
    "description": "Noyau de génération de questionnaires QCM. Tous les endpoints sous `/api/*` exigent le header `x-api-key`, sauf `/api/health`, `/api/version`, `/api/openapi.json` et `/api/postman.json`.\n\nVoir aussi : [Swagger UI](/docs), [Reference (Redoc)](/reference), [Postman Collection](/api/postman.json).",
    "contact": {
      "name": "HyperQuiz",
      "url": "https://api.hyperquiz.fr"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.hyperquiz.fr",
      "description": "Serveur courant"
    }
  ],
  "tags": [
    {
      "name": "Questionnaires",
      "description": "Génération de questions QCM."
    },
    {
      "name": "Tuning",
      "description": "Préparation du TuningContent."
    },
    {
      "name": "System",
      "description": "Santé et métadonnées de l'API."
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "schemas": {
      "TuningContent": {
        "type": "object",
        "properties": {
          "summary": {
            "type": "string"
          },
          "themes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "weight": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 5
                }
              },
              "required": [
                "name",
                "weight"
              ]
            }
          },
          "distribution": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "level_calibration": {
            "type": "object",
            "properties": {
              "facile": {
                "type": "string"
              },
              "moyen": {
                "type": "string"
              },
              "difficile": {
                "type": "string"
              },
              "expert": {
                "type": "string"
              }
            },
            "required": [
              "facile",
              "moyen",
              "difficile",
              "expert"
            ]
          },
          "forced_topics": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "forbidden_topics": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "authorized_sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "forbidden_sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "pedagogical_notes": {
            "type": "string"
          }
        },
        "required": [
          "summary",
          "themes",
          "distribution",
          "level_calibration",
          "forced_topics",
          "forbidden_topics",
          "authorized_sources",
          "forbidden_sources",
          "pedagogical_notes"
        ]
      },
      "LeanQuestion": {
        "type": "object",
        "properties": {
          "question": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "answer_index": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "question",
          "choices",
          "answer_index"
        ]
      },
      "ValidatedQuestion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10
          },
          "question": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "answer_index": {
            "type": "integer",
            "minimum": 0
          },
          "explanation": {
            "type": "string"
          },
          "validation": {
            "type": "object",
            "properties": {
              "ok": {
                "type": "boolean"
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "ok",
              "warnings"
            ]
          }
        },
        "required": [
          "id",
          "category",
          "level",
          "question",
          "choices",
          "answer_index",
          "explanation",
          "validation"
        ]
      },
      "Problem": {
        "type": "object",
        "description": "Erreur au format RFC 7807 (Problem Details).",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "type": "string",
            "description": "Code stable, voir la table des erreurs."
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          },
          "errors": {},
          "error": {
            "type": "object",
            "description": "Champ legacy conservé pour rétro-compatibilité.",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {}
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "title",
          "status",
          "code",
          "requestId",
          "error"
        ]
      }
    }
  },
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "System"
        ],
        "summary": "Liveness probe",
        "security": [],
        "responses": {
          "200": {
            "description": "Service en ligne",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    },
                    "uptimeSeconds": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/version": {
      "get": {
        "operationId": "getVersion",
        "tags": [
          "System"
        ],
        "summary": "Version et métadonnées de l'API",
        "security": [],
        "responses": {
          "200": {
            "description": "Version courante",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "reference": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "required": [
                    "name",
                    "version",
                    "openapi"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/questionnaire": {
      "post": {
        "operationId": "getQuestionnaire",
        "tags": [
          "Questionnaires"
        ],
        "summary": "Générer un questionnaire complet",
        "description": "Génère N questions QCM sur un thème en un seul appel IA. Les niveaux sont répartis de manière déterministe entre `minLevel` et `maxLevel`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "theme"
                ],
                "properties": {
                  "theme": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500
                  },
                  "minLevel": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10,
                    "default": 1
                  },
                  "maxLevel": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10,
                    "default": 10
                  },
                  "count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 10
                  },
                  "nbChoices": {
                    "type": "integer",
                    "minimum": 2,
                    "maximum": 10,
                    "default": 4
                  },
                  "tuning": {
                    "$ref": "#/components/schemas/TuningContent"
                  },
                  "avoidQuestions": {
                    "type": "array",
                    "maxItems": 200,
                    "items": {
                      "$ref": "#/components/schemas/LeanQuestion"
                    }
                  }
                }
              },
              "example": {
                "theme": "Astronomie du système solaire",
                "minLevel": 1,
                "maxLevel": 10,
                "count": 10,
                "nbChoices": 4
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Questionnaire généré",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tuning": {
                      "$ref": "#/components/schemas/TuningContent"
                    },
                    "questions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ValidatedQuestion"
                      }
                    }
                  },
                  "required": [
                    "tuning",
                    "questions"
                  ]
                },
                "example": {
                  "tuning": "<TuningContent>",
                  "questions": [
                    {
                      "id": "q1",
                      "category": "planetes",
                      "level": 3,
                      "question": "Quelle planète est la plus proche du Soleil ?",
                      "choices": [
                        "Vénus",
                        "Mars",
                        "Mercure",
                        "Terre"
                      ],
                      "answer_index": 2,
                      "explanation": "Mercure orbite à environ 58 millions de km du Soleil.",
                      "validation": {
                        "ok": true,
                        "warnings": []
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Body invalide",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "401": {
            "description": "Clé API manquante ou invalide",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "402": {
            "description": "Crédits IA épuisés",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "429": {
            "description": "Rate limit IA",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                },
                "description": "Nombre de secondes à attendre avant de réessayer (présent sur 429)."
              }
            }
          },
          "500": {
            "description": "Erreur serveur",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          }
        }
      }
    },
    "/api/question": {
      "post": {
        "operationId": "getQuestion",
        "tags": [
          "Questionnaires"
        ],
        "summary": "Générer une question unique",
        "description": "Génère UNE seule question à un niveau imposé. Équivalent à `getQuestionnaire` avec `count = 1` et `minLevel = maxLevel = level`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "theme",
                  "level"
                ],
                "properties": {
                  "theme": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500
                  },
                  "level": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10
                  },
                  "nbChoices": {
                    "type": "integer",
                    "minimum": 2,
                    "maximum": 10,
                    "default": 4
                  },
                  "tuning": {
                    "$ref": "#/components/schemas/TuningContent"
                  },
                  "avoidQuestions": {
                    "type": "array",
                    "maxItems": 200,
                    "items": {
                      "$ref": "#/components/schemas/LeanQuestion"
                    }
                  }
                }
              },
              "example": {
                "theme": "Astronomie du système solaire",
                "level": 5,
                "nbChoices": 4
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Question générée",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tuning": {
                      "$ref": "#/components/schemas/TuningContent"
                    },
                    "question": {
                      "$ref": "#/components/schemas/ValidatedQuestion"
                    }
                  },
                  "required": [
                    "tuning",
                    "question"
                  ]
                },
                "example": {
                  "tuning": "<TuningContent>",
                  "question": {
                    "id": "q1",
                    "category": "planetes",
                    "level": 5,
                    "question": "Quelle est la plus grande lune de Saturne ?",
                    "choices": [
                      "Europe",
                      "Titan",
                      "Ganymède",
                      "Encelade"
                    ],
                    "answer_index": 1,
                    "explanation": "Titan est la plus grande lune de Saturne et la 2ᵉ du système solaire.",
                    "validation": {
                      "ok": true,
                      "warnings": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Body invalide",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "401": {
            "description": "Clé API manquante ou invalide",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "402": {
            "description": "Crédits IA épuisés",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "429": {
            "description": "Rate limit IA",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                },
                "description": "Nombre de secondes à attendre avant de réessayer (présent sur 429)."
              }
            }
          },
          "500": {
            "description": "Erreur serveur",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          }
        }
      }
    },
    "/api/tuning": {
      "post": {
        "operationId": "generateTuning",
        "tags": [
          "Tuning"
        ],
        "summary": "Générer un TuningContent pour un thème",
        "description": "Génère un `TuningContent` JSON prêt à l'emploi pour un thème donné. Utile pour préparer un tuning, l'éditer, puis le renvoyer à `/api/questionnaire`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "theme"
                ],
                "properties": {
                  "theme": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500
                  },
                  "hints": {
                    "type": "object",
                    "properties": {
                      "sources": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "tone": {
                        "type": "string"
                      },
                      "notes": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "theme": "Astronomie du système solaire",
                "hints": {
                  "sources": [
                    "NASA",
                    "ESA"
                  ],
                  "tone": "vulgarisation"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tuning généré",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tuning": {
                      "$ref": "#/components/schemas/TuningContent"
                    }
                  },
                  "required": [
                    "tuning"
                  ]
                },
                "example": {
                  "tuning": {
                    "summary": "Panorama du système solaire : planètes, lunes, missions.",
                    "themes": [
                      {
                        "name": "Planètes",
                        "weight": 4
                      }
                    ],
                    "distribution": {
                      "planetes": 40,
                      "lunes": 20,
                      "asteroides": 20,
                      "missions": 20
                    },
                    "level_calibration": {
                      "facile": "...",
                      "moyen": "...",
                      "difficile": "...",
                      "expert": "..."
                    },
                    "forced_topics": [],
                    "forbidden_topics": [],
                    "authorized_sources": [
                      "NASA",
                      "ESA"
                    ],
                    "forbidden_sources": [],
                    "pedagogical_notes": "..."
                  }
                }
              }
            }
          },
          "400": {
            "description": "Body invalide",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "401": {
            "description": "Clé API manquante ou invalide",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "402": {
            "description": "Crédits IA épuisés",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          },
          "429": {
            "description": "Rate limit IA",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                },
                "description": "Nombre de secondes à attendre avant de réessayer (présent sur 429)."
              }
            }
          },
          "500": {
            "description": "Erreur serveur",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Identifiant unique de la requête, à transmettre au support en cas de problème."
              }
            }
          }
        }
      }
    }
  }
}