openapi: 3.0.3
info:
  title: Lerniva API
  version: 1.5.0
  description: REST API for Lerniva mobile, desktop, admin, and partner integrations.
servers:
  - url: https://lerniva.org/api/v1
    description: Production API
security:
  - bearerAuth: []
tags:
  - name: Auth
  - name: Users
  - name: Feed
  - name: Videos
  - name: Notes
  - name: Groups
  - name: Discussions
  - name: Quizzes
  - name: Assessments
  - name: SafeSpaces
  - name: Facilitator
  - name: Notifications
paths:
  /auth/login:
    post:
      tags: [Auth]
      summary: Login
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email: { type: string, format: email }
                password: { type: string, format: password }
      responses:
        '200': { description: Authenticated }
  /auth/register:
    post:
      tags: [Auth]
      summary: Register a new user
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, email, password, role]
              properties:
                name: { type: string }
                email: { type: string, format: email }
                password: { type: string, format: password }
                role: { type: string, enum: [student, educator, mentor, facilitator] }
      responses:
        '201': { description: Registered }
  /auth/request-password-reset:
    post:
      tags: [Auth]
      summary: Request a password reset OTP or token
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email: { type: string, format: email }
      responses:
        '200': { description: Reset initiated }
  /auth/reset-password:
    post:
      tags: [Auth]
      summary: Reset password using reset token or OTP flow
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password, token]
              properties:
                email: { type: string, format: email }
                password: { type: string, format: password }
                token: { type: string }
      responses:
        '200': { description: Password reset }
  /auth/session:
    get:
      tags: [Auth]
      summary: Get current authenticated session context
      responses:
        '200': { description: Session payload }
  /auth/refresh:
    post:
      tags: [Auth]
      summary: Refresh the current session
      responses:
        '200': { description: Session refreshed }
  /auth/change-password:
    post:
      tags: [Auth]
      summary: Change password for the authenticated user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [current_password, password, password_confirmation]
              properties:
                current_password: { type: string, format: password }
                password: { type: string, format: password }
                password_confirmation: { type: string, format: password }
      responses:
        '200': { description: Password changed }
  /auth/sessions:
    get:
      tags: [Auth]
      summary: List active sessions for the user
      responses:
        '200': { description: Sessions listed }
    delete:
      tags: [Auth]
      summary: Revoke all other sessions
      responses:
        '200': { description: Sessions revoked }
  /auth/sessions/{tokenId}:
    delete:
      tags: [Auth]
      summary: Revoke a single session token
      parameters:
        - in: path
          name: tokenId
          required: true
          schema: { type: string }
      responses:
        '200': { description: Session revoked }
  /users/profile:
    get:
      tags: [Users]
      summary: Get the authenticated user profile
      responses:
        '200': { description: Profile payload }
  /users/profile/update:
    post:
      tags: [Users]
      summary: Update the authenticated user profile
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ProfileUpdateRequest'
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileUpdateRequest'
      responses:
        '200': { description: Profile updated }
  /users/yearbook:
    get:
      tags: [Users]
      summary: List yearbook matches and school-based discovery
      responses:
        '200': { description: Yearbook payload }
  /users/yearbook/detail:
    get:
      tags: [Users]
      summary: Detailed yearbook view with schools, peers, and mentors
      responses:
        '200': { description: Yearbook detail }
  /feed/home:
    get:
      tags: [Feed]
      summary: Home feed across posts, notes, groups, and videos
      responses:
        '200': { description: Feed payload }
  /videos/providers:
    get:
      tags: [Videos]
      summary: List supported video providers
      responses:
        '200': { description: Providers list }
  /videos/analytics:
    get:
      tags: [Videos]
      summary: Creator analytics summary for the authenticated user
      responses:
        '200': { description: Analytics summary }
  /videos:
    get:
      tags: [Videos]
      summary: List videos
      parameters:
        - in: query
          name: mine
          schema: { type: boolean }
        - in: query
          name: video_type
          schema: { type: string }
      responses:
        '200': { description: Paginated videos }
    post:
      tags: [Videos]
      summary: Create video by external link or file upload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoCreateRequest'
          multipart/form-data:
            schema:
              type: object
              required: [title, source_type, video_file]
              properties:
                title: { type: string }
                description: { type: string }
                source_type: { type: string, enum: [upload] }
                video_type: { type: string, enum: [upload] }
                video_file: { type: string, format: binary }
                language: { type: string }
                is_public: { type: boolean }
      responses:
        '201': { description: Video submitted }
  /videos/{video}:
    get:
      tags: [Videos]
      summary: Video detail
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200': { description: Video detail }
    put:
      tags: [Videos]
      summary: Update a video
      parameters:
        - $ref: '#/components/parameters/VideoId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoUpdateRequest'
      responses:
        '200': { description: Updated }
    delete:
      tags: [Videos]
      summary: Delete a video
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200': { description: Deleted }
  /videos/{video}/analytics:
    get:
      tags: [Videos]
      summary: Per-video analytics for the creator
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200': { description: Video analytics detail }
  /videos/{video}/processing:
    get:
      tags: [Videos]
      summary: Video processing and moderation status
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200': { description: Video processing state }
  /videos/{video}/transcript:
    get:
      tags: [Videos]
      summary: Get transcript and segments
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200': { description: Transcript payload }
  /videos/{video}/transcript/generate:
    post:
      tags: [Videos]
      summary: Queue transcript generation
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '202': { description: Queued }
  /videos/{video}/subtitles:
    get:
      tags: [Videos]
      summary: List subtitle tracks
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200': { description: Subtitle tracks }
  /videos/{video}/subtitles/generate:
    post:
      tags: [Videos]
      summary: Generate subtitle track
      parameters:
        - $ref: '#/components/parameters/VideoId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                language: { type: string, example: en }
      responses:
        '202': { description: Queued }
  /videos/{video}/chapters/generate:
    post:
      tags: [Videos]
      summary: Generate chapter markers
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '202': { description: Queued }
  /videos/{video}/comments:
    post:
      tags: [Videos]
      summary: Add a comment to a video
      parameters:
        - $ref: '#/components/parameters/VideoId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [body]
              properties:
                body: { type: string }
                parent_id: { type: integer, nullable: true }
      responses:
        '201': { description: Comment created }
  /videos/{video}/like:
    post:
      tags: [Videos]
      summary: Toggle like on a video
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200': { description: Like toggled }
  /videos/{video}/share:
    post:
      tags: [Videos]
      summary: Register a share action for a video
      parameters:
        - $ref: '#/components/parameters/VideoId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                platform: { type: string, example: native }
      responses:
        '200': { description: Share recorded }
  /videos/{video}/report:
    post:
      tags: [Videos]
      summary: Report a video for moderation review
      parameters:
        - $ref: '#/components/parameters/VideoId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason: { type: string }
                details: { type: string }
      responses:
        '201': { description: Report submitted }
  /notes:
    get:
      tags: [Notes]
      summary: List notes
      responses:
        '200': { description: Notes listed }
    post:
      tags: [Notes]
      summary: Create a note
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NoteRequest'
      responses:
        '201': { description: Note created }
  /notes/folders/all:
    get:
      tags: [Notes]
      summary: List note folders
      responses:
        '200': { description: Folders listed }
  /notes/folders:
    post:
      tags: [Notes]
      summary: Create a note folder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
      responses:
        '201': { description: Folder created }
  /notes/{note}:
    get:
      tags: [Notes]
      summary: Get note detail
      parameters:
        - in: path
          name: note
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Note detail }
    put:
      tags: [Notes]
      summary: Update a note
      parameters:
        - in: path
          name: note
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NoteRequest'
      responses:
        '200': { description: Note updated }
    delete:
      tags: [Notes]
      summary: Delete a note
      parameters:
        - in: path
          name: note
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Note deleted }
  /notes/{note}/comments:
    post:
      tags: [Notes]
      summary: Add a comment to a note
      parameters:
        - in: path
          name: note
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [body]
              properties:
                body: { type: string }
      responses:
        '201': { description: Comment created }
  /notes/{note}/attachments:
    get:
      tags: [Notes]
      summary: List note attachments
      parameters:
        - in: path
          name: note
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Attachments listed }
    post:
      tags: [Notes]
      summary: Upload note attachment
      parameters:
        - in: path
          name: note
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file: { type: string, format: binary }
      responses:
        '201': { description: Attachment uploaded }
  /note-attachments/{attachment}:
    get:
      tags: [Notes]
      summary: Get note attachment detail
      parameters:
        - in: path
          name: attachment
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Attachment detail }
  /note-attachments/{attachment}/ocr:
    post:
      tags: [Notes]
      summary: Run OCR on a note attachment
      parameters:
        - in: path
          name: attachment
          required: true
          schema: { type: integer }
      responses:
        '202': { description: OCR queued }
  /study-packs:
    get:
      tags: [Notes]
      summary: List study packs
      responses:
        '200': { description: Study packs listed }
    post:
      tags: [Notes]
      summary: Create a study pack
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title]
              properties:
                title: { type: string }
                description: { type: string }
      responses:
        '201': { description: Study pack created }
  /groups:
    get:
      tags: [Groups]
      summary: List study groups
      responses:
        '200': { description: Groups listed }
    post:
      tags: [Groups]
      summary: Create a study group
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                description: { type: string }
                is_public: { type: boolean }
      responses:
        '201': { description: Group created }
  /groups/{group}:
    get:
      tags: [Groups]
      summary: Get group detail with recent activity
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Group detail }
  /groups/{group}/join:
    post:
      tags: [Groups]
      summary: Join a group
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Joined }
  /groups/{group}/leave:
    delete:
      tags: [Groups]
      summary: Leave a group
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Left }
  /groups/{group}/messages:
    get:
      tags: [Groups]
      summary: List group messages
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Messages listed }
    post:
      tags: [Groups]
      summary: Send a group message
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [body]
              properties:
                body: { type: string }
      responses:
        '201': { description: Message sent }
  /groups/{group}/typing:
    post:
      tags: [Groups]
      summary: Update typing status for a group
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                is_typing: { type: boolean }
      responses:
        '200': { description: Typing status updated }
  /groups/{group}/messages/{message}/read:
    post:
      tags: [Groups]
      summary: Mark a group message as read
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
        - in: path
          name: message
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Message marked read }
  /groups/{group}/sessions:
    get:
      tags: [Groups]
      summary: List group study sessions
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Sessions listed }
    post:
      tags: [Groups]
      summary: Create a group study session
      parameters:
        - in: path
          name: group
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title]
              properties:
                title: { type: string }
                starts_at: { type: string, format: date-time }
                ends_at: { type: string, format: date-time }
      responses:
        '201': { description: Study session created }
  /discussions:
    get:
      tags: [Discussions]
      summary: List discussion threads
      responses:
        '200': { description: Discussions listed }
    post:
      tags: [Discussions]
      summary: Create a discussion thread
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, body]
              properties:
                title: { type: string }
                body: { type: string }
      responses:
        '201': { description: Discussion created }
  /discussions/{discussion}:
    get:
      tags: [Discussions]
      summary: Get discussion detail
      parameters:
        - in: path
          name: discussion
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Discussion detail }
  /discussions/{discussion}/replies:
    post:
      tags: [Discussions]
      summary: Reply to a discussion
      parameters:
        - in: path
          name: discussion
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [body]
              properties:
                body: { type: string }
      responses:
        '201': { description: Reply created }
  /assessments:
    get:
      tags: [Assessments]
      summary: List assessments
      responses:
        '200': { description: Assessments listed }
    post:
      tags: [Assessments]
      summary: Create assessment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssessmentRequest'
      responses:
        '201': { description: Assessment created }
  /assessments/{assessment}:
    get:
      tags: [Assessments]
      summary: Get assessment detail
      parameters:
        - in: path
          name: assessment
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Assessment detail }
    put:
      tags: [Assessments]
      summary: Update assessment
      parameters:
        - in: path
          name: assessment
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssessmentRequest'
      responses:
        '200': { description: Assessment updated }
    delete:
      tags: [Assessments]
      summary: Delete assessment
      parameters:
        - in: path
          name: assessment
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Assessment deleted }
  /assessments/{assessment}/submit:
    post:
      tags: [Assessments]
      summary: Submit assessment answers
      parameters:
        - in: path
          name: assessment
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [answers]
              properties:
                answers:
                  type: array
                  items:
                    type: object
                    properties:
                      question_id: { type: integer }
                      answer: { type: string }
      responses:
        '200': { description: Assessment submitted }
  /safe-spaces:
    get:
      tags: [SafeSpaces]
      summary: List safe spaces
      responses:
        '200': { description: Safe spaces listed }
    post:
      tags: [SafeSpaces]
      summary: Create a safe space
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                description: { type: string }
                approval_required: { type: boolean }
      responses:
        '201': { description: Safe space created }
  /safe-spaces/{safeSpace}:
    get:
      tags: [SafeSpaces]
      summary: Get safe space detail
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Safe space detail }
    put:
      tags: [SafeSpaces]
      summary: Update a safe space
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string }
                description: { type: string }
                approval_required: { type: boolean }
      responses:
        '200': { description: Safe space updated }
  /safe-spaces/{safeSpace}/moderation:
    get:
      tags: [SafeSpaces]
      summary: Moderation overview for a safe space
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Moderation overview }
  /safe-spaces/{safeSpace}/members/{membership}/approve:
    post:
      tags: [SafeSpaces]
      summary: Approve a pending safe-space membership
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
        - in: path
          name: membership
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Membership approved }
  /safe-spaces/{safeSpace}/members/{membership}/reject:
    post:
      tags: [SafeSpaces]
      summary: Reject a pending safe-space membership
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
        - in: path
          name: membership
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Membership rejected }
  /safe-spaces/{safeSpace}/members/{membership}:
    delete:
      tags: [SafeSpaces]
      summary: Remove a safe-space member
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
        - in: path
          name: membership
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Member removed }
  /safe-spaces/{safeSpace}/report:
    post:
      tags: [SafeSpaces]
      summary: Report a safe-space issue
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason: { type: string }
                details: { type: string }
      responses:
        '201': { description: Report submitted }
  /safe-spaces/{safeSpace}/join:
    post:
      tags: [SafeSpaces]
      summary: Join a safe space
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Join request accepted or submitted }
  /safe-spaces/{safeSpace}/leave:
    delete:
      tags: [SafeSpaces]
      summary: Leave a safe space
      parameters:
        - in: path
          name: safeSpace
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Left safe space }
  /facilitator/dashboard:
    get:
      tags: [Facilitator]
      summary: Facilitator dashboard summary
      responses:
        '200': { description: Dashboard summary }
  /facilitator/cohorts:
    get:
      tags: [Facilitator]
      summary: List facilitator cohorts
      responses:
        '200': { description: Cohorts listed }
    post:
      tags: [Facilitator]
      summary: Create a cohort
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                school_id: { type: string }
                target_group: { type: string }
                status: { type: string }
      responses:
        '201': { description: Cohort created }
  /facilitator/cohorts/{cohort}:
    get:
      tags: [Facilitator]
      summary: Get cohort detail
      parameters:
        - in: path
          name: cohort
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Cohort detail }
    put:
      tags: [Facilitator]
      summary: Update cohort
      parameters:
        - in: path
          name: cohort
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string }
                school_id: { type: string }
                target_group: { type: string }
                status: { type: string }
      responses:
        '200': { description: Cohort updated }
    delete:
      tags: [Facilitator]
      summary: Delete cohort
      parameters:
        - in: path
          name: cohort
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Cohort deleted }
  /facilitator/users:
    get:
      tags: [Facilitator]
      summary: List facilitators/users in facilitator management
      responses:
        '200': { description: Users listed }
    post:
      tags: [Facilitator]
      summary: Create facilitator user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, email]
              properties:
                name: { type: string }
                email: { type: string, format: email }
      responses:
        '201': { description: Facilitator created }
  /facilitator/users/{facilitator}:
    put:
      tags: [Facilitator]
      summary: Update facilitator user
      parameters:
        - in: path
          name: facilitator
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string }
                email: { type: string, format: email }
      responses:
        '200': { description: Facilitator updated }
    delete:
      tags: [Facilitator]
      summary: Delete facilitator user
      parameters:
        - in: path
          name: facilitator
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Facilitator deleted }
  /notifications:
    get:
      tags: [Notifications]
      summary: List current user notifications
      responses:
        '200': { description: Notifications listed }
  /notifications/unread:
    get:
      tags: [Notifications]
      summary: List unread notifications
      responses:
        '200': { description: Unread notifications listed }
  /notifications/unread-count:
    get:
      tags: [Notifications]
      summary: Get unread notification count
      responses:
        '200': { description: Unread count returned }
  /notifications/read-all:
    post:
      tags: [Notifications]
      summary: Mark all notifications as read
      responses:
        '200': { description: Notifications marked read }
  /notifications/{id}/read:
    post:
      tags: [Notifications]
      summary: Mark one notification as read
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        '200': { description: Notification marked read }
  /notifications/delete/{id}:
    delete:
      tags: [Notifications]
      summary: Delete one notification
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        '200': { description: Notification deleted }
  /notifications/delete-all:
    delete:
      tags: [Notifications]
      summary: Delete all notifications
      responses:
        '200': { description: Notifications deleted }

  /quizzes:
    get:
      tags: [Quizzes]
      summary: List quiz bank items
      parameters:
        - in: query
          name: subject
          schema: { type: string }
        - in: query
          name: difficulty
          schema: { type: string }
        - in: query
          name: mine
          schema: { type: boolean }
      responses:
        '200': { description: Quiz list }
    post:
      tags: [Quizzes]
      summary: Create a quiz bank item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, questions]
              properties:
                title: { type: string }
                description: { type: string }
                subject: { type: string }
                difficulty: { type: string }
                level: { type: string }
                is_public: { type: boolean }
                time_limit_minutes: { type: integer }
                questions:
                  type: array
                  items:
                    type: object
                    properties:
                      prompt: { type: string }
                      options:
                        type: array
                        items: { type: string }
                      correct_answer: { type: string }
      responses:
        '201': { description: Quiz created }
  /quizzes/filters:
    get:
      tags: [Quizzes]
      summary: Get available quiz filters
      responses:
        '200': { description: Filter values }
  /quizzes/{quizId}:
    get:
      tags: [Quizzes]
      summary: Get quiz detail
      parameters:
        - in: path
          name: quizId
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Quiz detail }
    put:
      tags: [Quizzes]
      summary: Update a quiz bank item
      parameters:
        - in: path
          name: quizId
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Quiz updated }
    delete:
      tags: [Quizzes]
      summary: Delete a quiz bank item
      parameters:
        - in: path
          name: quizId
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Quiz deleted }
  /quizzes/{quizId}/submit:
    post:
      tags: [Quizzes]
      summary: Submit a quiz attempt
      parameters:
        - in: path
          name: quizId
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Quiz result }
  /quizzes/{quizId}/leaderboard:
    get:
      tags: [Quizzes]
      summary: Get quiz leaderboard
      parameters:
        - in: path
          name: quizId
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Leaderboard }
  /assessments/upcoming:
    get:
      tags: [Assessments]
      summary: Get upcoming published class tests and assessments
      responses:
        '200': { description: Upcoming assessments }
  /assessments/history:
    get:
      tags: [Assessments]
      summary: Get current learner assessment history
      responses:
        '200': { description: Assessment history }
  /assessments/by-code/{code}:
    get:
      tags: [Assessments]
      summary: Resolve an assessment using its access code
      parameters:
        - in: path
          name: code
          required: true
          schema: { type: string }
      responses:
        '200': { description: Assessment detail }
  /assessments/{assessmentId}/analytics:
    get:
      tags: [Assessments]
      summary: Get owner analytics for an assessment
      parameters:
        - in: path
          name: assessmentId
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Assessment analytics }
  /assessments/{assessmentId}/publish:
    post:
      tags: [Assessments]
      summary: Publish an assessment and move it into live or scheduled state
      parameters:
        - in: path
          name: assessmentId
          required: true
          schema: { type: integer }
      responses:
        '200': { description: Assessment published }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Bearer
  parameters:
    VideoId:
      in: path
      name: video
      required: true
      schema:
        type: integer
  schemas:
    VideoCreateRequest:
      type: object
      required: [title, source_type]
      properties:
        title: { type: string }
        description: { type: string, nullable: true }
        source_type: { type: string, enum: [upload, external] }
        video_type: { type: string, enum: [youtube, facebook, instagram, other, upload] }
        video_url: { type: string, format: uri, nullable: true }
        language: { type: string, nullable: true }
        is_public: { type: boolean, default: true }
        is_offline_ready: { type: boolean, default: false }
    VideoUpdateRequest:
      type: object
      properties:
        title: { type: string }
        description: { type: string, nullable: true }
        video_type: { type: string, enum: [youtube, facebook, instagram, other, upload] }
        video_url: { type: string, format: uri, nullable: true }
        language: { type: string, nullable: true }
        is_public: { type: boolean }
        is_offline_ready: { type: boolean }
    ProfileUpdateRequest:
      type: object
      properties:
        name: { type: string }
        bio: { type: string }
        gender: { type: string }
        phone: { type: string }
        country: { type: string }
        state: { type: string }
        city: { type: string }
        preferred_language: { type: string }
        school_name: { type: string }
        graduation_year: { type: integer }
        avatar: { type: string, format: binary }
    NoteRequest:
      type: object
      required: [title, body]
      properties:
        title: { type: string }
        body: { type: string }
        folder_id: { type: integer, nullable: true }
        subject: { type: string, nullable: true }
        topic: { type: string, nullable: true }
        is_public: { type: boolean }
    AssessmentRequest:
      type: object
      required: [title]
      properties:
        title: { type: string }
        description: { type: string }
        type: { type: string, enum: [quiz, test, assignment, exam] }
        duration_minutes: { type: integer }
        pass_score: { type: number }
        questions:
          type: array
          items:
            type: object
            properties:
              prompt: { type: string }
              type: { type: string }
              options:
                type: array
                items: { type: string }
              correct_answer: { type: string }
