openapi: 3.1.0
info:
  title: Chesly Bridge API
  version: 2.0.0
  description: |
    Chesly Bridge API - unified messaging backend.

    This API provides authentication, message management, bridge connections,
    and AI-powered search capabilities for the Chesly messaging platform.

    ## Security

    All endpoints except `/health` and `/api/attestation` require authentication via JWT Bearer tokens.
    Transport is TLS; sensitive columns are encrypted at rest.

    ## Rate Limiting

    All endpoints are rate limited. Rate limit headers are included in responses:
    - `X-RateLimit-Limit`: Maximum requests per window
    - `X-RateLimit-Remaining`: Remaining requests in current window
    - `X-RateLimit-Reset`: Unix timestamp when the window resets

  contact:
    name: Chesly Support
    email: support@chesly.app
  license:
    name: Proprietary
    url: https://chesly.app/terms

servers:
  - url: https://api.chesly.app
    description: Production server
  - url: http://localhost:3000
    description: Development server

tags:
  - name: Health
    description: Health check and service status
  - name: Authentication
    description: Passwordless authentication and session management
  - name: Keys
    description: User signing/device key management
  - name: Messages
    description: Message operations and search
  - name: Rooms
    description: Chat room management
  - name: Linked Rooms
    description: Cross-platform room linking
  - name: Bridges
    description: Platform bridge connections (WhatsApp, Telegram, Discord)
  - name: Devices
    description: Push notification device management
  - name: Accounts
    description: Multi-account management
  - name: Scheduled Messages
    description: Delayed message scheduling
  - name: Attestation
    description: Zero-knowledge server verification
  - name: Sync
    description: Real-time message synchronization

security:
  - BearerAuth: []

