# Author: marco, June 2, 2025
openapi: 3.0.3
info:
  title: InNotes API
  description: API documentation for InNotes - A contact and job management platform
  version: 0.1.0
  contact:
    name: InNotes Support
    url: https://innotes.com
servers:
  - url: http://localhost:3000
    description: Development server
  - url: https://innotes.com
    description: Production server

paths:
  /api/user:
    get:
      summary: Get current user information
      description: Retrieve the authenticated user's profile information, including subscription status
      security:
        - bearerAuth: []
      responses:
        '200':
          description: User information retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          description: Unauthorized - Invalid or missing authentication
        '404':
          description: User not found
        '500':
          description: Internal server error

  /api/contacts:
    get:
      summary: Get contacts
      description: Retrieve contacts with optional pagination, search, and filtering
      security:
        - bearerAuth: []
      parameters:
        - name: page
          in: query
          description: Page number for pagination
          schema:
            type: integer
            default: 1
        - name: search
          in: query
          description: Search term for filtering contacts
          schema:
            type: string
        - name: tags
          in: query
          description: Comma-separated list of tags to filter by
          schema:
            type: string
        - name: tag
          in: query
          description: Individual tag filter (can be used multiple times)
          schema:
            type: array
            items:
              type: string
        - name: ext_id
          in: query
          description: External ID for extension compatibility
          schema:
            type: string
      responses:
        '200':
          description: Contacts retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Contact'
        '401':
          description: Unauthorized
        '404':
          description: No contacts found
        '500':
          description: Internal server error

    post:
      summary: Create a new contact
      description: Add a new contact to the user's contact list
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
      responses:
        '200':
          description: Contact created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          description: Bad request - Missing required fields
        '401':
          description: Unauthorized
        '402':
          description: Payment required - Free user limit exceeded
        '409':
          description: Conflict - Contact with linkedin_key already exists
        '500':
          description: Internal server error

  /api/contacts/{id}:
    get:
      summary: Get contact by ID
      description: Retrieve a specific contact by its ID
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Contact ID
          schema:
            type: string
      responses:
        '200':
          description: Contact retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '401':
          description: Unauthorized
        '404':
          description: Contact not found
        '500':
          description: Internal server error

    put:
      summary: Update contact
      description: Update an existing contact
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Contact ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
      responses:
        '200':
          description: Contact updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '404':
          description: Contact not found
        '500':
          description: Internal server error

    delete:
      summary: Delete contact
      description: Delete a contact from the user's contact list
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Contact ID
          schema:
            type: string
      responses:
        '200':
          description: Contact deleted successfully
        '401':
          description: Unauthorized
        '404':
          description: Contact not found
        '500':
          description: Internal server error

  /api/job:
    get:
      summary: Get jobs
      description: Retrieve job applications with optional search and filtering
      security:
        - bearerAuth: []
      parameters:
        - name: ext_id
          in: query
          description: External ID for extension compatibility
          schema:
            type: string
        - name: searchTerm
          in: query
          description: Search term for filtering jobs
          schema:
            type: string
      responses:
        '200':
          description: Jobs retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Job'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

    post:
      summary: Create a new job
      description: Add a new job application
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JobInput'
      responses:
        '201':
          description: Job created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: ID of the created job
        '401':
          description: Unauthorized
        '402':
          description: Payment required - Free user limit exceeded
        '409':
          description: Conflict - Job with ext_id already exists
        '500':
          description: Internal server error

  /api/job/{id}:
    get:
      summary: Get job by ID
      description: Retrieve a specific job by its ID
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Job ID
          schema:
            type: string
      responses:
        '200':
          description: Job retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          description: Unauthorized
        '404':
          description: Job not found
        '500':
          description: Internal server error

    put:
      summary: Update job
      description: Update an existing job application
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Job ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JobInput'
      responses:
        '200':
          description: Job updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '404':
          description: Job not found
        '500':
          description: Internal server error

    delete:
      summary: Delete job
      description: Delete a job application
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Job ID
          schema:
            type: string
      responses:
        '200':
          description: Job deleted successfully
        '401':
          description: Unauthorized
        '404':
          description: Job not found
        '500':
          description: Internal server error

  /api/note:
    get:
      summary: Get notes
      description: Retrieve notes for a specific contact or job
      security:
        - bearerAuth: []
      parameters:
        - name: linkedinDataId
          in: query
          description: LinkedIn data ID (deprecated, use ext_id)
          schema:
            type: string
        - name: ext_id
          in: query
          description: External ID of the related entity
          schema:
            type: string
      responses:
        '200':
          description: Notes retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Note'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

    post:
      summary: Create a new note
      description: Add a new note to a contact or job
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NoteInput'
      responses:
        '201':
          description: Note created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: ID of the created note
        '400':
          description: Bad request - ext_id is required
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/note/{id}:
    get:
      summary: Get note by ID
      description: Retrieve a specific note by its ID
      tags:
        - Notes
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Note ID
      responses:
        '200':
          description: Note retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Note'
        '401':
          description: Unauthorized
        '404':
          description: Note not found
        '500':
          description: Internal server error
    put:
      summary: Update note
      description: Update an existing note
      tags:
        - Notes
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Note ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                text:
                  type: string
                  description: Note content
                flagColor:
                  type: string
                  description: Flag color for the note
                lastUpdate:
                  type: string
                  format: date-time
                  description: Last update timestamp
                visibility:
                  type: string
                  enum: [private, shared_read_only, shared_editable, public]
                  description: Note visibility setting
      responses:
        '200':
          description: Note updated successfully
        '401':
          description: Unauthorized
        '404':
          description: Note not found
        '500':
          description: Internal server error
    delete:
      summary: Delete note
      description: Delete a note
      tags:
        - Notes
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Note ID
      responses:
        '200':
          description: Note deleted successfully
        '401':
          description: Unauthorized
        '404':
          description: Note not found
        '500':
          description: Internal server error

  /api/note/{id}/share/read:
    post:
      summary: Share note for reading
      description: Grant read access to a note for another user or group
      tags:
        - Notes
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Note ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
                  description: User ID to grant read access
                groupId:
                  type: string
                  description: Group ID to grant read access
      responses:
        '201':
          description: Read access granted successfully
        '400':
          description: Bad request - must specify userId or groupId
        '401':
          description: Unauthorized
        '500':
          description: Internal server error
    delete:
      summary: Revoke read access
      description: Revoke read access to a note from a user or group
      tags:
        - Notes
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Note ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
                  description: User ID to revoke read access from
                groupId:
                  type: string
                  description: Group ID to revoke read access from
      responses:
        '204':
          description: Read access revoked successfully
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/note/{id}/share/edit:
    post:
      summary: Share note for editing
      description: Grant edit access to a note for another user or group
      tags:
        - Notes
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Note ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
                  description: User ID to grant edit access
                groupId:
                  type: string
                  description: Group ID to grant edit access
      responses:
        '201':
          description: Edit access granted successfully
        '400':
          description: Bad request - must specify userId or groupId
        '401':
          description: Unauthorized
        '500':
          description: Internal server error
    delete:
      summary: Revoke edit access
      description: Revoke edit access to a note from a user or group
      tags:
        - Notes
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Note ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userId:
                  type: string
                  description: User ID to revoke edit access from
                groupId:
                  type: string
                  description: Group ID to revoke edit access from
      responses:
        '204':
          description: Edit access revoked successfully
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/tokens:
    get:
      summary: List API tokens
      description: Get all API tokens for the authenticated user
      tags:
        - Tokens
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Tokens retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokens:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiToken'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error
    post:
      summary: Create API token
      description: Create a new API token for programmatic access
      tags:
        - Tokens
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  maxLength: 100
                  description: Name for the token
                expiresInDays:
                  type: integer
                  default: 90
                  description: Number of days until token expires
                scopes:
                  type: array
                  items:
                    type: string
                  default: [read, write]
                  description: Permissions granted to this token
      responses:
        '201':
          description: Token created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  token:
                    type: string
                    description: The actual token (shown only once)
                  name:
                    type: string
                  expiresAt:
                    type: string
                    format: date-time
                  scopes:
                    type: array
                    items:
                      type: string
        '400':
          description: Invalid token name
        '401':
          description: Unauthorized
        '409':
          description: Token with this name already exists
        '500':
          description: Internal server error

  /api/tokens/{id}:
    delete:
      summary: Deactivate token
      description: Deactivate or delete an API token
      tags:
        - Tokens
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Token ID
      responses:
        '200':
          description: Token deactivated successfully
        '400':
          description: Token ID is required
        '401':
          description: Unauthorized
        '404':
          description: Token not found
        '500':
          description: Internal server error
    patch:
      summary: Update token
      description: Update token name or scopes
      tags:
        - Tokens
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Token ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 100
                  description: New name for the token
                scopes:
                  type: array
                  items:
                    type: string
                  description: New permissions for this token
      responses:
        '200':
          description: Token updated successfully
        '400':
          description: Bad request - at least one field required
        '401':
          description: Unauthorized
        '404':
          description: Token not found
        '409':
          description: Token with this name already exists
        '500':
          description: Internal server error

  /api/search:
    get:
      summary: Search notes
      description: Search through notes content
      security:
        - bearerAuth: []
      parameters:
        - name: searchTerm
          in: query
          required: true
          description: Search term for filtering notes
          schema:
            type: string
      responses:
        '200':
          description: Search results retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SearchResult'
        '400':
          description: Bad request - Search term is required
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/tags:
    get:
      summary: Get all tags
      description: Retrieve all unique tags for the authenticated user
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Tags retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Tag'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/statuses:
    get:
      summary: Get all statuses
      description: Retrieve all contact statuses for the authenticated user
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Statuses retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Status'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

    post:
      summary: Create a new status
      description: Add a new contact status
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StatusInput'
      responses:
        '201':
          description: Status created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/statuses/{id}:
    put:
      summary: Update status
      description: Update an existing contact status
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Status ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StatusInput'
      responses:
        '200':
          description: Status updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '404':
          description: Status not found
        '500':
          description: Internal server error

    delete:
      summary: Delete status
      description: Delete a contact status
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Status ID
          schema:
            type: string
      responses:
        '200':
          description: Status deleted successfully
        '401':
          description: Unauthorized
        '404':
          description: Status not found
        '500':
          description: Internal server error

  /api/group:
    get:
      summary: Get user groups
      description: Retrieve all groups for the authenticated user
      tags:
        - Groups
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Groups retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Group'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error
    post:
      summary: Create group
      description: Create a new user group
      tags:
        - Groups
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: Group name
      responses:
        '201':
          description: Group created successfully
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/group/{id}:
    get:
      summary: Get group
      description: Retrieve a specific group by ID
      tags:
        - Groups
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Group ID
      responses:
        '200':
          description: Group retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Group'
        '401':
          description: Unauthorized
        '404':
          description: Group not found
        '500':
          description: Internal server error
    put:
      summary: Update group
      description: Update an existing group
      tags:
        - Groups
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Group ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Group name
      responses:
        '200':
          description: Group updated successfully
        '401':
          description: Unauthorized
        '404':
          description: Group not found
        '500':
          description: Internal server error
    delete:
      summary: Delete group
      description: Delete a group
      tags:
        - Groups
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Group ID
      responses:
        '204':
          description: Group deleted successfully
        '401':
          description: Unauthorized
        '404':
          description: Group not found
        '500':
          description: Internal server error

  /api/group/{id}/members:
    get:
      summary: Get group members
      description: Retrieve all members of a group
      tags:
        - Groups
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Group ID
      responses:
        '200':
          description: Group members retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error
    post:
      summary: Add group member
      description: Add a user to a group
      tags:
        - Groups
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Group ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
              properties:
                userId:
                  type: string
                  description: User ID to add to the group
      responses:
        '201':
          description: Member added successfully
        '400':
          description: Missing userId field
        '401':
          description: Unauthorized
        '500':
          description: Internal server error
    delete:
      summary: Remove group member
      description: Remove a user from a group
      tags:
        - Groups
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Group ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
              properties:
                userId:
                  type: string
                  description: User ID to remove from the group
      responses:
        '204':
          description: Member removed successfully
        '400':
          description: Missing userId field
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/feedback:
    post:
      summary: Submit feedback
      description: Submit user feedback
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FeedbackInput'
      responses:
        '200':
          description: Feedback submitted successfully
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/clients/generate:
    get:
      summary: Get supported client languages
      description: Returns list of supported programming languages for client generation
      tags:
        - Clients
      responses:
        '200':
          description: Supported languages retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  supportedLanguages:
                    type: array
                    items:
                      type: string
                    example: [javascript, typescript, python, java, csharp, go]
                  description:
                    type: string
    post:
      summary: Generate client library
      description: Generate a client library for the InNotes API in the specified programming language
      tags:
        - Clients
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - language
              properties:
                language:
                  type: string
                  description: Target programming language
                  enum: [javascript, typescript, python, java, csharp, go, rust, php, ruby, swift, kotlin, dart]
                packageName:
                  type: string
                  default: innotes-client
                  description: Name for the generated package
                clientName:
                  type: string
                  default: InNotesClient
                  description: Name for the generated client class
      responses:
        '200':
          description: Client library generated successfully
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '400':
          description: Unsupported language
        '500':
          description: Failed to generate client library

  /api/features:
    get:
      summary: Get feature requests
      description: Retrieve all feature requests
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Feature requests retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Feature'
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

    post:
      summary: Create feature request
      description: Submit a new feature request
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FeatureInput'
      responses:
        '201':
          description: Feature request created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Feature'
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/features/vote:
    post:
      summary: Vote on feature request
      description: Vote for or against a feature request
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VoteInput'
      responses:
        '200':
          description: Vote submitted successfully
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/features/bug:
    post:
      summary: Report bug
      description: Submit a bug report
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BugReportInput'
      responses:
        '200':
          description: Bug report submitted successfully
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/features/comment:
    post:
      summary: Add comment to feature request
      description: Add a comment to an existing feature request
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - featureId
                - userId
                - content
              properties:
                featureId:
                  type: string
                  description: The ID of the feature request
                userId:
                  type: string
                  description: The user ID adding the comment
                content:
                  type: string
                  description: The comment content
      responses:
        '200':
          description: Comment added successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  userId:
                    type: string
                  userName:
                    type: string
                  content:
                    type: string
                  createdAt:
                    type: string
                    format: date-time
        '400':
          description: Bad request or cannot comment on completed features
        '401':
          description: Unauthorized
        '404':
          description: Feature not found
        '500':
          description: Internal server error

  /api/board/load-more:
    post:
      summary: Load more board items
      description: Paginated loading of additional contacts or jobs for a kanban board column
      tags:
        - Board
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
                - boardObjectType
                - statusId
                - page
                - pageSize
              properties:
                userId:
                  type: string
                  description: The user ID
                boardObjectType:
                  type: string
                  enum: [contact, job]
                  description: Type of objects on the board
                statusId:
                  type: integer
                  description: Status column to load items from
                page:
                  type: integer
                  description: Page number (1-indexed)
                pageSize:
                  type: integer
                  description: Number of items per page
                searchTerm:
                  type: string
                  description: Optional search filter
                tags:
                  type: array
                  items:
                    type: string
                  description: Optional tag filters
                showAll:
                  type: boolean
                  description: Whether to show all items including hidden ones
                hideClosedLostDays:
                  type: integer
                  description: Days after which to hide closed-lost items
      responses:
        '200':
          description: Items loaded successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  oneOf:
                    - $ref: '#/components/schemas/Contact'
                    - $ref: '#/components/schemas/Job'
        '400':
          description: Missing required parameters
        '401':
          description: Unauthorized
        '500':
          description: Internal server error

  /api/automation/callback:
    get:
      summary: Health check for automation callback
      description: Simple health check endpoint for the automation callback service
      tags:
        - Automation
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  endpoint:
                    type: string
                    example: automation-callback
    post:
      summary: Process automation workflow callback
      description: Called by n8n after workflow execution to report results. Requires a valid callback secret.
      tags:
        - Automation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
                - status
                - secret
              properties:
                userId:
                  type: string
                  description: The user ID whose automation ran
                status:
                  type: string
                  enum: [success, error]
                  description: Result status of the workflow
                jobsAdded:
                  type: integer
                  description: Number of jobs added during execution
                error:
                  type: string
                  description: Error message if status is error
                secret:
                  type: string
                  description: Callback authentication secret
      responses:
        '200':
          description: Callback processed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
        '400':
          description: Missing required fields
        '401':
          description: Unauthorized - Invalid secret
        '404':
          description: User automation config not found
        '500':
          description: Internal server error

  /api/automation/config:
    get:
      summary: Get AI model configuration
      description: Retrieve AI model settings for the automation workflow. Used by n8n to dynamically select models.
      tags:
        - Automation
      security:
        - bearerAuth: []
      responses:
        '200':
          description: AI configuration retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  models:
                    type: object
                    properties:
                      low:
                        type: string
                        description: Model for low complexity tasks (Agent 1)
                        example: gpt-4o-mini
                      medium:
                        type: string
                        description: Model for medium complexity tasks (Agent 2)
                        example: gpt-5-mini
                      high:
                        type: string
                        description: Model for high complexity tasks (Agent 3)
                        example: gpt-5-mini
        '401':
          description: Unauthorized - Invalid or missing authentication
        '500':
          description: Internal server error

  /api/admin/ai-models:
    get:
      summary: Get AI model settings
      description: Retrieve current AI model configuration for all complexity levels. Requires admin privileges.
      tags:
        - Admin
      security:
        - bearerAuth: []
      responses:
        '200':
          description: AI model settings retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  models:
                    type: object
                    properties:
                      low:
                        type: string
                        example: gpt-4o-mini
                      medium:
                        type: string
                        example: gpt-5-mini
                      high:
                        type: string
                        example: gpt-5-mini
                  options:
                    type: array
                    items:
                      type: object
                      properties:
                        value:
                          type: string
                        label:
                          type: string
                        inputCost:
                          type: number
                        outputCost:
                          type: number
        '401':
          description: Unauthorized
        '403':
          description: Forbidden - Admin access required
        '500':
          description: Internal server error
    put:
      summary: Update AI model settings
      description: Update AI model configuration for complexity levels. Requires admin privileges.
      tags:
        - Admin
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                low:
                  type: string
                  description: Model for low complexity tasks
                medium:
                  type: string
                  description: Model for medium complexity tasks
                high:
                  type: string
                  description: Model for high complexity tasks
      responses:
        '200':
          description: Settings updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  models:
                    type: object
                    properties:
                      low:
                        type: string
                      medium:
                        type: string
                      high:
                        type: string
        '400':
          description: Invalid input
        '401':
          description: Unauthorized
        '403':
          description: Forbidden - Admin access required
        '500':
          description: Internal server error

  /api/admin/cache:
    get:
      summary: Get cache statistics
      description: Retrieve Redis cache statistics. Requires admin privileges.
      tags:
        - Admin
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Cache statistics retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  cacheSize:
                    type: integer
                    description: Total cache size in bytes
                  hitRatio:
                    type: number
                    format: float
                    description: Cache hit ratio (0-1)
                  cacheSizeMB:
                    type: string
                    description: Cache size in megabytes
                  hitRatioPercent:
                    type: string
                    description: Cache hit ratio as percentage
                  timestamp:
                    type: string
                    format: date-time
                    description: Timestamp of the statistics
        '403':
          description: Forbidden - Admin access required
        '500':
          description: Internal server error
    post:
      summary: Manage cache
      description: Flush cache or reset statistics. Requires admin privileges.
      tags:
        - Admin
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
              properties:
                action:
                  type: string
                  enum: [flush, reset-stats]
                  description: Action to perform on the cache
      responses:
        '200':
          description: Action completed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    description: Success message
        '400':
          description: Invalid action
        '403':
          description: Forbidden - Admin access required
        '500':
          description: Internal server error

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

  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          description: User ID
        username:
          type: string
          description: Username
        email:
          type: string
          nullable: true
          description: User email
        name:
          type: string
          description: User full name
        status:
          type: string
          enum: [free, premium]
          description: User subscription status
        created_at:
          type: string
          format: date-time
          description: Account creation date
        subscription_id:
          type: string
          nullable: true
          description: Stripe subscription ID
        home_location:
          type: string
          nullable: true
          description: User's home location
        picture:
          type: string
          nullable: true
          description: Profile picture URL
        cv:
          type: string
          nullable: true
          description: CV/Resume content
        cv_updated_at:
          type: string
          format: date-time
          nullable: true
          description: CV last update timestamp
        job_preferences:
          type: string
          nullable: true
          description: JSON string of job preferences
        job_expiration_days:
          type: integer
          description: Days after which jobs expire
        hide_closed_lost_days:
          type: integer
          description: Days after which to hide closed/lost jobs

    Group:
      type: object
      properties:
        id:
          type: string
          description: Group ID
        ownerid:
          type: string
          description: Owner user ID
        name:
          type: string
          description: Group name

    ApiToken:
      type: object
      properties:
        id:
          type: string
          description: Token ID
        name:
          type: string
          description: Token name
        created_at:
          type: string
          format: date-time
          description: When the token was created
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: When the token expires
        last_used_at:
          type: string
          format: date-time
          nullable: true
          description: Last time the token was used
        is_active:
          type: boolean
          description: Whether the token is active
        scopes:
          type: array
          items:
            type: string
          description: Permissions granted to this token
        is_browser_extension:
          type: boolean
          description: Whether this is a browser extension token

    Contact:
      type: object
      properties:
        id:
          type: string
          description: Contact ID
        linkedin_user:
          type: string
          nullable: true
          description: LinkedIn username
        linkedin_key:
          type: string
          nullable: true
          description: LinkedIn profile key
        name:
          type: string
          nullable: true
          description: Contact name
        status_id:
          type: integer
          nullable: true
          description: Contact status ID
        note:
          type: string
          nullable: true
          description: Contact note
        tags:
          type: array
          items:
            $ref: '#/components/schemas/Tag'
          nullable: true
          description: Contact tags
        notes_count:
          type: integer
          description: Number of notes for this contact
        picture_url:
          type: string
          nullable: true
          description: Profile picture URL
        location:
          type: string
          nullable: true
          description: Contact location
        current_company:
          type: string
          nullable: true
          description: Current company
        created_at:
          type: string
          format: date-time
          description: Contact creation date
        updated_at:
          type: string
          format: date-time
          description: Contact last update date

    ContactInput:
      type: object
      required:
        - linkedin_key
      properties:
        linkedin_key:
          type: string
          description: LinkedIn profile key
        linkedin_user:
          type: string
          description: LinkedIn username
        name:
          type: string
          description: Contact name
        status_id:
          type: integer
          description: Contact status ID
        tags:
          type: array
          items:
            type: string
          description: Contact tags
        picture_url:
          type: string
          description: Profile picture URL
        location:
          type: string
          description: Contact location
        current_company:
          type: string
          description: Current company

    Job:
      type: object
      properties:
        id:
          type: string
          description: Job ID
        ext_id:
          type: string
          description: External ID
        name:
          type: string
          description: Job title
        company_name:
          type: string
          description: Company name
        location:
          type: string
          description: Job location
        url:
          type: string
          format: uri
          description: Job posting URL
        provider:
          type: string
          description: Job source provider
          enum:
            - linkedin
            - external
            - company
            - referral
            - indeed
            - glassdoor
            - jobsch
        description:
          type: string
          description: Job description
        status:
          type: string
          description: Application status
        status_id:
          type: integer
          description: Application status identifier
        notes:
          type: string
          description: Application notes
        tags:
          type: array
          items:
            type: string
          description: Tags associated with the job
        created_at:
          type: string
          format: date-time
          description: Job creation date
        updated_at:
          type: string
          format: date-time
          description: Job last update date

    JobInput:
      type: object
      properties:
        name:
          type: string
          description: Job title
        company_name:
          type: string
          description: Company name
        ext_id:
          type: string
          description: External ID
        location:
          type: string
          description: Job location
        url:
          type: string
          format: uri
          description: Job posting URL
        provider:
          type: string
          description: Job source provider
          enum:
            - linkedin
            - external
            - company
            - referral
            - indeed
            - glassdoor
            - jobsch
        description:
          type: string
          description: Job description
        status:
          type: string
          description: Application status
        status_id:
          type: integer
          description: Status identifier
        notes:
          type: string
          description: Application notes
        tags:
          type: array
          description: Tags associated with the job
          items:
            type: string

    Note:
      type: object
      properties:
        id:
          type: string
          description: Note ID
        text:
          type: string
          description: Note content
        ext_id:
          type: string
          description: External ID of related entity
        ext_table_name:
          type: string
          description: Table name of related entity
        flag_color:
          type: string
          nullable: true
          description: Flag color for the note
        created_at:
          type: string
          format: date-time
          description: Note creation date
        updated_at:
          type: string
          format: date-time
          description: Note last update date

    NoteInput:
      type: object
      required:
        - ext_id
      properties:
        text:
          type: string
          description: Note content
        ext_id:
          type: string
          description: External ID of related entity
        ext_table_name:
          type: string
          default: contacts
          description: Table name of related entity
        flag_color:
          type: string
          description: Flag color for the note

    SearchResult:
      type: object
      properties:
        id:
          type: string
          description: Note ID
        text:
          type: string
          description: Note content
        flag_color:
          type: string
          nullable: true
          description: Flag color
        last_update:
          type: string
          format: date-time
          description: Last update timestamp
        linkedin_user:
          type: string
          nullable: true
          description: Contact LinkedIn username
        linkedin_key:
          type: string
          nullable: true
          description: Contact LinkedIn key
        contact_name:
          type: string
          nullable: true
          description: Contact name
        picture_url:
          type: string
          nullable: true
          description: Contact picture URL

    Tag:
      type: object
      properties:
        id:
          type: string
          description: Tag ID
        name:
          type: string
          description: Tag name
        color:
          type: string
          description: Tag color

    Status:
      type: object
      properties:
        id:
          type: integer
          description: Status ID
        name:
          type: string
          description: Status name
        color:
          type: string
          description: Status color
        user_id:
          type: string
          description: User ID

    StatusInput:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Status name
        color:
          type: string
          description: Status color

    FeedbackInput:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: Feedback message
        type:
          type: string
          enum: [feedback, bug, feature]
          description: Feedback type

    Feature:
      type: object
      properties:
        id:
          type: string
          description: Feature ID
        title:
          type: string
          description: Feature title
        description:
          type: string
          description: Feature description
        votes:
          type: integer
          description: Number of votes
        status:
          type: string
          description: Feature status
        created_at:
          type: string
          format: date-time
          description: Feature creation date

    FeatureInput:
      type: object
      required:
        - title
        - description
      properties:
        title:
          type: string
          description: Feature title
        description:
          type: string
          description: Feature description

    VoteInput:
      type: object
      required:
        - feature_id
        - vote
      properties:
        feature_id:
          type: string
          description: Feature ID to vote on
        vote:
          type: string
          enum: [up, down]
          description: Vote direction

    BugReportInput:
      type: object
      required:
        - title
        - description
      properties:
        title:
          type: string
          description: Bug title
        description:
          type: string
          description: Bug description
        steps_to_reproduce:
          type: string
          description: Steps to reproduce the bug
        expected_behavior:
          type: string
          description: Expected behavior
        actual_behavior:
          type: string
          description: Actual behavior

    Error:
      type: object
      properties:
        error:
          type: string
          description: Error message

security:
  - bearerAuth: []
