# API Reference

Complete endpoint reference for the PedAI application. All endpoints are prefixed with the application base URL. Unless noted otherwise, authenticated endpoints require a valid JWT token passed via cookie or `Authorization: Bearer <token>` header.

---

## Table of Contents

- [Authentication](#authentication)
- [OIDC / SSO](#oidc--sso)
- [AI Generation](#ai-generation)
- [Transcription](#transcription)
- [Text-to-Speech](#text-to-speech)
- [Models](#models)
- [Encounters](#encounters)
- [Memories](#memories)
- [Audio Backups](#audio-backups)
- [Documents](#documents)
- [User Preferences](#user-preferences)
- [Nextcloud Integration](#nextcloud-integration)
- [Learning Hub (Public)](#learning-hub-public)
- [Learning Hub CMS (Moderator+)](#learning-hub-cms-moderator)
- [Admin - Users](#admin---users)
- [Admin - Configuration](#admin---configuration)
- [Logs](#logs)
- [Milestones (Admin)](#milestones-admin)
- [Health](#health)

---

## Authentication

Base path: `/api/auth/`

### POST /api/auth/register

Register a new user account. The first user to register is automatically assigned the admin role. Requires Cloudflare Turnstile verification. A verification email is sent on success.

- **Auth required:** No
- **Request body:**
  ```json
  {
    "name": "string",
    "email": "string",
    "password": "string",
    "turnstileToken": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "Registration successful. Please check your email to verify your account."
  }
  ```

---

### GET /api/auth/verify-email

Verify a user's email address via the link sent during registration.

- **Auth required:** No
- **Query parameters:**
  | Parameter | Type   | Description                          |
  |-----------|--------|--------------------------------------|
  | `token`   | string | Email verification token from the link |
- **Response:** Redirects to the login page with a success or error message.

---

### POST /api/auth/resend-verification

Resend the email verification link.

- **Auth required:** No
- **Request body:**
  ```json
  {
    "email": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "Verification email sent."
  }
  ```

---

### POST /api/auth/login

Authenticate a user. Supports optional TOTP two-factor authentication. On success, sets an HTTP-only JWT cookie and returns user data.

- **Auth required:** No
- **Request body:**
  ```json
  {
    "email": "string",
    "password": "string",
    "totpCode": "string (optional, required if 2FA is enabled)",
    "turnstileToken": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "token": "jwt-string",
    "user": {
      "id": "number",
      "name": "string",
      "email": "string",
      "role": "admin | moderator | user"
    }
  }
  ```
  If 2FA is enabled but `totpCode` is not provided:
  ```json
  {
    "requires2FA": true
  }
  ```

---

### POST /api/auth/setup-2fa

Generate a TOTP secret and QR code for setting up two-factor authentication.

- **Auth required:** Yes
- **Request body:** None
- **Response:**
  ```json
  {
    "secret": "string",
    "qrCode": "string (data URI)"
  }
  ```

---

### POST /api/auth/verify-2fa

Verify a TOTP code and enable two-factor authentication for the user.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "code": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "2FA enabled successfully."
  }
  ```

---

### POST /api/auth/disable-2fa

Disable two-factor authentication. Requires password confirmation.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "password": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "2FA disabled successfully."
  }
  ```

---

### POST /api/auth/forgot-password

Initiate password reset. Sends a reset link to the user's email.

- **Auth required:** No
- **Request body:**
  ```json
  {
    "email": "string",
    "turnstileToken": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "If an account exists with that email, a reset link has been sent."
  }
  ```

---

### POST /api/auth/reset-password

Reset the user's password using a valid reset token.

- **Auth required:** No
- **Request body:**
  ```json
  {
    "token": "string",
    "password": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "Password reset successfully."
  }
  ```

---

### POST /api/auth/logout

Log out the current user by clearing the JWT cookie.

- **Auth required:** No
- **Request body:** None
- **Response:**
  ```json
  {
    "success": true,
    "message": "Logged out."
  }
  ```

---

### GET /api/auth/me

Retrieve the currently authenticated user's profile.

- **Auth required:** Yes
- **Response:**
  ```json
  {
    "id": "number",
    "name": "string",
    "email": "string",
    "role": "admin | moderator | user",
    "twoFactorEnabled": "boolean"
  }
  ```

---

### GET /api/auth/registration-status

Check whether new user registration is enabled on this instance.

- **Auth required:** No
- **Response:**
  ```json
  {
    "enabled": true
  }
  ```

---

## OIDC / SSO

Base path: `/api/auth/`

### GET /api/auth/oidc-status

Check whether OIDC single sign-on is configured and enabled.

- **Auth required:** No
- **Response:**
  ```json
  {
    "oidcEnabled": true,
    "disableLocalAuth": false,
    "buttonLabel": "Sign in with SSO"
  }
  ```

---

### GET /api/auth/oidc

Initiates the OIDC login flow. Redirects the user to the configured identity provider.

- **Auth required:** No
- **Response:** HTTP 302 redirect to the IdP authorization endpoint.

---

### GET /api/auth/oidc/callback

Handles the callback from the identity provider after authentication. Processes the authorization code and creates/logs in the user.

- **Auth required:** No
- **Query parameters:** Standard OIDC callback parameters (`code`, `state`).
- **Response:** Redirects to the application with a session cookie set.

---

## AI Generation

All generation endpoints require authentication and stream or return AI-generated clinical text. Each accepts a `model` parameter to select the LLM.

### POST /api/generate-hpi-encounter

Generate a History of Present Illness note from a patient encounter transcript.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "transcript": "string",
    "patientAge": "string",
    "patientGender": "string",
    "model": "string",
    "setting": "string",
    "physicianMemories": "string (optional)"
  }
  ```
- **Response:** Streamed text (text/event-stream) or JSON with the generated HPI note.

---

### POST /api/generate-hpi-dictation

Generate an HPI note from a physician's dictated summary (same parameters as encounter).

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "transcript": "string",
    "patientAge": "string",
    "patientGender": "string",
    "model": "string",
    "setting": "string",
    "physicianMemories": "string (optional)"
  }
  ```
- **Response:** Streamed text (text/event-stream) or JSON with the generated HPI note.

---

### POST /api/generate-soap

Generate a SOAP note from a patient encounter transcript.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "transcript": "string",
    "patientAge": "string",
    "patientGender": "string",
    "model": "string",
    "type": "string",
    "additionalInstructions": "string (optional)",
    "physicianMemories": "string (optional)"
  }
  ```
- **Response:** Streamed text (text/event-stream) or JSON with the generated SOAP note.

---

### POST /api/generate-chart-review

Generate a chart review summary from clinical notes.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "notes": "string",
    "visitType": "string",
    "model": "string"
  }
  ```
- **Response:** Streamed text or JSON with the chart review.

---

### POST /api/generate-hospital-course

Generate a hospital course summary from clinical notes.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "notes": "string",
    "patientAge": "string",
    "patientGender": "string",
    "model": "string",
    "format": "string",
    "physicianMemories": "string (optional)"
  }
  ```
- **Response:** Streamed text or JSON with the hospital course summary.

---

### POST /api/well-visit/shadess

Generate a SHADESS (Strengths, Home, Activities, Drugs, Emotions, Sexuality, Safety) assessment from well-visit responses.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "responses": "object",
    "patientAge": "string",
    "model": "string"
  }
  ```
- **Response:** Streamed text or JSON with the SHADESS assessment.

---

### POST /api/generate-sick-visit

Generate a sick visit note from a transcript, incorporating diagnosis information.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "transcript": "string",
    "patientAge": "string",
    "patientGender": "string",
    "model": "string",
    "diagnoses": "string | array",
    "physicianMemories": "string (optional)"
  }
  ```
- **Response:** Streamed text or JSON with the sick visit note.

---

### POST /api/generate-milestone-narrative

Generate a developmental milestone narrative from milestone data.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "milestones": "object | array",
    "patientAge": "string",
    "patientGender": "string",
    "model": "string"
  }
  ```
- **Response:** Streamed text or JSON with the milestone narrative.

---

### POST /api/generate-milestone-summary

Generate a concise summary from a previously generated milestone narrative.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "narrative": "string",
    "model": "string"
  }
  ```
- **Response:** Streamed text or JSON with the milestone summary.

---

### POST /api/refine

Refine existing clinical text according to provided instructions.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "text": "string",
    "instructions": "string",
    "model": "string"
  }
  ```
- **Response:** Streamed text or JSON with the refined text.

---

### POST /api/shorten

Shorten a block of clinical text while preserving key information.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "text": "string",
    "model": "string"
  }
  ```
- **Response:** Streamed text or JSON with the shortened text.

---

### POST /api/clarify

Improve clarity and readability of clinical text.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "text": "string",
    "model": "string"
  }
  ```
- **Response:** Streamed text or JSON with the clarified text.

---

## Transcription

### GET /api/transcribe/status

Check whether speech-to-text transcription is available and which provider is configured.

- **Auth required:** Yes
- **Response:**
  ```json
  {
    "available": true,
    "provider": "whisper | deepgram | browser"
  }
  ```

---

### POST /api/transcribe

Transcribe an audio file to text. Accepts multipart form data with the audio file.

- **Auth required:** Yes
- **Content-Type:** `multipart/form-data`
- **File size limit:** 25 MB
- **Form fields:**
  | Field   | Type | Description        |
  |---------|------|--------------------|
  | `audio` | file | Audio file to transcribe |
- **Response:**
  ```json
  {
    "success": true,
    "text": "string",
    "provider": "string",
    "duration": "number (seconds)"
  }
  ```

---

## Text-to-Speech

### POST /api/text-to-speech

Convert text to speech. Returns an audio stream.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "text": "string",
    "voice": "string (optional)"
  }
  ```
- **Response:** Audio stream (`audio/mpeg`).
- **Response headers:**
  | Header           | Description                        |
  |------------------|------------------------------------|
  | `X-TTS-Provider` | The TTS provider used for synthesis |

---

## Models

### GET /api/models

List available AI models for the current instance.

- **Auth required:** Yes
- **Response:**
  ```json
  {
    "provider": "string",
    "models": [
      {
        "id": "string",
        "name": "string",
        "cost": "string",
        "category": "string"
      }
    ],
    "defaultModel": "string"
  }
  ```

---

## Encounters

### GET /api/encounters/saved

List all saved encounters for the authenticated user.

- **Auth required:** Yes
- **Response:**
  ```json
  [
    {
      "id": "number",
      "label": "string",
      "enc_type": "string",
      "created_at": "string (ISO 8601)",
      "updated_at": "string (ISO 8601)"
    }
  ]
  ```

---

### GET /api/encounters/saved/:id

Retrieve a single saved encounter with full data.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description     |
  |-----------|--------|-----------------|
  | `id`      | number | Encounter ID    |
- **Response:**
  ```json
  {
    "id": "number",
    "label": "string",
    "enc_type": "string",
    "transcript": "string",
    "generated_note": "string",
    "partial_data": "object | null",
    "created_at": "string (ISO 8601)",
    "updated_at": "string (ISO 8601)"
  }
  ```

---

### POST /api/encounters/saved

Create or update a saved encounter. Uses `idempotency_key` to prevent duplicates.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "label": "string",
    "enc_type": "string",
    "transcript": "string",
    "generated_note": "string",
    "partial_data": "object (optional)",
    "idempotency_key": "string (optional)"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "id": "number"
  }
  ```

---

### DELETE /api/encounters/saved/:id

Delete a saved encounter.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description     |
  |-----------|--------|-----------------|
  | `id`      | number | Encounter ID    |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

## Memories

Physician memories are reusable context snippets (preferences, style corrections, common instructions) injected into AI generation prompts.

### GET /api/memories

List all memories for the authenticated user. Returns up to 200 entries.

- **Auth required:** Yes
- **Response:**
  ```json
  [
    {
      "id": "number",
      "category": "string",
      "name": "string",
      "content": "string",
      "created_at": "string (ISO 8601)"
    }
  ]
  ```

---

### POST /api/memories

Create a new memory.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "category": "string",
    "name": "string",
    "content": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "id": "number"
  }
  ```

---

### PUT /api/memories/:id

Update an existing memory.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description |
  |-----------|--------|-------------|
  | `id`      | number | Memory ID   |
- **Request body:**
  ```json
  {
    "category": "string",
    "name": "string",
    "content": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### DELETE /api/memories/:id

Delete a memory.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description |
  |-----------|--------|-------------|
  | `id`      | number | Memory ID   |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### GET /api/memories/context

Retrieve all memories formatted for injection into AI generation prompts.

- **Auth required:** Yes
- **Response:**
  ```json
  {
    "context": "string"
  }
  ```

---

### POST /api/memories/correction

Automatically save a style correction as a memory. Used when the physician corrects AI output.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "section": "string",
    "original_snippet": "string",
    "corrected_snippet": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "id": "number"
  }
  ```

---

## Audio Backups

Temporary audio backup storage with automatic 24-hour expiry.

### POST /api/audio-backups

Upload an audio backup. The file is gzip-compressed on the server.

- **Auth required:** Yes
- **Content-Type:** `multipart/form-data`
- **Form fields:**
  | Field   | Type | Description            |
  |---------|------|------------------------|
  | `audio` | file | Audio file to back up  |
- **Response:**
  ```json
  {
    "success": true,
    "id": "number"
  }
  ```

---

### GET /api/audio-backups

List all audio backups for the authenticated user.

- **Auth required:** Yes
- **Response:**
  ```json
  [
    {
      "id": "number",
      "filename": "string",
      "created_at": "string (ISO 8601)",
      "expires_at": "string (ISO 8601)"
    }
  ]
  ```

---

### GET /api/audio-backups/:id/audio

Download a decompressed audio backup.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description       |
  |-----------|--------|-------------------|
  | `id`      | number | Audio backup ID   |
- **Response:** Binary audio stream with appropriate content-type header.

---

### DELETE /api/audio-backups/:id

Delete an audio backup.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description       |
  |-----------|--------|-------------------|
  | `id`      | number | Audio backup ID   |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

## Documents

S3-backed document storage for clinical files.

### GET /api/documents

List all documents for the authenticated user.

- **Auth required:** Yes
- **Response:**
  ```json
  [
    {
      "id": "number",
      "filename": "string",
      "mimetype": "string",
      "size": "number",
      "created_at": "string (ISO 8601)"
    }
  ]
  ```

---

### POST /api/documents/upload

Upload a document to S3 storage.

- **Auth required:** Yes
- **Content-Type:** `multipart/form-data`
- **File size limit:** 10 MB
- **Allowed types:** PDF, images (JPEG, PNG, GIF, WebP), Word documents (.doc, .docx), plain text (.txt), CSV (.csv)
- **Form fields:**
  | Field  | Type | Description         |
  |--------|------|---------------------|
  | `file` | file | Document to upload  |
- **Response:**
  ```json
  {
    "success": true,
    "id": "number",
    "filename": "string"
  }
  ```

---

### GET /api/documents/:id/download

Download a document.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description   |
  |-----------|--------|---------------|
  | `id`      | number | Document ID   |
- **Response:** Binary file stream with appropriate content-type and content-disposition headers.

---

### DELETE /api/documents/:id

Delete a document from S3 storage.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description   |
  |-----------|--------|---------------|
  | `id`      | number | Document ID   |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

## User Preferences

### GET /api/user/preferences

Get the authenticated user's preferences.

- **Auth required:** Yes
- **Response:**
  ```json
  {
    "stt_model": "string",
    "tts_voice": "string"
  }
  ```

---

### POST /api/user/preferences

Save user preferences.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "stt_model": "string",
    "tts_voice": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### GET /api/user/preferences/options

List available STT models and TTS voices that the user can choose from.

- **Auth required:** Yes
- **Response:**
  ```json
  {
    "sttModels": [
      { "id": "string", "name": "string" }
    ],
    "ttsVoices": [
      { "id": "string", "name": "string" }
    ]
  }
  ```

---

### POST /api/user/webdav-path

Save the user's preferred WebDAV learning content path.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "path": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

## Nextcloud Integration

### POST /api/nextcloud/connect

Connect the user's account to a Nextcloud instance via WebDAV.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "url": "string",
    "username": "string",
    "password": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "Connected to Nextcloud."
  }
  ```

---

### POST /api/nextcloud/export

Export text content to the connected Nextcloud instance.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "text": "string",
    "title": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "path": "string"
  }
  ```

---

### POST /api/nextcloud/disconnect

Disconnect the user's Nextcloud integration.

- **Auth required:** Yes
- **Request body:** None
- **Response:**
  ```json
  {
    "success": true,
    "message": "Disconnected from Nextcloud."
  }
  ```

---

## Learning Hub (Public)

Public-facing learning content endpoints. Authentication is required to track progress and submit quizzes.

### GET /api/learning/categories

List all learning content categories.

- **Auth required:** Yes
- **Response:**
  ```json
  [
    {
      "id": "number",
      "name": "string",
      "slug": "string",
      "description": "string"
    }
  ]
  ```

---

### GET /api/learning/feed

Get a paginated feed of learning content.

- **Auth required:** Yes
- **Query parameters:**
  | Parameter | Type   | Default | Description           |
  |-----------|--------|---------|-----------------------|
  | `limit`   | number | 20      | Items per page        |
  | `offset`  | number | 0       | Pagination offset     |
- **Response:**
  ```json
  [
    {
      "id": "number",
      "title": "string",
      "slug": "string",
      "summary": "string",
      "category": "string",
      "created_at": "string (ISO 8601)"
    }
  ]
  ```

---

### GET /api/learning/category/:slug

Get all learning content within a specific category.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description    |
  |-----------|--------|----------------|
  | `slug`    | string | Category slug  |
- **Response:** Array of content items in the category.

---

### GET /api/learning/content/:slug

Get a single piece of learning content, including quiz questions, answer options, and the user's progress.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description   |
  |-----------|--------|---------------|
  | `slug`    | string | Content slug  |
- **Response:**
  ```json
  {
    "id": "number",
    "title": "string",
    "slug": "string",
    "body": "string (markdown)",
    "category": "object",
    "questions": [
      {
        "id": "number",
        "text": "string",
        "options": [
          {
            "id": "number",
            "text": "string"
          }
        ]
      }
    ],
    "progress": {
      "completed": "boolean",
      "score": "number | null"
    }
  }
  ```

---

### GET /api/learning/content/:slug/slides

Render Marp-formatted markdown content as HTML presentation slides.

- **Auth required:** Yes
- **Path parameters:**
  | Parameter | Type   | Description   |
  |-----------|--------|---------------|
  | `slug`    | string | Content slug  |
- **Response:** HTML presentation content.

---

### POST /api/learning/submit-quiz

Submit quiz answers for a piece of learning content.

- **Auth required:** Yes
- **Request body:**
  ```json
  {
    "contentId": "number",
    "answers": [
      {
        "questionId": "number",
        "optionIds": ["number"]
      }
    ]
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "score": "number",
    "total": "number",
    "results": [
      {
        "questionId": "number",
        "correct": "boolean"
      }
    ]
  }
  ```

---

### GET /api/learning/search

Keyword-based search across learning content.

- **Auth required:** Yes
- **Query parameters:**
  | Parameter | Type   | Description     |
  |-----------|--------|-----------------|
  | `q`       | string | Search query    |
- **Response:** Array of matching content items.

---

### GET /api/learning/search/semantic

Semantic (vector-based) search across learning content using embeddings.

- **Auth required:** Yes
- **Query parameters:**
  | Parameter | Type   | Description     |
  |-----------|--------|-----------------|
  | `q`       | string | Search query    |
- **Response:** Array of matching content items ranked by semantic similarity.

---

### GET /api/learning/search/hybrid

Combined keyword and semantic search for best-of-both-worlds results.

- **Auth required:** Yes
- **Query parameters:**
  | Parameter | Type   | Description     |
  |-----------|--------|-----------------|
  | `q`       | string | Search query    |
- **Response:** Array of matching content items with combined ranking.

---

## Learning Hub CMS (Moderator+)

Content management endpoints for learning content. Requires moderator or admin role.

### GET /api/admin/learning/categories

List all learning categories (admin view).

- **Auth required:** Yes (moderator+)
- **Response:** Array of category objects.

---

### POST /api/admin/learning/categories

Create a new learning category.

- **Auth required:** Yes (moderator+)
- **Request body:**
  ```json
  {
    "name": "string",
    "slug": "string",
    "description": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "id": "number"
  }
  ```

---

### PUT /api/admin/learning/categories/:id

Update a learning category.

- **Auth required:** Yes (moderator+)
- **Path parameters:**
  | Parameter | Type   | Description   |
  |-----------|--------|---------------|
  | `id`      | number | Category ID   |
- **Request body:**
  ```json
  {
    "name": "string",
    "slug": "string",
    "description": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### DELETE /api/admin/learning/categories/:id

Delete a learning category.

- **Auth required:** Yes (moderator+)
- **Path parameters:**
  | Parameter | Type   | Description   |
  |-----------|--------|---------------|
  | `id`      | number | Category ID   |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### GET /api/admin/learning/content

List all learning content (admin view, includes unpublished).

- **Auth required:** Yes (moderator+)
- **Response:** Array of content objects.

---

### POST /api/admin/learning/content

Create new learning content.

- **Auth required:** Yes (moderator+)
- **Request body:**
  ```json
  {
    "title": "string",
    "slug": "string",
    "body": "string (markdown)",
    "categoryId": "number",
    "questions": "array (optional)"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "id": "number"
  }
  ```

---

### GET /api/admin/learning/content/:id

Get a single content item for editing.

- **Auth required:** Yes (moderator+)
- **Path parameters:**
  | Parameter | Type   | Description  |
  |-----------|--------|--------------|
  | `id`      | number | Content ID   |
- **Response:** Full content object with questions and metadata.

---

### PUT /api/admin/learning/content/:id

Update existing learning content.

- **Auth required:** Yes (moderator+)
- **Path parameters:**
  | Parameter | Type   | Description  |
  |-----------|--------|--------------|
  | `id`      | number | Content ID   |
- **Request body:**
  ```json
  {
    "title": "string",
    "slug": "string",
    "body": "string (markdown)",
    "categoryId": "number",
    "questions": "array (optional)"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### DELETE /api/admin/learning/content/:id

Delete learning content.

- **Auth required:** Yes (moderator+)
- **Path parameters:**
  | Parameter | Type   | Description  |
  |-----------|--------|--------------|
  | `id`      | number | Content ID   |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/learning/ai-generate

Generate learning content or presentations using AI. Accepts either multipart form data (with file uploads) or JSON.

- **Auth required:** Yes (moderator+)
- **Content-Type:** `multipart/form-data` or `application/json`
- **Request body (JSON):**
  ```json
  {
    "topic": "string",
    "type": "article | presentation",
    "model": "string"
  }
  ```
- **Request body (multipart):** Same fields plus uploaded reference files.
- **Response:**
  ```json
  {
    "success": true,
    "content": "string (markdown)"
  }
  ```

---

### POST /api/admin/learning/ai-refine

Refine learning content body text using AI.

- **Auth required:** Yes (moderator+)
- **Request body:**
  ```json
  {
    "body": "string",
    "instructions": "string",
    "model": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "content": "string"
  }
  ```

---

### POST /api/admin/learning/preview-slides

Preview Marp-formatted markdown as rendered presentation slides.

- **Auth required:** Yes (moderator+)
- **Request body:**
  ```json
  {
    "markdown": "string"
  }
  ```
- **Response:**
  ```json
  {
    "css": "string",
    "slides": ["string (HTML)"]
  }
  ```

---

### POST /api/admin/learning/generate-pptx

Generate a PowerPoint file from Marp markdown.

- **Auth required:** Yes (moderator+)
- **Request body:**
  ```json
  {
    "markdown": "string",
    "title": "string"
  }
  ```
- **Response:** Binary `.pptx` file download.

---

### GET /api/admin/learning/webdav-browse

Browse files on the connected WebDAV/Nextcloud server.

- **Auth required:** Yes (moderator+)
- **Query parameters:**
  | Parameter | Type   | Description                  |
  |-----------|--------|------------------------------|
  | `path`    | string | Directory path to browse     |
- **Response:** Array of file/directory entries.

---

### GET /api/admin/learning/stats

Get learning hub statistics (content counts, quiz completion rates, etc.).

- **Auth required:** Yes (moderator+)
- **Response:**
  ```json
  {
    "totalContent": "number",
    "totalCategories": "number",
    "totalQuizSubmissions": "number",
    "averageScore": "number"
  }
  ```

---

## Admin - Users

Requires admin role.

### GET /api/admin/users

List all registered users.

- **Auth required:** Yes (admin)
- **Response:**
  ```json
  [
    {
      "id": "number",
      "name": "string",
      "email": "string",
      "role": "string",
      "verified": "boolean",
      "disabled": "boolean",
      "created_at": "string (ISO 8601)"
    }
  ]
  ```

---

### GET /api/admin/users/:id

Get detailed information and usage statistics for a specific user.

- **Auth required:** Yes (admin)
- **Path parameters:**
  | Parameter | Type   | Description |
  |-----------|--------|-------------|
  | `id`      | number | User ID     |
- **Response:**
  ```json
  {
    "id": "number",
    "name": "string",
    "email": "string",
    "role": "string",
    "verified": "boolean",
    "disabled": "boolean",
    "stats": {
      "totalGenerations": "number",
      "totalTranscriptions": "number",
      "lastActive": "string (ISO 8601)"
    }
  }
  ```

---

### POST /api/admin/users/:id/verify

Manually verify a user's email address (bypass email verification).

- **Auth required:** Yes (admin)
- **Path parameters:**
  | Parameter | Type   | Description |
  |-----------|--------|-------------|
  | `id`      | number | User ID     |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/users/:id/disable

Disable a user account, preventing login.

- **Auth required:** Yes (admin)
- **Path parameters:**
  | Parameter | Type   | Description |
  |-----------|--------|-------------|
  | `id`      | number | User ID     |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/users/:id/enable

Re-enable a previously disabled user account.

- **Auth required:** Yes (admin)
- **Path parameters:**
  | Parameter | Type   | Description |
  |-----------|--------|-------------|
  | `id`      | number | User ID     |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

## Admin - Configuration

Requires admin role unless noted otherwise.

### GET /api/admin/config/announcement

Get the current announcement banner. This endpoint is public and does not require authentication.

- **Auth required:** No
- **Response:**
  ```json
  {
    "announcement": "string | null"
  }
  ```

---

### GET /api/admin/config

Get all application configuration settings.

- **Auth required:** Yes (admin)
- **Response:** Object containing all configuration key-value pairs.

---

### POST /api/admin/config

Update application configuration settings.

- **Auth required:** Yes (admin)
- **Request body:** Object with configuration key-value pairs to update.
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/config/test-email

Send a test email to verify SMTP configuration.

- **Auth required:** Yes (admin)
- **Request body:**
  ```json
  {
    "to": "string (email address)"
  }
  ```
- **Response:**
  ```json
  {
    "success": true,
    "message": "Test email sent."
  }
  ```

---

### GET /api/admin/config/prompts

Get all AI prompt templates, including any admin overrides.

- **Auth required:** Yes (admin)
- **Response:** Object mapping prompt keys to their current values.

---

### POST /api/admin/config/prompts

Save AI prompt template overrides.

- **Auth required:** Yes (admin)
- **Request body:** Object mapping prompt keys to new values.
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/config/prompts/:key/reset

Reset a specific AI prompt template back to its default value.

- **Auth required:** Yes (admin)
- **Path parameters:**
  | Parameter | Type   | Description         |
  |-----------|--------|---------------------|
  | `key`     | string | Prompt template key |
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/config/reset-defaults

Reset all configuration settings to their default values.

- **Auth required:** Yes (admin)
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### GET /api/admin/config/smtp/status

Check the current SMTP configuration status.

- **Auth required:** Yes (admin)
- **Response:**
  ```json
  {
    "configured": "boolean",
    "host": "string",
    "port": "number"
  }
  ```

---

### POST /api/admin/config/smtp/update

Update SMTP email configuration.

- **Auth required:** Yes (admin)
- **Request body:**
  ```json
  {
    "host": "string",
    "port": "number",
    "username": "string",
    "password": "string",
    "from": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/config/registration

Update registration settings (enable/disable new registrations).

- **Auth required:** Yes (admin)
- **Request body:**
  ```json
  {
    "enabled": "boolean"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### GET /api/admin/config/oidc

Get the current OIDC/SSO configuration.

- **Auth required:** Yes (admin)
- **Response:**
  ```json
  {
    "enabled": "boolean",
    "clientId": "string",
    "issuerUrl": "string",
    "buttonLabel": "string",
    "disableLocalAuth": "boolean"
  }
  ```

---

### POST /api/admin/config/oidc

Update the OIDC/SSO configuration.

- **Auth required:** Yes (admin)
- **Request body:**
  ```json
  {
    "enabled": "boolean",
    "clientId": "string",
    "clientSecret": "string",
    "issuerUrl": "string",
    "buttonLabel": "string",
    "disableLocalAuth": "boolean"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### GET /api/admin/config/models

List all AI models with their enabled/disabled status.

- **Auth required:** Yes (admin)
- **Response:**
  ```json
  [
    {
      "id": "string",
      "name": "string",
      "enabled": "boolean",
      "isDefault": "boolean",
      "category": "string"
    }
  ]
  ```

---

### PUT /api/admin/config/models/toggle

Enable or disable a specific AI model.

- **Auth required:** Yes (admin)
- **Request body:**
  ```json
  {
    "modelId": "string",
    "enabled": "boolean"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### PUT /api/admin/config/models/default

Set the default AI model for all users.

- **Auth required:** Yes (admin)
- **Request body:**
  ```json
  {
    "modelId": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### POST /api/admin/config/models/custom

Add a custom AI model definition.

- **Auth required:** Yes (admin)
- **Request body:**
  ```json
  {
    "id": "string",
    "name": "string",
    "category": "string"
  }
  ```
- **Response:**
  ```json
  {
    "success": true
  }
  ```

---

### GET /api/admin/config/models/discover

Discover available models from the configured AI provider.

- **Auth required:** Yes (admin)
- **Response:**
  ```json
  {
    "models": [
      {
        "id": "string",
        "name": "string"
      }
    ]
  }
  ```

---

## Logs

Requires admin role.

### GET /api/logs/usage

Get a 30-day usage summary broken down by model and endpoint.

- **Auth required:** Yes (admin)
- **Response:**
  ```json
  {
    "byModel": [
      {
        "model": "string",
        "count": "number",
        "tokens": "number"
      }
    ],
    "byEndpoint": [
      {
        "endpoint": "string",
        "count": "number"
      }
    ]
  }
  ```

---

### GET /api/logs/audit

Get the audit log of user actions (login, settings changes, etc.).

- **Auth required:** Yes (admin)
- **Response:** Array of audit log entries with timestamps, user IDs, and action descriptions.

---

### GET /api/logs/api

Get API call metrics (request counts, latency, error rates).

- **Auth required:** Yes (admin)
- **Response:** Array of API metric entries.

---

### GET /api/logs/access

Get user access history (login times, IP addresses).

- **Auth required:** Yes (admin)
- **Response:** Array of access log entries.

---

## Milestones (Admin)

Developmental milestones management. Requires admin role.

### GET /api/admin/milestones

List all developmental milestones in the database.

- **Auth required:** Yes (admin)
- **Response:** Array of milestone objects organized by age group and domain.

---

### POST /api/admin/milestones/seed

Seed the database with the default set of developmental milestones.

- **Auth required:** Yes (admin)
- **Request body:** None
- **Response:**
  ```json
  {
    "success": true,
    "count": "number"
  }
  ```

---

## Health

### GET /api/health

Application health check endpoint.

- **Auth required:** No
- **Response:**
  ```json
  {
    "status": "ok",
    "provider": "string",
    "uptime": "number (seconds)"
  }
  ```