paths:
  /health:
    get:
      tags:
        - Health
      summary: Health check
      description: |
        Comprehensive health check including all dependencies.
        Returns status of database, Redis, AI service, and Matrix homeserver.
      security: []
      responses:
        '200':
          description: All services healthy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
        '503':
          description: One or more services unhealthy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'

  # ============================================================================
  # Authentication Endpoints
  # ============================================================================
  /api/auth/code:
    post:
      tags:
        - Authentication
      summary: Request verification code
      description: |
        Send a 6-digit verification code to the specified email address.
        Rate limited to 3 codes per 10 minutes per email.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                  example: user@example.com
      responses:
        '200':
          description: Verification code sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Verification code sent
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /api/auth/verify:
    post:
      tags:
        - Authentication
      summary: Verify code and authenticate
      description: |
        Verify the 6-digit code and authenticate the user.
        Creates a new account if the user doesn't exist.
        Returns access token, refresh token, and Matrix access token.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - code
              properties:
                email:
                  type: string
                  format: email
                code:
                  type: string
                  minLength: 6
                  maxLength: 6
                  pattern: ^\d{6}$
                publicKey:
                  type: string
                  format: base64
                  description: X25519 public key for encryption (new users only)
      responses:
        '200':
          description: Authentication successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthResponse'
        '400':
          description: Invalid or expired verification code
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/auth/refresh:
    post:
      tags:
        - Authentication
      summary: Refresh access token
      description: Exchange a refresh token for a new access token.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - refreshToken
              properties:
                refreshToken:
                  type: string
      responses:
        '200':
          description: New access token
          content:
            application/json:
              schema:
                type: object
                properties:
                  accessToken:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'

  /api/auth/logout:
    post:
      tags:
        - Authentication
      summary: Logout and revoke tokens
      description: Revoke all refresh tokens for the authenticated user.
      responses:
        '200':
          description: Logged out successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Logged out successfully

  /api/auth/me:
    get:
      tags:
        - Authentication
      summary: Get current user info
      description: Get the authenticated user's profile information.
      responses:
        '200':
          description: User profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserProfile'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /api/auth/profile:
    patch:
      tags:
        - Authentication
      summary: Update profile
      description: |
        Update user profile (display name, username, avatar).
        Avatar can be base64 encoded image, mxc:// URL, or https:// URL.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                displayName:
                  type: string
                  minLength: 1
                  maxLength: 255
                username:
                  type: string
                  minLength: 3
                  maxLength: 50
                  pattern: ^[a-zA-Z0-9_]+$
                avatarUrl:
                  type: string
                  description: Base64 image, mxc://, or https:// URL
      responses:
        '200':
          description: Profile updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  user:
                    $ref: '#/components/schemas/User'
        '409':
          description: Username already taken
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/auth/complete-onboarding:
    post:
      tags:
        - Authentication
      summary: Complete onboarding
      description: Mark the user as having completed the onboarding flow.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                displayName:
                  type: string
      responses:
        '200':
          description: Onboarding completed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  user:
                    $ref: '#/components/schemas/User'

  # ============================================================================
  # Keys Endpoints
  # ============================================================================
  /api/keys/register:
    post:
      tags:
        - Keys
      summary: Register public keys
      description: Register the user's signing and encryption public keys.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - publicKey
              properties:
                publicKey:
                  type: string
                  format: base64
                  description: X25519 public key (Base64 encoded)
                encryptedMasterKey:
                  type: string
                  format: base64
                  description: Encrypted master key for recovery
      responses:
        '200':
          description: Keys registered
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  keyVersion:
                    type: integer
        '400':
          description: Keys already registered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/keys:
    put:
      tags:
        - Keys
      summary: Update encryption keys
      description: Rotate the user's encryption keys (key rotation).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - publicKey
              properties:
                publicKey:
                  type: string
                  format: base64
                encryptedMasterKey:
                  type: string
                  format: base64
      responses:
        '200':
          description: Keys updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  keyVersion:
                    type: integer
    delete:
      tags:
        - Keys
      summary: Delete encryption keys
      description: |
        **WARNING: IRREVERSIBLE** - Delete all encryption keys.
        All encrypted messages will become permanently unreadable.
        Requires explicit confirmation header.
        Rate limited to 1 deletion per hour.
      parameters:
        - name: X-Confirm-Key-Deletion
          in: header
          required: true
          schema:
            type: string
            enum:
              - PERMANENTLY_DELETE_ALL_MY_MESSAGES
      responses:
        '200':
          description: Keys deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  warning:
                    type: string
        '400':
          description: Missing confirmation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'

  /api/keys/info:
    get:
      tags:
        - Keys
      summary: Get key info
      description: Get metadata about the user's encryption keys.
      responses:
        '200':
          description: Key info
          content:
            application/json:
              schema:
                type: object
                properties:
                  hasKeys:
                    type: boolean
                  hasRecoveryKey:
                    type: boolean
                  keyVersion:
                    type: integer
                  createdAt:
                    type: string
                    format: date-time
                  updatedAt:
                    type: string
                    format: date-time

  /api/keys/public/{targetUserId}:
    get:
      tags:
        - Keys
      summary: Get user's public key
      description: Get another user's public key for encrypting messages to them.
      parameters:
        - name: targetUserId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Public key
          content:
            application/json:
              schema:
                type: object
                properties:
                  publicKey:
                    type: string
                    format: base64
                  keyVersion:
                    type: integer
        '404':
          description: User has no encryption keys
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/keys/recovery:
    get:
      tags:
        - Keys
      summary: Get recovery key
      description: Get the encrypted master key for account recovery.
      responses:
        '200':
          description: Recovery key
          content:
            application/json:
              schema:
                type: object
                properties:
                  encryptedMasterKey:
                    type: string
                    format: base64
                  salt:
                    type: string
                    format: base64
        '404':
          description: No recovery key available
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================================================
  # Messages Endpoints
  # ============================================================================
  /api/messages:
    get:
      tags:
        - Messages
      summary: Get encrypted messages
      description: Get encrypted messages with visible metadata. Client decrypts content.
      parameters:
        - name: roomId
          in: query
          schema:
            type: string
        - name: before
          in: query
          description: Timestamp for pagination
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        '200':
          description: Encrypted messages
          content:
            application/json:
              schema:
                type: object
                properties:
                  messages:
                    type: array
                    items:
                      $ref: '#/components/schemas/EncryptedMessage'
    delete:
      tags:
        - Messages
      summary: Bulk delete messages
      description: |
        Delete multiple messages. Requires confirmation header.
        Can filter by room and/or timestamp.
      parameters:
        - name: roomId
          in: query
          schema:
            type: string
        - name: before
          in: query
          description: Delete messages before this timestamp
          schema:
            type: string
        - name: X-Confirm-Delete
          in: header
          required: true
          schema:
            type: string
            enum:
              - DELETE_MESSAGES
      responses:
        '200':
          description: Messages deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedCount:
                    type: integer
                  criteria:
                    type: object
                    properties:
                      roomId:
                        type: string
                      before:
                        type: string

  /api/messages/{messageId}:
    delete:
      tags:
        - Messages
      summary: Delete message
      description: Delete a specific message.
      parameters:
        - name: messageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Message deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedId:
                    type: string
        '404':
          description: Message not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/messages/send-multi:
    post:
      tags:
        - Messages
      summary: Send to multiple rooms
      description: |
        Send the same message to multiple rooms simultaneously.
        Handles partial failures gracefully.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - roomIds
                - body
              properties:
                roomIds:
                  type: array
                  items:
                    type: string
                  minItems: 1
                body:
                  type: string
                  minLength: 1
                msgtype:
                  type: string
                  default: m.text
      responses:
        '200':
          description: All messages sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MultiSendResponse'
        '207':
          description: Partial success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MultiSendResponse'
        '500':
          description: All messages failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MultiSendResponse'

  /api/messages/stats:
    get:
      tags:
        - Messages
      summary: Get message statistics
      description: Get aggregated statistics about messages.
      responses:
        '200':
          description: Message statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  stats:
                    type: object
                    properties:
                      total_messages:
                        type: integer
                      total_rooms:
                        type: integer
                      avg_importance:
                        type: number
                      high_importance_count:
                        type: integer
                      questions_count:
                        type: integer
                      all_categories:
                        type: array
                        items:
                          type: string

  # ============================================================================
  # Scheduled Messages Endpoints
  # ============================================================================
  /api/messages/schedule:
    post:
      tags:
        - Scheduled Messages
      summary: Schedule a message
      description: Create a new scheduled message for future delivery.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - roomId
                - encryptedBody
                - nonce
                - ephemeralPubkey
                - scheduledFor
              properties:
                roomId:
                  type: string
                encryptedBody:
                  type: string
                  format: base64
                nonce:
                  type: string
                  format: base64
                ephemeralPubkey:
                  type: string
                  format: base64
                scheduledFor:
                  type: string
                  format: date-time
                targetPlatform:
                  type: string
                  enum: [whatsapp, telegram, discord]
      responses:
        '201':
          description: Message scheduled
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  scheduledMessage:
                    $ref: '#/components/schemas/ScheduledMessage'
        '400':
          description: Invalid schedule time
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/messages/scheduled:
    get:
      tags:
        - Scheduled Messages
      summary: List scheduled messages
      description: Get all scheduled messages for the user.
      parameters:
        - name: roomId
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, sent, cancelled, failed]
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        '200':
          description: Scheduled messages
          content:
            application/json:
              schema:
                type: object
                properties:
                  scheduledMessages:
                    type: array
                    items:
                      $ref: '#/components/schemas/ScheduledMessage'
                  pagination:
                    $ref: '#/components/schemas/Pagination'

  /api/messages/scheduled/{id}:
    get:
      tags:
        - Scheduled Messages
      summary: Get scheduled message
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Scheduled message
          content:
            application/json:
              schema:
                type: object
                properties:
                  scheduledMessage:
                    $ref: '#/components/schemas/ScheduledMessage'
    patch:
      tags:
        - Scheduled Messages
      summary: Update scheduled message
      description: Update a pending scheduled message.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                scheduledFor:
                  type: string
                  format: date-time
                encryptedBody:
                  type: string
                  format: base64
                nonce:
                  type: string
                  format: base64
                ephemeralPubkey:
                  type: string
                  format: base64
      responses:
        '200':
          description: Scheduled message updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  scheduledMessage:
                    $ref: '#/components/schemas/ScheduledMessage'
        '400':
          description: Cannot update non-pending message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Scheduled Messages
      summary: Cancel scheduled message
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Message cancelled
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  cancelledId:
                    type: string

  /api/messages/scheduled/stats:
    get:
      tags:
        - Scheduled Messages
      summary: Get scheduled message stats
      responses:
        '200':
          description: Statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  stats:
                    type: object
                    properties:
                      pendingCount:
                        type: integer
                      sentCount:
                        type: integer
                      cancelledCount:
                        type: integer
                      failedCount:
                        type: integer
                      roomsWithScheduled:
                        type: integer
                      nextScheduled:
                        type: string
                        format: date-time
                        nullable: true

  # ============================================================================
  # Rooms Endpoints
  # ============================================================================
  /api/rooms:
    get:
      tags:
        - Rooms
      summary: Get rooms
      description: Get user's rooms sorted by importance, recency, or unread count.
      parameters:
        - name: sortBy
          in: query
          schema:
            type: string
            enum: [importance, recent, unread]
            default: importance
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        '200':
          description: Rooms list
          content:
            application/json:
              schema:
                type: object
                properties:
                  rooms:
                    type: array
                    items:
                      $ref: '#/components/schemas/Room'

  /api/rooms/{roomId}:
    get:
      tags:
        - Rooms
      summary: Get room details
      parameters:
        - name: roomId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Room details
          content:
            application/json:
              schema:
                type: object
                properties:
                  room:
                    $ref: '#/components/schemas/Room'
        '404':
          description: Room not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      tags:
        - Rooms
      summary: Update room
      description: Update room name or avatar (encrypted).
      parameters:
        - name: roomId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                encryptedName:
                  type: string
                  format: base64
                encryptedAvatar:
                  type: string
                  format: base64
                nonce:
                  type: string
                  format: base64
      responses:
        '200':
          description: Room updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
    delete:
      tags:
        - Rooms
      summary: Delete room
      description: Delete a room from Chesly (does not affect Matrix/bridges).
      parameters:
        - name: roomId
          in: path
          required: true
          schema:
            type: string
        - name: deleteMessages
          in: query
          schema:
            type: string
            enum: ['true', 'false']
            default: 'false'
      responses:
        '200':
          description: Room deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  roomId:
                    type: string
                  messagesDeleted:
                    type: boolean
                  deletedMessagesCount:
                    type: integer

  /api/rooms/{roomId}/read:
    post:
      tags:
        - Rooms
      summary: Mark room as read
      parameters:
        - name: roomId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Room marked as read
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/rooms/{roomId}/archive:
    post:
      tags:
        - Rooms
      summary: Archive room
      description: Hide room from active list without deleting.
      parameters:
        - name: roomId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Room archived
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  roomId:
                    type: string
                  note:
                    type: string

  /api/rooms/filter/priority:
    get:
      tags:
        - Rooms
      summary: Get priority rooms
      description: Get rooms with high-priority unread messages.
      responses:
        '200':
          description: Priority rooms
          content:
            application/json:
              schema:
                type: object
                properties:
                  rooms:
                    type: array
                    items:
                      $ref: '#/components/schemas/Room'

  # ============================================================================
  # Linked Rooms Endpoints
  # ============================================================================
  /api/rooms/link:
    post:
      tags:
        - Linked Rooms
      summary: Create linked room group
      description: Link multiple rooms together across platforms.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - roomIds
              properties:
                name:
                  type: string
                  maxLength: 255
                encryptedName:
                  type: string
                  format: base64
                nonce:
                  type: string
                  format: base64
                roomIds:
                  type: array
                  items:
                    type: string
                  minItems: 2
      responses:
        '201':
          description: Linked group created
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  group:
                    $ref: '#/components/schemas/LinkedRoomGroup'
        '409':
          description: Rooms already in a linked group
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/rooms/linked:
    get:
      tags:
        - Linked Rooms
      summary: List linked room groups
      responses:
        '200':
          description: Linked groups
          content:
            application/json:
              schema:
                type: object
                properties:
                  groups:
                    type: array
                    items:
                      $ref: '#/components/schemas/LinkedRoomGroup'

  /api/rooms/linked/{groupId}:
    get:
      tags:
        - Linked Rooms
      summary: Get linked room group
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Linked group details
          content:
            application/json:
              schema:
                type: object
                properties:
                  group:
                    $ref: '#/components/schemas/LinkedRoomGroup'
    put:
      tags:
        - Linked Rooms
      summary: Update linked room group
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                encryptedName:
                  type: string
                  format: base64
                nonce:
                  type: string
                  format: base64
      responses:
        '200':
          description: Group updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
    delete:
      tags:
        - Linked Rooms
      summary: Delete linked room group
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Group deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  groupId:
                    type: string

  /api/rooms/linked/{groupId}/rooms:
    post:
      tags:
        - Linked Rooms
      summary: Add rooms to group
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - roomIds
              properties:
                roomIds:
                  type: array
                  items:
                    type: string
                  minItems: 1
      responses:
        '200':
          description: Rooms added
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  addedRoomIds:
                    type: array
                    items:
                      type: string

  /api/rooms/linked/{groupId}/rooms/{roomId}:
    delete:
      tags:
        - Linked Rooms
      summary: Remove room from group
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: roomId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Room removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  roomId:
                    type: string
                  groupDeleted:
                    type: boolean

  /api/rooms/{roomId}/linked-group:
    get:
      tags:
        - Linked Rooms
      summary: Get room's linked group
      description: Get the linked group for a specific room (if any).
      parameters:
        - name: roomId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Linked group or null
          content:
            application/json:
              schema:
                type: object
                properties:
                  linkedGroup:
                    nullable: true
                    allOf:
                      - $ref: '#/components/schemas/LinkedRoomGroup'

  # ============================================================================
  # Bridges Endpoints
  # ============================================================================
  /api/bridges:
    get:
      tags:
        - Bridges
      summary: List all bridges
      description: Get all bridge connections for the user.
      responses:
        '200':
          description: Bridge list
          content:
            application/json:
              schema:
                type: object
                properties:
                  bridges:
                    type: array
                    items:
                      $ref: '#/components/schemas/Bridge'

  /api/bridges/configured:
    get:
      tags:
        - Bridges
      summary: Get configured platforms
      description: Get list of platforms that have bridges configured.
      responses:
        '200':
          description: Configured platforms
          content:
            application/json:
              schema:
                type: object
                properties:
                  platforms:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        configured:
                          type: boolean

  /api/bridges/{platform}:
    get:
      tags:
        - Bridges
      summary: Get bridge status
      description: Get specific bridge connection status.
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      responses:
        '200':
          description: Bridge status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BridgeStatus'
    delete:
      tags:
        - Bridges
      summary: Remove bridge
      description: Completely remove bridge connection and local record.
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      responses:
        '200':
          description: Bridge removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/bridges/{platform}/flows:
    get:
      tags:
        - Bridges
      summary: Get login flows
      description: Get available login flows for a platform.
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      responses:
        '200':
          description: Available login flows
          content:
            application/json:
              schema:
                type: object
                properties:
                  flows:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        description:
                          type: string

  /api/bridges/{platform}/login/start:
    post:
      tags:
        - Bridges
      summary: Start login flow
      description: Start a bridge login flow (QR code, phone verification, etc).
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - flowId
              properties:
                flowId:
                  type: string
                loginId:
                  type: string
                  description: For re-authentication
      responses:
        '200':
          description: Login started
          content:
            application/json:
              schema:
                type: object
                properties:
                  loginProcessId:
                    type: string
                  step:
                    $ref: '#/components/schemas/LoginStep'
        '429':
          $ref: '#/components/responses/RateLimited'

  /api/bridges/{platform}/login/step:
    post:
      tags:
        - Bridges
      summary: Submit login step
      description: Submit data for a login step (phone number, code, etc).
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - loginProcessId
                - stepId
                - stepType
                - data
              properties:
                loginProcessId:
                  type: string
                stepId:
                  type: string
                stepType:
                  type: string
                  enum: [user_input, cookies]
                data:
                  type: object
                  additionalProperties:
                    type: string
      responses:
        '200':
          description: Step submitted
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    properties:
                      complete:
                        type: boolean
                        const: true
                      loginId:
                        type: string
                      userInfo:
                        type: object
                  - type: object
                    properties:
                      complete:
                        type: boolean
                        const: false
                      step:
                        $ref: '#/components/schemas/LoginStep'

  /api/bridges/{platform}/login/wait:
    post:
      tags:
        - Bridges
      summary: Wait for step completion
      description: Wait for async step completion (QR code scan).
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - loginProcessId
                - stepId
              properties:
                loginProcessId:
                  type: string
                stepId:
                  type: string
      responses:
        '200':
          description: Wait result
          content:
            application/json:
              schema:
                type: object
                properties:
                  complete:
                    type: boolean
                  step:
                    $ref: '#/components/schemas/LoginStep'
                  loginId:
                    type: string
                  userInfo:
                    type: object
                  timeout:
                    type: object
                    properties:
                      totalSeconds:
                        type: integer
                      remainingSeconds:
                        type: integer
                      expiresAt:
                        type: string
                        format: date-time

  /api/bridges/{platform}/status:
    get:
      tags:
        - Bridges
      summary: Get connection status
      description: Get detailed bridge connection status.
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      responses:
        '200':
          description: Connection status
          content:
            application/json:
              schema:
                type: object
                properties:
                  connected:
                    type: boolean
                  logins:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        remoteName:
                          type: string
                        remoteProfile:
                          type: object
                        loggedIn:
                          type: boolean
                        stateEvent:
                          type: string
                        error:
                          type: string

  /api/bridges/{platform}/logout:
    post:
      tags:
        - Bridges
      summary: Logout from bridge
      description: Logout from a specific login or all logins.
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                loginId:
                  type: string
                  description: If not provided, logout from all
      responses:
        '200':
          description: Logged out
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/bridges/{platform}/connect:
    post:
      tags:
        - Bridges
      summary: Quick connect (legacy)
      description: Legacy endpoint - auto-selects login flow and starts connection.
      deprecated: true
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      responses:
        '200':
          description: Connection started
          content:
            application/json:
              schema:
                type: object
                properties:
                  method:
                    type: string
                    enum: [qr, phone, flow]
                  qrCode:
                    type: string
                  loginProcessId:
                    type: string
                  stepId:
                    type: string
                  timeout:
                    type: object
                    properties:
                      seconds:
                        type: integer
                      expiresAt:
                        type: string
                        format: date-time

  /api/bridges/{platform}/disconnect:
    post:
      tags:
        - Bridges
      summary: Disconnect (legacy)
      description: Legacy endpoint - alias for logout.
      deprecated: true
      parameters:
        - name: platform
          in: path
          required: true
          schema:
            type: string
            enum: [whatsapp, telegram, discord]
      responses:
        '200':
          description: Disconnected
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  # ============================================================================
  # Devices Endpoints
  # ============================================================================
  /api/devices:
    get:
      tags:
        - Devices
      summary: List devices
      description: Get all registered devices for push notifications.
      responses:
        '200':
          description: Device list
          content:
            application/json:
              schema:
                type: object
                properties:
                  devices:
                    type: array
                    items:
                      $ref: '#/components/schemas/Device'

  /api/devices/register:
    post:
      tags:
        - Devices
      summary: Register device
      description: Register a device token for APNs push notifications.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  minLength: 64
                  maxLength: 200
                  pattern: ^[a-fA-F0-9]+$
                  description: APNs device token (hex string)
                deviceName:
                  type: string
                  maxLength: 100
                deviceModel:
                  type: string
                  maxLength: 50
                osVersion:
                  type: string
                  maxLength: 20
                appVersion:
                  type: string
                  maxLength: 20
                environment:
                  type: string
                  enum: [production, development]
      responses:
        '200':
          description: Device registered
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  tokenId:
                    type: string
                    format: uuid

  /api/devices/unregister:
    post:
      tags:
        - Devices
      summary: Unregister by token
      description: Unregister a device by its token value.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  minLength: 64
                  maxLength: 200
                  pattern: ^[a-fA-F0-9]+$
      responses:
        '200':
          description: Device unregistered
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/devices/{tokenId}:
    delete:
      tags:
        - Devices
      summary: Unregister device
      description: Unregister a device by its ID.
      parameters:
        - name: tokenId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Device unregistered
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/devices/{tokenId}/test:
    post:
      tags:
        - Devices
      summary: Test push notification
      description: Send a test push notification to verify setup.
      parameters:
        - name: tokenId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Test notification sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/devices/status:
    get:
      tags:
        - Devices
      summary: Get push notification status
      description: Check if APNs is configured and available.
      responses:
        '200':
          description: Push status
          content:
            application/json:
              schema:
                type: object
                properties:
                  configured:
                    type: boolean
                  environment:
                    type: string
                    enum: [production, development]
                  bundleId:
                    type: string

  # ============================================================================
  # Accounts Endpoints
  # ============================================================================
  /api/accounts:
    get:
      tags:
        - Accounts
      summary: List accounts
      description: Get all accounts for the current user.
      responses:
        '200':
          description: Account list
          content:
            application/json:
              schema:
                type: object
                properties:
                  accounts:
                    type: array
                    items:
                      $ref: '#/components/schemas/Account'
                  activeAccount:
                    nullable: true
                    allOf:
                      - $ref: '#/components/schemas/Account'

  /api/accounts/active:
    get:
      tags:
        - Accounts
      summary: Get active account
      description: Get the currently active account with Matrix access token.
      responses:
        '200':
          description: Active account
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    $ref: '#/components/schemas/Account'
                  matrixAccessToken:
                    type: string

  /api/accounts/add/code:
    post:
      tags:
        - Accounts
      summary: Request code to add account
      description: Send verification code to add a new account.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
      responses:
        '200':
          description: Code sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          description: Account already added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/accounts/add/verify:
    post:
      tags:
        - Accounts
      summary: Verify and add account
      description: Verify code and add a new account.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - code
              properties:
                email:
                  type: string
                  format: email
                code:
                  type: string
                  minLength: 6
                  maxLength: 6
      responses:
        '200':
          description: Account added
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    $ref: '#/components/schemas/Account'

  /api/accounts/{id}/switch:
    post:
      tags:
        - Accounts
      summary: Switch account
      description: Switch to a different account.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Switched
          content:
            application/json:
              schema:
                type: object
                properties:
                  accessToken:
                    type: string
                  refreshToken:
                    type: string
                  matrixAccessToken:
                    type: string
                  account:
                    $ref: '#/components/schemas/Account'

  /api/accounts/{id}:
    patch:
      tags:
        - Accounts
      summary: Update account
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                displayName:
                  type: string
                avatarUrl:
                  type: string
                  format: uri
      responses:
        '200':
          description: Account updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    $ref: '#/components/schemas/Account'
    delete:
      tags:
        - Accounts
      summary: Remove account
      description: Remove an account (cannot remove primary account).
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Account removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          description: Cannot remove primary account
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /api/accounts/{id}/logout:
    post:
      tags:
        - Accounts
      summary: Logout account
      description: Logout a specific account without removing it.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Account logged out
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/accounts/{id}/reauth/code:
    post:
      tags:
        - Accounts
      summary: Request re-authentication code
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Code sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/accounts/{id}/reauth/verify:
    post:
      tags:
        - Accounts
      summary: Verify and reactivate account
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - code
              properties:
                code:
                  type: string
                  minLength: 6
                  maxLength: 6
      responses:
        '200':
          description: Account reactivated
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/accounts/detect:
    get:
      tags:
        - Accounts
      summary: Detect available accounts
      description: Get suggestions for accounts that could be added.
      responses:
        '200':
          description: Account suggestions
          content:
            application/json:
              schema:
                type: object
                properties:
                  suggestions:
                    type: array
                    items:
                      type: object
                      properties:
                        email:
                          type: string
                        source:
                          type: string
                        confidence:
                          type: string
                          enum: [high, medium, low]
                  existingCount:
                    type: integer
                  hint:
                    type: string

  # ============================================================================
  # Attestation Endpoints
  # ============================================================================
  /api/attestation:
    get:
      tags:
        - Attestation
      summary: Get server attestation
      description: |
        Get server attestation for zero-knowledge verification.
        Clients verify buildHash against known-good values.
      security: []
      responses:
        '200':
          description: Attestation data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttestationResponse'

  /api/attestation/verify:
    post:
      tags:
        - Attestation
      summary: Verify attestation signature
      description: Verify a previously received Ed25519 attestation signature.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - buildHash
                - configHash
                - runtimeHash
                - timestamp
                - signature
              properties:
                buildHash:
                  type: string
                configHash:
                  type: string
                runtimeHash:
                  type: string
                timestamp:
                  type: string
                  format: date-time
                signature:
                  type: string
      responses:
        '200':
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
        '400':
          description: Invalid or expired attestation
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
                    const: false
                  error:
                    type: string

  /api/attestation/expected-hashes:
    get:
      tags:
        - Attestation
      summary: Get expected build hashes
      description: |
        Get list of valid build hashes for client verification.
        Requires authentication for security.
      responses:
        '200':
          description: Expected hashes
          content:
            application/json:
              schema:
                type: object
                properties:
                  hashes:
                    type: array
                    items:
                      type: string
                  note:
                    type: string
                  retrievedAt:
                    type: string
                    format: date-time

  # ============================================================================
  # Sync Endpoint
  # ============================================================================
  /api/sync:
    get:
      tags:
        - Sync
      summary: Real-time sync (SSE)
      description: |
        Server-Sent Events endpoint for real-time message sync.
        Maintains long-polling connection to Matrix homeserver.
      parameters:
        - name: since
          in: query
          description: Sync token for incremental sync
          schema:
            type: string
      responses:
        '200':
          description: SSE stream
          content:
            text/event-stream:
              schema:
                type: string
                description: |
                  Events:
                  - `room`: New messages in a room
                  - `heartbeat`: Keep-alive with sync token
                  - `error`: Sync error with retry information

  # ============================================================================
  # Internal Endpoints
  # ============================================================================
  /api/messages/internal/process:
    post:
      tags:
        - Messages
      summary: Process message (internal)
      description: |
        Internal endpoint for bridges to process and store encrypted messages.
        Requires internal API key.
      security:
        - InternalApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
                - roomId
                - eventId
                - body
                - senderId
                - timestamp
              properties:
                userId:
                  type: string
                roomId:
                  type: string
                eventId:
                  type: string
                body:
                  type: string
                senderId:
                  type: string
                senderName:
                  type: string
                timestamp:
                  type: integer
                mediaType:
                  type: string
                mediaData:
                  type: string
                  format: base64
      responses:
        '200':
          description: Message processed
          content:
            application/json:
              schema:
                type: object
                properties:
                  messageId:
                    type: string
                  importance:
                    type: number
                  categories:
                    type: array
                    items:
                      type: string
                  aiProcessed:
                    type: boolean

  /api/rooms/internal/upsert:
    post:
      tags:
        - Rooms
      summary: Upsert room (internal)
      description: Internal endpoint for bridges to create/update rooms.
      security:
        - InternalApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
                - roomId
                - platform
              properties:
                userId:
                  type: string
                roomId:
                  type: string
                name:
                  type: string
                avatarUrl:
                  type: string
                platform:
                  type: string
                isGroup:
                  type: boolean
                  default: false
                memberCount:
                  type: integer
                  default: 0
      responses:
        '200':
          description: Room upserted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string

  /api/developer/tokens:
    get:
      summary: List your personal API tokens
      description: |
        Returns the metadata for every token the caller has created. The
        plaintext is never included — to view a token's secret, the user
        must reveal it on the device that created it (Settings → Developer
        → API Tokens). Token-management endpoints reject csk_ bearer
        tokens; only JWT or signature auth is accepted here.
      tags:
        - Developer
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Token list
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokens:
                    type: array
                    items:
                      $ref: '#/components/schemas/APIToken'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      summary: Create a new personal API token
      description: |
        Plus only. Returns the plaintext secret in the response body —
        this is the only time it ever leaves the server, so capture it
        immediately. iOS persists it to the Keychain on the creating
        device.
      tags:
        - Developer
      security:
        - BearerAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 100
                  description: Optional human label.
                expiresInDays:
                  type: integer
                  minimum: 1
                  maximum: 365
                  description: Optional expiry, in days. Omit for no expiry.
      responses:
        '200':
          description: Token created
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/APIToken'
                  - type: object
                    required:
                      - token
                    properties:
                      token:
                        type: string
                        description: The full csk_ plaintext, returned exactly once.
        '400':
          description: Validation error or per-user token cap reached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Plus subscription required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/developer/tokens/{id}:
    delete:
      summary: Revoke a personal API token
      description: |
        Idempotent. After revocation, requests using the revoked token
        receive `401 Unauthorized`.
      tags:
        - Developer
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Revoked
        '404':
          description: Token not found (also returned for tokens belonging to a different user, by design).

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT access token obtained from /api/auth/verify or /api/auth/refresh
    BearerAuthCSK:
      type: http
      scheme: bearer
      bearerFormat: csk_*
      description: |
        Personal API token (Plus feature). Format: `csk_<48 url-safe chars>`.
        Mint one in the iOS app at Settings → Developer → API Tokens.
        Tokens are gated on the user's subscription tier on every request —
        if Plus lapses, all of the user's tokens are immediately rejected
        with `403 subscription_expired`.
    InternalApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Internal API key for service-to-service communication

  responses:
    Unauthorized:
      description: Authentication required or token invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Too many requests
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
              retryAfter:
                type: integer
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
          description: Detailed message (development only)

    APIToken:
      type: object
      description: Metadata for a personal API token. Never includes the plaintext.
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
          description: Optional human label.
        tokenPrefix:
          type: string
          example: csk_aBcD
          description: First 8 characters of the plaintext, for at-a-glance ID.
        tokenLast4:
          type: string
          example: x9z2
          description: Last 4 characters of the plaintext.
        expiresAt:
          type: string
          format: date-time
          nullable: true
        lastUsedAt:
          type: string
          format: date-time
          nullable: true
        createdAt:
          type: string
          format: date-time

    User:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
          format: email
        username:
          type: string
          nullable: true
        displayName:
          type: string
          nullable: true
        avatarUrl:
          type: string
          nullable: true
        hasCompletedOnboarding:
          type: boolean

    UserProfile:
      type: object
      properties:
        user:
          $ref: '#/components/schemas/User'
        hasEncryptionKeys:
          type: boolean

    AuthResponse:
      type: object
      properties:
        accessToken:
          type: string
        refreshToken:
          type: string
        matrixAccessToken:
          type: string
        user:
          $ref: '#/components/schemas/User'
        isNewUser:
          type: boolean

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [healthy, degraded, unhealthy]
        version:
          type: string
        timestamp:
          type: string
          format: date-time
        totalLatencyMs:
          type: integer
        dependencies:
          type: object
          properties:
            database:
              type: object
              properties:
                healthy:
                  type: boolean
                latencyMs:
                  type: integer
            redis:
              type: object
              properties:
                healthy:
                  type: boolean
                latencyMs:
                  type: integer
            synapse:
              type: object
              properties:
                healthy:
                  type: boolean
                latencyMs:
                  type: integer

    EncryptedMessage:
      type: object
      properties:
        id:
          type: string
          format: uuid
        roomId:
          type: string
        encryptedBody:
          type: string
          format: base64
        encryptedSender:
          type: string
          format: base64
          nullable: true
        encryptedMedia:
          type: string
          format: base64
          nullable: true
        nonce:
          type: string
          format: base64
        ephemeralPubkey:
          type: string
          format: base64
        importance:
          type: number
          minimum: 0
          maximum: 1
        categories:
          type: array
          items:
            type: string
        sentiment:
          type: string
        language:
          type: string
        isQuestion:
          type: boolean
        isActionable:
          type: boolean
        summaryHint:
          type: string
        hasMedia:
          type: boolean
        mediaType:
          type: string
          nullable: true
        timestamp:
          type: integer
        eventId:
          type: string

    MultiSendResponse:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              roomId:
                type: string
              success:
                type: boolean
              eventId:
                type: string
              error:
                type: string
        summary:
          type: object
          properties:
            total:
              type: integer
            succeeded:
              type: integer
            failed:
              type: integer

    Room:
      type: object
      properties:
        id:
          type: string
          format: uuid
        roomId:
          type: string
        encryptedName:
          type: string
          format: base64
          nullable: true
        encryptedAvatar:
          type: string
          format: base64
          nullable: true
        nonce:
          type: string
          format: base64
          nullable: true
        platform:
          type: string
        isGroup:
          type: boolean
        memberCount:
          type: integer
          nullable: true
        lastMessageTs:
          type: integer
          nullable: true
        unreadCount:
          type: integer
        lastImportance:
          type: number
          nullable: true
        lastCategories:
          type: array
          items:
            type: string
          nullable: true
        lastSentiment:
          type: string
          nullable: true
        lastIsQuestion:
          type: boolean
          nullable: true

    LinkedRoomGroup:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
        encryptedName:
          type: string
          format: base64
          nullable: true
        nonce:
          type: string
          format: base64
          nullable: true
        rooms:
          type: array
          items:
            type: object
            properties:
              roomId:
                type: string
              addedAt:
                type: string
                format: date-time
              encryptedName:
                type: string
                format: base64
                nullable: true
              platform:
                type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    ScheduledMessage:
      type: object
      properties:
        id:
          type: string
          format: uuid
        roomId:
          type: string
        encryptedBody:
          type: string
          format: base64
        nonce:
          type: string
          format: base64
        ephemeralPubkey:
          type: string
          format: base64
        scheduledFor:
          type: string
          format: date-time
        status:
          type: string
          enum: [pending, sent, cancelled, failed]
        targetPlatform:
          type: string
          enum: [whatsapp, telegram, discord]
          nullable: true
        lastError:
          type: string
          nullable: true
        retryCount:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        sentAt:
          type: string
          format: date-time
          nullable: true

    Pagination:
      type: object
      properties:
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer
        hasMore:
          type: boolean

    Bridge:
      type: object
      properties:
        id:
          type: string
          format: uuid
        platform:
          type: string
          enum: [whatsapp, telegram, discord]
        loginId:
          type: string
          nullable: true
        status:
          type: string
        avatarUrl:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time

    BridgeStatus:
      type: object
      properties:
        platform:
          type: string
        status:
          type: string
        connected:
          type: boolean
        logins:
          type: array
          items:
            type: object
        loginId:
          type: string
          nullable: true
        avatarUrl:
          type: string
          nullable: true
        error:
          type: string
          nullable: true
        lastCheckedAt:
          type: string
          format: date-time
        statusDiverged:
          type: boolean

    LoginStep:
      type: object
      properties:
        type:
          type: string
          enum: [user_input, display_and_wait, cookies, complete]
        step_id:
          type: string
        instructions:
          type: string
        user_input_params:
          type: object
          properties:
            fields:
              type: array
              items:
                type: object
        display_and_wait_params:
          type: object
          properties:
            type:
              type: string
            data:
              type: string
        complete_params:
          type: object
          properties:
            user_login_id:
              type: string
            user_info:
              type: object

    Device:
      type: object
      properties:
        id:
          type: string
          format: uuid
        deviceName:
          type: string
          nullable: true
        deviceModel:
          type: string
          nullable: true
        osVersion:
          type: string
          nullable: true
        appVersion:
          type: string
          nullable: true
        environment:
          type: string
          enum: [production, development]
        createdAt:
          type: string
          format: date-time
        lastUsedAt:
          type: string
          format: date-time
          nullable: true

    Account:
      type: object
      properties:
        id:
          type: string
          format: uuid
        matrixUserId:
          type: string
        email:
          type: string
          format: email
        displayName:
          type: string
          nullable: true
        avatarUrl:
          type: string
          nullable: true
        isActive:
          type: boolean
        isPrimary:
          type: boolean
        status:
          type: string
          enum: [active, suspended, logged_out]
        lastUsedAt:
          type: string
          format: date-time
          nullable: true
        createdAt:
          type: string
          format: date-time

    AttestationResponse:
      type: object
      properties:
        buildHash:
          type: string
          description: SHA256 hash of the sealed Docker image
        configHash:
          type: string
          description: SHA256 hash of configuration files
        runtimeHash:
          type: string
          description: SHA256 hash of runtime verification
        timestamp:
          type: string
          format: date-time
        securityFeatures:
          type: object
          properties:
            sealedMode:
              type: boolean
            shellRemoved:
              type: boolean
            readOnlyFilesystem:
              type: boolean
            nonRootUser:
              type: boolean
            seccompProfile:
              type: boolean
            networkIsolation:
              type: boolean
        signature:
          type: string
          description: Base64-encoded Ed25519 signature of attestation data
