openapi: 3.0.3
info:
  title: Tribe Social API
  description: |
    The Tribe Social API lets you integrate with the Tribe Social platform —
    managing users, content, collections, groups, chat, analytics, and more.

    This spec is generated from the Tribe Social documentation site.
  version: 1.0.0
  contact:
    name: Tribe Social Support
    email: support@tribesocial.io
servers:
  - url: https://api.tribesocial.io
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: Sign in, sign out, OTP, and password reset
  - name: Users
    description: Create, update, and manage users
  - name: Content
    description: Create, retrieve, search, and delete content items
  - name: Collections
    description: Organize content into collections
  - name: Groups
    description: Community groups and membership
  - name: Chat
    description: Content-level chat messages
  - name: Platforms
    description: Platform configuration and lookup
  - name: Analytics
    description: View events and platform/video analytics
  - name: Push Notifications
    description: Schedule and send push notifications
  - name: Transcripts
    description: Enqueue transcript generation jobs
  - name: Vector DB
    description: Semantic search and AI chat
  - name: Posts
    description: Community discussion posts within groups
  - name: Comments
    description: Comments and replies on content items
  - name: Reactions
    description: React to content and chat messages with emoji

paths:
  /api/user/signin:
    post:
      tags: [Authentication]
      summary: Sign In
      description: Sign in with email and password to receive an authentication token.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password, PlatformId]
              properties:
                email: { type: string, format: email }
                password: { type: string, format: password }
                PlatformId: { type: string }
              example:
                email: user@example.com
                password: userpassword
                PlatformId: "123"
      responses:
        '200':
          description: Signed in
          content:
            application/json:
              schema:
                type: object
                properties:
                  token: { type: string }
                  user:
                    type: object
                    properties:
                      id: { type: integer }
                      email: { type: string }
                      name: { type: string }
                      role: { type: string }
                      status: { type: string }
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /api/user/signout:
    post:
      tags: [Authentication]
      summary: Sign Out
      description: Invalidate the current authentication token.
      responses:
        '200':
          description: Signed out
          content:
            application/json:
              schema:
                type: object
                properties:
                  message: { type: string, example: Successfully signed out }

  /api/user/signin-otp:
    post:
      tags: [Authentication]
      summary: Request OTP
      description: Request a one-time password for authentication.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, PlatformId]
              properties:
                email: { type: string, format: email }
                PlatformId: { type: string }
      responses:
        '200':
          description: OTP sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message: { type: string, example: OTP sent successfully }
                  expiresIn: { type: integer, example: 300 }

  /api/user/verify-otp:
    post:
      tags: [Authentication]
      summary: Verify OTP
      description: Verify the one-time password and receive an authentication token.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, otp, PlatformId]
              properties:
                email: { type: string, format: email }
                otp: { type: string }
                PlatformId: { type: string }
      responses:
        '200':
          description: Verified
          content:
            application/json:
              schema:
                type: object
                properties:
                  token: { type: string }
                  user: { type: object }

  /api/user/forgot-password:
    post:
      tags: [Authentication]
      summary: Request Password Reset
      description: Request a password reset link to be sent to the user's email.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, PlatformId]
              properties:
                email: { type: string, format: email }
                PlatformId: { type: string }
      responses:
        '200':
          description: Instructions sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message: { type: string, example: Password reset instructions sent }

  /api/user/reset-password:
    post:
      tags: [Authentication]
      summary: Reset Password
      description: Reset the user's password using the reset token.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [token, password]
              properties:
                token: { type: string }
                password: { type: string, format: password }
      responses:
        '200':
          description: Password reset
          content:
            application/json:
              schema:
                type: object
                properties:
                  message: { type: string, example: Password successfully reset }

  /api/user:
    post:
      tags: [Users]
      summary: Add or Edit User
      description: Create a new user or update an existing user's information.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id: { type: integer, description: Provide to update an existing user }
                email: { type: string, format: email }
                name: { type: string }
                role: { type: string, enum: [free, basic, premium, admin] }
                PlatformId: { type: integer }
                photoUrl: { type: string }
                bannerImageUrl: { type: string }
                notes: { type: string }
                GroupIds:
                  type: array
                  items: { type: integer }
                status: { type: string }
                password: { type: string, format: password }
      responses:
        '200':
          description: User created or updated
          content:
            application/json:
              schema:
                type: array
                description: "[user, created]"
                items: {}

  /api/user/{id}:
    delete:
      tags: [Users]
      summary: Delete User
      description: Remove a user from the platform.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message: { type: string }
                  id: { type: string }

  /api/platform/{platformId}/admins:
    get:
      tags: [Users]
      summary: Get Platform Admins
      description: Retrieve a list of all administrators for a specific platform.
      parameters:
        - in: path
          name: platformId
          required: true
          schema: { type: string }
        - in: query
          name: page
          schema: { type: integer, default: 1 }
        - in: query
          name: limit
          schema: { type: integer, default: 20 }
      responses:
        '200':
          description: List of admins
          content:
            application/json:
              schema:
                type: object
                properties:
                  admins:
                    type: array
                    items: { type: object }
                  total: { type: integer }
                  page: { type: integer }
                  limit: { type: integer }

  /api/content:
    post:
      tags: [Content]
      summary: Add or Edit Content
      description: Add new content or update existing content.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id: { type: integer, nullable: true }
                title: { type: string }
                description: { type: string }
                type: { type: string, enum: [article, video, post] }
                visibility: { type: string, enum: [public, private, members] }
                GroupIds:
                  type: array
                  items: { type: integer }
                KeywordIds:
                  type: array
                  items: { type: integer }
                featuredImage: { type: string, nullable: true }
                video: { type: string, nullable: true }
                chatEnabled: { type: boolean }
                featured: { type: boolean }
                publishedDate: { type: string, format: date-time, nullable: true }
                expireDate: { type: string, format: date-time, nullable: true }
      responses:
        '200':
          description: Content created or updated
          content:
            application/json:
              schema:
                type: array
                description: "[content, created]"
                items: {}

  /api/content/{id}:
    get:
      tags: [Content]
      summary: Get Content by ID
      description: Retrieve a specific content item by its ID.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: The content item
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Content'
    delete:
      tags: [Content]
      summary: Delete Content
      description: Remove content by its ID.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message: { type: string, example: Content deleted successfully }

  /api/content/search:
    get:
      tags: [Content]
      summary: Search Content
      description: Search for content across the platform by title, description, or author name.
      parameters:
        - in: query
          name: q
          required: true
          schema: { type: string }
        - in: query
          name: PlatformId
          schema: { type: integer }
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items: { type: object }
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/content/vector-search-preview:
    get:
      tags: [Vector DB]
      summary: Vector Search Preview
      description: Search transcript segments using semantic/vector similarity.
      parameters:
        - in: query
          name: q
          required: true
          schema: { type: string, minLength: 2 }
        - in: query
          name: platformId
          schema: { type: integer }
        - in: query
          name: limit
          schema: { type: integer, default: 10 }
        - in: query
          name: scoreThreshold
          schema: { type: number, default: 0.3 }
        - in: query
          name: contentIds
          schema: { type: string, description: Comma-separated content IDs }
      responses:
        '200':
          description: Vector search results
          content:
            application/json:
              schema:
                type: object
                properties:
                  query: { type: string }
                  count: { type: integer }
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        score: { type: number }
                        segment:
                          type: object
                          properties:
                            text: { type: string }
                            start: { type: number }
                            end: { type: number }
                        content: { type: object }
        '400':
          $ref: '#/components/responses/BadRequest'
        '503':
          description: Vector search not enabled on this node

  /api/collection:
    post:
      tags: [Collections]
      summary: Create or Update Collection
      description: Create a new collection or update an existing one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id: { type: integer, nullable: true }
                name: { type: string }
                description: { type: string }
                slug: { type: string }
                collectionBGImage: { type: string, nullable: true }
                position: { type: integer }
                expireDate: { type: string, format: date-time, nullable: true }
                publishedDate: { type: string, format: date-time, nullable: true }
                collectionType: { type: string }
                sortPreference: { type: string }
                PlatformId: { type: integer }
                UserId: { type: integer }
                showOnHomepage: { type: boolean }
                GroupIds:
                  type: array
                  items: { type: integer }
                ContentIds:
                  type: array
                  items: { type: integer }
      responses:
        '200':
          description: Collection created or updated
          content:
            application/json:
              schema:
                type: array
                description: "[collection, created]"
                items: {}
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/collection-by-id/{id}:
    get:
      tags: [Collections]
      summary: Get Collection by ID
      description: Retrieve detailed information about a specific collection by its unique identifier.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
        - in: query
          name: showFuture
          schema: { type: string, enum: ["true", "false"] }
      responses:
        '200':
          description: The collection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Collection'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/collection-by-slug/{slug}/{PlatformId}:
    get:
      tags: [Collections]
      summary: Get Collection by Slug
      description: Retrieve detailed information about a specific collection by its slug and platform ID.
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
        - in: path
          name: PlatformId
          required: true
          schema: { type: integer }
        - in: query
          name: limit
          schema: { type: integer }
      responses:
        '200':
          description: The collection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Collection'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/collection-options:
    get:
      tags: [Collections]
      summary: Get Collection Options
      description: Retrieve a simplified list of collections for use in dropdown menus and selection interfaces.
      responses:
        '200':
          description: Option list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id: { type: integer }
                    name: { type: string }
                    UserId: { type: integer }
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/collection/{PlatformId}:
    get:
      tags: [Collections]
      summary: Get Collections by Platform
      description: Retrieve all collections associated with a specific platform.
      parameters:
        - in: path
          name: PlatformId
          required: true
          schema: { type: integer }
        - in: query
          name: showHidden
          schema: { type: string, enum: ["true", "false"] }
      responses:
        '200':
          description: Collections
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Collection'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/admin/collections:
    get:
      tags: [Collections]
      summary: Get Collections for Dashboard
      description: Retrieve collections for use in the admin dashboard, with pagination, filtering, and search capabilities.
      parameters:
        - in: query
          name: itemsPerPage
          schema: { type: integer, default: 25 }
        - in: query
          name: currentPage
          schema: { type: integer, default: 1 }
        - in: query
          name: searchTerm
          schema: { type: string }
        - in: query
          name: filters
          schema: { type: string }
        - in: query
          name: sort
          schema: { type: string, example: "position:asc" }
      responses:
        '200':
          description: Paginated collections
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedCollections'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/collection/{id}/public:
    get:
      tags: [Collections]
      summary: Get Public Collection by ID
      description: Retrieve a single collection by its ID without requiring authentication.
      security: []
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: The collection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Collection'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/collection/public/{PlatformId}:
    get:
      tags: [Collections]
      summary: Get Public Collections
      description: Retrieve public collections for a specific platform, optimized for display on the platform's homepage.
      security: []
      parameters:
        - in: path
          name: PlatformId
          required: true
          schema: { type: integer }
        - in: query
          name: itemsPerPage
          schema: { type: integer, default: 25 }
        - in: query
          name: currentPage
          schema: { type: integer, default: 1 }
        - in: query
          name: searchTerm
          schema: { type: string }
        - in: query
          name: filters
          schema: { type: string }
        - in: query
          name: sort
          schema: { type: string }
      responses:
        '200':
          description: Paginated public collections
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedCollections'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/collections/reorder:
    post:
      tags: [Collections]
      summary: Reorder Collections
      description: Update the display order of multiple collections.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [collectionIds]
              properties:
                collectionIds:
                  type: array
                  items: { type: integer }
      responses:
        '200':
          description: Reordered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/group:
    post:
      tags: [Groups]
      summary: Create or Update Group
      description: Create a new group or update an existing group's information.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id: { type: integer, nullable: true }
                name: { type: string }
                description: { type: string }
                visibility: { type: string, enum: [public, private, premium] }
                coverImage: { type: string }
                purchaseLink: { type: string, nullable: true }
                adminIds:
                  type: array
                  items: { type: integer }
      responses:
        '200':
          description: Group created or updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Group'
        '400':
          $ref: '#/components/responses/BadRequest'
    get:
      tags: [Groups]
      summary: Get Group
      description: Retrieve detailed information about a specific group using its id or slug.
      parameters:
        - in: query
          name: id
          schema: { type: integer }
        - in: query
          name: slug
          schema: { type: string }
      responses:
        '200':
          description: The group
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Group'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/group/collections:
    get:
      tags: [Groups]
      summary: Get Group Collections
      description: Retrieve collections belonging to a specific group.
      parameters:
        - in: query
          name: id
          schema: { type: integer }
        - in: query
          name: slug
          schema: { type: string }
        - in: query
          name: page
          schema: { type: integer, default: 1 }
        - in: query
          name: limit
          schema: { type: integer, default: 25 }
        - in: query
          name: search
          schema: { type: string }
      responses:
        '200':
          description: Paginated collections
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'

  /api/group/contents:
    get:
      tags: [Groups]
      summary: Get Group Contents
      description: Retrieve content items belonging to a specific group.
      parameters:
        - in: query
          name: id
          schema: { type: integer }
        - in: query
          name: slug
          schema: { type: string }
        - in: query
          name: page
          schema: { type: integer, default: 1 }
        - in: query
          name: limit
          schema: { type: integer, default: 25 }
        - in: query
          name: search
          schema: { type: string }
      responses:
        '200':
          description: Paginated contents
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'

  /api/groups/platform/{platformId}:
    get:
      tags: [Groups]
      summary: Get Groups by Platform ID
      description: Retrieve all groups belonging to a specific platform.
      parameters:
        - in: path
          name: platformId
          required: true
          schema: { type: integer }
        - in: query
          name: page
          schema: { type: integer, default: 1 }
        - in: query
          name: limit
          schema: { type: integer, default: 25 }
        - in: query
          name: search
          schema: { type: string }
      responses:
        '200':
          description: Paginated groups
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/my-groups:
    get:
      tags: [Groups]
      summary: Get My Groups
      description: Retrieve all groups in which the requesting user is a member or admin.
      responses:
        '200':
          description: Groups
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Group'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/group/{id}/user/{userId}:
    delete:
      tags: [Groups]
      summary: Remove User from Group
      description: Remove a user from a specific group.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
        - in: path
          name: userId
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  message: { type: string }
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/chat/:
    post:
      tags: [Chat]
      summary: Create Chat Message
      description: Create a new chat message associated with a specific content item.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message, ContentId, UserId]
              properties:
                message: { type: string }
                ContentId: { type: integer }
                UserId: { type: string }
                parentCommentId: { type: integer, nullable: true }
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: array
                description: "[message, created]"
                items: {}
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/chat/{id}:
    get:
      tags: [Chat]
      summary: Get Chat Messages
      description: Retrieve all chat messages associated with a specific content item.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Messages
          content:
            application/json:
              schema:
                type: array
                items: { type: object }
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/chat/{id}/{delete}/:
    delete:
      tags: [Chat]
      summary: Delete Chat Message
      description: Mark a chat message as deleted (soft delete).
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
        - in: path
          name: delete
          required: true
          schema: { type: string, enum: ["true"] }
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'

  /api/platform/{id}:
    get:
      tags: [Platforms]
      summary: Get Platform by ID
      description: Retrieve detailed information about a specific platform using its unique identifier.
      security: []
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        '200':
          description: The platform
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Platform'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/platform/slug/{slug}:
    get:
      tags: [Platforms]
      summary: Get Platform by Slug
      description: Retrieve detailed platform information using its unique slug identifier.
      security: []
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
      responses:
        '200':
          description: The platform
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Platform'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/platform/homepage/{url}:
    get:
      tags: [Platforms]
      summary: Get Platform by Homepage URL
      description: Retrieve platform information using its homepage URL.
      security: []
      parameters:
        - in: path
          name: url
          required: true
          schema: { type: string }
      responses:
        '200':
          description: The platform
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Platform'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/platform/info:
    get:
      tags: [Platforms]
      summary: Get Platform Info
      description: Retrieve comprehensive information about a platform, including associated content, creators, and configuration data.
      parameters:
        - in: query
          name: platformId
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Platform info
          content:
            application/json:
              schema:
                type: object
                properties:
                  followingCreatorsContent:
                    type: array
                    items: { type: object }
                  featuredChurches:
                    type: array
                    items: { type: object }
                  featuredContent: { nullable: true }
                  trendingContent:
                    type: array
                    items: { type: object }
                  topics:
                    type: array
                    items: { type: object }
                  ads:
                    type: array
                    items: { type: object }
                  platformdata: { type: object }

  /api/platform/analytics/{id}:
    get:
      tags: [Analytics]
      summary: Get Platform Analytics
      description: Retrieves user count statistics by role for a specific platform.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Role counts
          content:
            application/json:
              schema:
                type: object
                properties:
                  free: { type: integer }
                  premium: { type: integer }
                  basic: { type: integer }
                  admin: { type: integer }
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/view-data:
    post:
      tags: [Analytics]
      summary: Create View Event
      description: Records that a specific content item was viewed by a user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ContentId]
              properties:
                ContentId: { type: integer }
      responses:
        '201':
          description: Recorded
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/view-data/week:
    get:
      tags: [Analytics]
      summary: View Data by Week
      description: Retrieves aggregated view data organized by week for the authenticated creator's content.
      responses:
        '200':
          description: Weekly view data
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    date: { type: string, example: "06/07/2022" }
                    count: { type: integer }
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/video-analytics:
    post:
      tags: [Analytics]
      summary: Video Analytics
      description: Records detailed analytics about video watch events, including duration and playback position.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [videoId, watchedDuration, currentTime]
              properties:
                videoId: { type: integer }
                watchedDuration: { type: number, description: Seconds watched }
                currentTime: { type: number, description: Current playback position in seconds }
      responses:
        '200':
          description: Recorded
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/push-notifications:
    get:
      tags: [Push Notifications]
      summary: Get Push Notifications
      description: Retrieve a list of push notifications for a platform.
      parameters:
        - in: query
          name: page
          schema: { type: integer, default: 1 }
        - in: query
          name: limit
          schema: { type: integer, default: 25 }
        - in: query
          name: sort
          schema: { type: string }
        - in: query
          name: order
          schema: { type: string, enum: [asc, desc] }
        - in: query
          name: PlatformId
          schema: { type: integer }
        - in: query
          name: GroupId
          schema: { type: integer }
      responses:
        '200':
          description: List
          content:
            application/json:
              schema:
                type: object
                properties:
                  count: { type: integer }
                  rows:
                    type: array
                    items:
                      $ref: '#/components/schemas/PushNotification'
        '400':
          $ref: '#/components/responses/BadRequest'
    post:
      tags: [Push Notifications]
      summary: Send Push Notification
      description: Send push notifications to users' mobile devices.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                GroupId: { type: integer }
                PlatformId: { type: integer }
                title: { type: string }
                message: { type: string }
                scheduledFor: { type: string, format: date-time }
                ContentId: { type: integer }
                url: { type: string }
                parameterData: { type: string }
                destinationPage: { type: string }
                recipients:
                  type: array
                  items: { type: integer }
      responses:
        '200':
          description: Sent
          content:
            text/plain:
              schema:
                type: string
                example: Notification sent
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/push-notification/{id}:
    delete:
      tags: [Push Notifications]
      summary: Delete Push Notification
      description: Delete a scheduled push notification that has not yet been sent.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Deleted
          content:
            text/plain:
              schema:
                type: string
                example: Notification deleted
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/transcript/enqueue-group/{groupId}:
    get:
      tags: [Transcripts]
      summary: Enqueue Transcripts for Group
      description: Enqueue transcript generation jobs for all video or audio contents within a specific group.
      parameters:
        - in: path
          name: groupId
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Enqueued
          content:
            application/json:
              schema:
                type: object
                properties:
                  groupId: { type: string }
                  total: { type: integer }
                  queued: { type: integer }
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/ServerError'

  /api/transcript/{contentId}/enqueue:
    post:
      tags: [Transcripts]
      summary: Enqueue Transcript by Content ID
      description: Enqueue a background job to generate a transcript for a single content item.
      parameters:
        - in: path
          name: contentId
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                forceTranscriptGeneration: { type: boolean }
                forceEmbeddingGeneration: { type: boolean }
      responses:
        '200':
          description: Enqueued
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId: { type: string }
                  state: { type: string }
        '500':
          $ref: '#/components/responses/ServerError'

  /api/chatembed/{group_id}:
    get:
      tags: [Vector DB]
      summary: Get or Initialize Chat by Group ID
      description: Retrieve (or create) an AI-powered chat session tied to a specific group.
      parameters:
        - in: path
          name: group_id
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Chat session
          content:
            application/json:
              schema:
                type: object
                properties:
                  aiChat: { type: object }
                  messages:
                    type: array
                    items: { type: object }
                  createdNewChat: { type: boolean }
        '500':
          $ref: '#/components/responses/ServerError'

  /api/posts:
    get:
      tags: [Posts]
      summary: List Posts
      description: |
        Retrieve a paginated list of posts for a group or user. Each post includes
        inline `reactionCounts` and `myReactions` fields. At least one of `groupId`
        or `userId` is required.
      parameters:
        - in: query
          name: groupId
          schema: { type: integer }
          description: The group ID to fetch posts for
        - in: query
          name: userId
          schema: { type: integer }
          description: Fetch posts by a specific user (non-admins can only use their own ID)
        - in: query
          name: keyword
          schema: { type: string }
          description: Filter posts by keyword name
        - in: query
          name: pinned
          schema: { type: string }
          description: Set to "true" to return only pinned posts
        - in: query
          name: fromGroupAdmins
          schema: { type: string }
          description: Set to "true" to filter to posts from group admins only
        - in: query
          name: searchTerm
          schema: { type: string }
          description: Full-text search on post title
        - in: query
          name: adminStatus
          schema: { type: string, enum: [answered, unanswered] }
          description: Filter by admin reply status
        - in: query
          name: includeScheduled
          schema: { type: string }
          description: Set to "true" to include future-dated posts (group admins only)
        - in: query
          name: type
          schema:
            type: string
            enum: [video embed, wistia, searchie, resi, download, restream, poll, onestream, video, audio, article, live streaming, link, zoom, internallink, mux, book, meeting]
          description: Filter posts by content type (e.g. "article", "video", "live streaming")
        - in: query
          name: itemsPerPage
          schema: { type: integer, default: 25 }
          description: Number of posts per page
        - in: query
          name: currentPage
          schema: { type: integer, default: 1 }
          description: Page number (1-based)
      responses:
        '200':
          description: Paginated list of posts
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Post'
                  totalItems: { type: integer }
                  currentPage: { type: integer }
                  itemsPerPage: { type: integer }
                  totalPages: { type: integer }
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Posts]
      summary: Create Post
      description: Create a new post in a group's community feed.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [groupId]
              properties:
                groupId: { type: integer, description: The group to create the post in }
                title: { type: string }
                description: { type: string, description: Post body (supports HTML) }
                type: { type: string, enum: [article, video, link], default: article }
                video: { type: string }
                contentURI: { type: string }
                featuredImage: { type: string }
                duration: { type: integer }
                isLiveStream: { type: boolean }
                streamId: { type: string }
                ctaBtnText: { type: string }
                ctaBtnUrl: { type: string }
                publishedDate: { type: string, format: date-time }
                pinned: { type: boolean }
                KeywordIds: { type: array, items: { type: integer } }
                Keywords: { type: array, items: { type: string }, description: Keyword names (alternative to KeywordIds) }
      responses:
        '201':
          description: Post created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Post'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /api/posts/{id}:
    get:
      tags: [Posts]
      summary: Get Post by ID
      description: Retrieve a single post including inline reactionCounts and myReactions.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The post ID
      responses:
        '200':
          description: Post object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Post'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Posts]
      summary: Update Post
      description: |
        Update an existing post. Only the post creator, a group admin, or a platform admin
        can update a post. All body fields are optional.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The post ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title: { type: string }
                description: { type: string }
                type: { type: string, enum: [article, video, link] }
                video: { type: string }
                contentURI: { type: string }
                featuredImage: { type: string }
                duration: { type: integer }
                isLiveStream: { type: boolean }
                streamId: { type: string }
                ctaBtnText: { type: string }
                ctaBtnUrl: { type: string }
                publishedDate: { type: string, format: date-time }
      responses:
        '200':
          description: Updated post
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Post'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags: [Posts]
      summary: Delete Post
      description: |
        Delete a post. Only the post creator, a group admin, or a platform admin can delete.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The post ID
      responses:
        '200':
          description: Post deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  message: { type: string, example: Post deleted successfully }
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/posts/count:
    get:
      tags: [Posts]
      summary: Get Posts Count
      description: Get the total number of posts, optionally scoped to a specific group.
      parameters:
        - in: query
          name: groupId
          schema: { type: integer }
          description: Scope count to a specific group
      responses:
        '200':
          description: Post count
          content:
            application/json:
              schema:
                type: object
                properties:
                  count: { type: integer }
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'

  /api/posts/{id}/pin:
    post:
      tags: [Posts]
      summary: Toggle Pin Post
      description: Toggle whether a post is pinned. Only group admins or platform admins can pin/unpin.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The post ID
      responses:
        '200':
          description: Pin state toggled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  pinned: { type: boolean }
                  postId: { type: integer }
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/posts/{id}/broadcast:
    get:
      tags: [Posts]
      summary: Get Post Broadcast
      description: Retrieve live stream broadcast data for a post from Firestore.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The post ID
      responses:
        '200':
          description: Broadcast data (or found=false if no broadcast)
          content:
            application/json:
              schema:
                type: object
                properties:
                  found: { type: boolean }
                  video: { type: string, nullable: true }
                  featuredImage: { type: string, nullable: true }
                  isLive: { type: boolean }
                  livestreamState: { type: string, nullable: true }
                  watcherCount: { type: integer }
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/content/{id}/reactions:
    get:
      tags: [Reactions]
      summary: Get Reactions on a Content Item
      security:
        - bearerAuth: []
        - {}
      description: |
        Retrieve reaction data for a specific content item (post, video, etc.).

        - **Without `emoji` query param**: returns aggregated counts per emoji
          plus the authenticated user's own reactions.
        - **With `emoji` query param**: returns the list of users who reacted
          with that specific emoji, including `{id, name, photoUrl}`.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The content item ID
        - in: query
          name: emoji
          required: false
          schema:
            $ref: '#/components/schemas/Emoji'
          description: Filter to a specific emoji to get the list of reactor users.
      responses:
        '200':
          description: Reaction data
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    title: Aggregated counts (no emoji filter)
                    properties:
                      reactions:
                        type: array
                        items:
                          $ref: '#/components/schemas/ReactionCount'
                      myReactions:
                        type: array
                        items:
                          $ref: '#/components/schemas/Emoji'
                        description: Emojis the authenticated user has reacted with (empty array if unauthenticated)
                  - type: object
                    title: Users for a specific emoji
                    properties:
                      emoji:
                        $ref: '#/components/schemas/Emoji'
                      users:
                        type: array
                        items:
                          type: object
                          properties:
                            id: { type: integer }
                            name: { type: string }
                            photoUrl: { type: string, nullable: true }
        '404':
          $ref: '#/components/responses/NotFound'

  /api/content/{id}/react:
    post:
      tags: [Reactions]
      summary: Add Reaction to Content
      description: |
        Add an emoji reaction to a content item. If the user has already reacted
        with the same emoji, the endpoint returns 200 with `alreadyExists: true`.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The content item ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [emoji]
              properties:
                emoji:
                  $ref: '#/components/schemas/Emoji'
            example:
              emoji: heart
      responses:
        '201':
          description: Reaction added
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
        '200':
          description: Reaction already exists
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  alreadyExists: { type: boolean, example: true }
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags: [Reactions]
      summary: Remove Reaction from Content
      description: Remove a previously added emoji reaction from a content item.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The content item ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [emoji]
              properties:
                emoji:
                  $ref: '#/components/schemas/Emoji'
            example:
              emoji: heart
      responses:
        '200':
          description: Reaction removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/chat:
    get:
      tags: [Comments]
      summary: List Comments on a Content Item
      security:
        - bearerAuth: []
        - {}
      description: |
        Retrieve paginated top-level comments for a content item (e.g. a post).
        Each comment includes a `replyCount`, inline `reactions` (aggregated
        counts), and the authenticated user's `myReactions`.

        Use the returned comment `id` as `parentCommentId` with `GET /api/replies`
        to fetch threaded replies.
      parameters:
        - in: query
          name: contentId
          required: true
          schema: { type: integer }
          description: The content item ID whose comments to fetch
        - in: query
          name: itemsPerPage
          schema: { type: integer, default: 25 }
          description: Number of comments per page
        - in: query
          name: currentPage
          schema: { type: integer, default: 1 }
          description: Page number (1-based)
      responses:
        '200':
          description: Paginated list of comments
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Comment'
                  totalItems: { type: integer }
                  currentPage: { type: integer }
                  itemsPerPage: { type: integer }
                  totalPages: { type: integer }

  /api/replies:
    get:
      tags: [Comments]
      summary: List Replies to a Comment
      security:
        - bearerAuth: []
        - {}
      description: |
        Retrieve paginated replies (threaded responses) to a specific comment.
        Each reply includes inline `reactions` and the authenticated user's `myReactions`.
      parameters:
        - in: query
          name: parentCommentId
          required: true
          schema: { type: integer }
          description: The parent comment ID to fetch replies for
        - in: query
          name: itemsPerPage
          schema: { type: integer, default: 25 }
          description: Number of replies per page
        - in: query
          name: currentPage
          schema: { type: integer, default: 1 }
          description: Page number (1-based)
      responses:
        '200':
          description: Paginated list of replies
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Reply'
                  totalItems: { type: integer }
                  currentPage: { type: integer }
                  itemsPerPage: { type: integer }
                  totalPages: { type: integer }

  /api/chat/{id}/reactions:
    get:
      tags: [Reactions]
      summary: Get Reactions on a Chat Message
      security:
        - bearerAuth: []
        - {}
      description: |
        Retrieve reaction data for a specific chat message (comment or reply).

        - **Without `emoji` query param**: returns aggregated counts per emoji
          plus the authenticated user's own reactions.
        - **With `emoji` query param**: returns the list of users who reacted
          with that specific emoji.
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The chat message ID
        - in: query
          name: emoji
          required: false
          schema:
            $ref: '#/components/schemas/Emoji'
          description: Filter to a specific emoji to get the list of reactor users.
      responses:
        '200':
          description: Reaction data
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    title: Aggregated counts (no emoji filter)
                    properties:
                      reactions:
                        type: array
                        items:
                          $ref: '#/components/schemas/ReactionCount'
                      myReactions:
                        type: array
                        items:
                          $ref: '#/components/schemas/Emoji'
                        description: Emojis the authenticated user has reacted with (empty array if unauthenticated)
                  - type: object
                    title: Users for a specific emoji
                    properties:
                      emoji:
                        $ref: '#/components/schemas/Emoji'
                      users:
                        type: array
                        items:
                          type: object
                          properties:
                            id: { type: integer }
                            name: { type: string }
                            photoUrl: { type: string, nullable: true }
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags: [Reactions]
      summary: Toggle Reaction on a Chat Message
      description: |
        Toggle an emoji reaction on a chat message. If the reaction does not
        exist it is created; if it already exists it is removed (toggle behaviour).
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
          description: The chat message ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [emoji]
              properties:
                emoji:
                  $ref: '#/components/schemas/Emoji'
            example:
              emoji: fire
      responses:
        '200':
          description: Reaction toggled
          content:
            application/json:
              schema:
                type: object
                properties:
                  action:
                    type: string
                    enum: [added, removed]
                  emoji:
                    $ref: '#/components/schemas/Emoji'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  responses:
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  schemas:
    Emoji:
      type: string
      enum:
        - grinning
        - crying
        - heart_eyes
        - party
        - angry
        - exploding_head
        - rocket
        - star_eyes
        - sunglasses
        - hugging
        - heart
        - praying
        - raising_hands
        - fire
        - thumbs_up
      description: |
        Emoji identifier. Supported values and their unicode equivalents:
        `grinning` 😀, `crying` 😭, `heart_eyes` 😍, `party` 🥳, `angry` 😡,
        `exploding_head` 🤯, `rocket` 🚀, `star_eyes` 🤩, `sunglasses` 😎,
        `hugging` 🤗, `heart` ❤️, `praying` 🙏, `raising_hands` 🙌,
        `fire` 🔥, `thumbs_up` 👍

    ReactionCount:
      type: object
      properties:
        emoji:
          $ref: '#/components/schemas/Emoji'
        count:
          type: integer
          description: Number of users who reacted with this emoji

    Post:
      type: object
      properties:
        id: { type: integer }
        title: { type: string, nullable: true }
        description: { type: string, nullable: true, description: Rich text / HTML body }
        type: { type: string, enum: [article, video, link] }
        visibility: { type: string }
        video: { type: string, nullable: true }
        contentURI: { type: string, nullable: true }
        featuredImage: { type: string, nullable: true }
        duration: { type: integer, nullable: true }
        isLiveStream: { type: boolean }
        streamId: { type: string, nullable: true }
        ctaBtnText: { type: string, nullable: true }
        ctaBtnUrl: { type: string, nullable: true }
        publishedDate: { type: string, format: date-time, nullable: true }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
        commentCount: { type: integer, description: Top-level non-deleted comment count }
        reactionCounts:
          type: array
          items:
            $ref: '#/components/schemas/ReactionCount'
          description: Aggregated reaction counts per emoji
        myReactions:
          type: array
          items:
            $ref: '#/components/schemas/Emoji'
          description: Emoji identifiers the authenticated user has reacted with
        adminHasReplied: { type: boolean, description: Whether an admin has commented }
        adminLastReply: { type: string, format: date-time, nullable: true }
        User:
          $ref: '#/components/schemas/User'
        ContentGroups:
          type: array
          items:
            type: object
            properties:
              GroupId: { type: integer }
              type: { type: string }
              pinned: { type: boolean }
        Keywords:
          type: array
          items:
            type: object
            properties:
              id: { type: integer }
              title: { type: string }

    Comment:
      type: object
      properties:
        id: { type: integer }
        message: { type: string, description: Rich text / HTML comment body }
        messagePlain: { type: string, description: Plain text version of the comment }
        displayName: { type: string, nullable: true }
        deleted: { type: boolean }
        moderationCategory: { type: string, nullable: true }
        isEdited: { type: boolean }
        editedAt: { type: string, format: date-time, nullable: true }
        createdAt: { type: string, format: date-time }
        replyCount: { type: integer, description: Number of threaded replies }
        reactions:
          type: array
          items:
            $ref: '#/components/schemas/ReactionCount'
          description: Aggregated reaction counts per emoji
        myReactions:
          type: array
          items:
            $ref: '#/components/schemas/Emoji'
          description: Emoji identifiers the authenticated user has reacted with
        User:
          type: object
          properties:
            id: { type: integer }
            name: { type: string }
            photoUrl: { type: string, nullable: true }
            banned: { type: boolean }

    Reply:
      type: object
      properties:
        id: { type: integer }
        message: { type: string, description: Rich text / HTML reply body }
        messagePlain: { type: string, description: Plain text version of the reply }
        displayName: { type: string, nullable: true }
        deleted: { type: boolean }
        isEdited: { type: boolean }
        editedAt: { type: string, format: date-time, nullable: true }
        createdAt: { type: string, format: date-time }
        UserId: { type: integer }
        parentCommentId: { type: integer }
        reactions:
          type: array
          items:
            $ref: '#/components/schemas/ReactionCount'
          description: Aggregated reaction counts per emoji
        myReactions:
          type: array
          items:
            $ref: '#/components/schemas/Emoji'
          description: Emoji identifiers the authenticated user has reacted with
        User:
          type: object
          properties:
            id: { type: integer }
            name: { type: string }
            photoUrl: { type: string, nullable: true }
            banned: { type: boolean }

    Error:
      type: object
      properties:
        error: { type: string }
        message: { type: string }

    User:
      type: object
      properties:
        id: { type: integer }
        email: { type: string }
        name: { type: string }
        role: { type: string }
        status: { type: string }
        photoUrl: { type: string, nullable: true }
        bannerImageUrl: { type: string, nullable: true }
        notes: { type: string, nullable: true }
        PlatformId: { type: integer }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }

    Content:
      type: object
      properties:
        id: { type: integer }
        title: { type: string }
        slug: { type: string }
        description: { type: string, nullable: true }
        descriptionPlain: { type: string, nullable: true }
        likeCount: { type: integer }
        commentCount: { type: integer }
        hasCurrentUserLiked: { type: boolean }
        featuredImage: { type: string, nullable: true }
        type: { type: string, enum: [link, video, post, article] }
        contentURI: { type: string, nullable: true }
        video: { type: string, nullable: true }
        chatEnabled: { type: boolean }
        visibility: { type: string, enum: [public, private, members, premium] }
        expireDate: { type: string, format: date-time, nullable: true }
        publishedDate: { type: string, format: date-time, nullable: true }
        featured: { type: boolean }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
        PlatformId: { type: integer }
        UserId: { type: integer }
        Groups:
          type: array
          items:
            $ref: '#/components/schemas/Group'
        User:
          $ref: '#/components/schemas/User'

    Collection:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        description: { type: string, nullable: true }
        descriptionPlain: { type: string, nullable: true }
        descriptionHtml: { type: string, nullable: true }
        slug: { type: string }
        collectionBGImage: { type: string, nullable: true }
        position: { type: integer }
        expireDate: { type: string, format: date-time, nullable: true }
        publishedDate: { type: string, format: date-time, nullable: true }
        collectionType: { type: string }
        sortPreference: { type: string }
        showOnHomepage: { type: boolean }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
        PlatformId: { type: integer }
        UserId: { type: integer, nullable: true }
        Contents:
          type: array
          items:
            $ref: '#/components/schemas/Content'
        Ads:
          type: array
          items: { type: object }
        contentTotal: { type: integer }

    Group:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        description: { type: string, nullable: true }
        slug: { type: string }
        coverImage: { type: string, nullable: true }
        visibility: { type: string, enum: [public, private, premium] }
        purchaseLink: { type: string, nullable: true }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
        PlatformId: { type: integer }
        groupCreatorId: { type: integer }
        userCount: { type: integer }
        myRole: { type: string, nullable: true }

    Platform:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        slug: { type: string }
        description: { type: string, nullable: true }
        upgradeBtn: { type: string, nullable: true }
        heroImage: { type: string, nullable: true }
        favIconImage: { type: string, nullable: true }
        heroBGColor: { type: string, nullable: true }
        heroTextColor: { type: string, nullable: true }
        primaryColor: { type: string, nullable: true }
        secondaryColor: { type: string, nullable: true }
        darkColor: { type: string, nullable: true }
        lightColor: { type: string, nullable: true }
        heroImgOnly: { type: boolean }
        fathomAnalytics: { type: string, nullable: true }
        homepageUrl: { type: string, nullable: true }
        upgradeUrl: { type: string, nullable: true }
        logo: { type: string, nullable: true }
        premiumToken: { type: string, nullable: true }
        basicToken: { type: string, nullable: true }
        hideSignin: { type: boolean, nullable: true }
        timezone: { type: string, nullable: true }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }

    PushNotification:
      type: object
      properties:
        id: { type: integer }
        GroupId: { type: integer }
        PlatformId: { type: integer }
        title: { type: string }
        message: { type: string }
        status: { type: string, enum: [scheduled, sent, failed] }
        scheduledFor: { type: string, format: date-time }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
        parameterData: { type: string }
        destinationPage: { type: string }

    PaginatedList:
      type: object
      properties:
        items:
          type: array
          items: { type: object }
        itemsPerPage: { type: integer }
        currentPage: { type: integer }
        totalItems: { type: integer }
        totalPages: { type: integer }
        searchTerm: { type: string }
        hasMore: { type: boolean }

    PaginatedCollections:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/Collection'
        currentPage: { type: integer }
        itemsPerPage: { type: integer }
        totalItems: { type: integer }
        totalPages: { type: integer }
        firstItem: { type: integer }
        lastItem: { type: integer }
        sort: { type: string, nullable: true }
        filters: { type: string, nullable: true }
