openapi: 3.0.0
paths:
  /account/info:
    get:
      description: |-
        Returns information about the currently authenticated user within the current workspace.

        **What is returned:**
        - Basic profile information (name, email, avatar)
        - Workspace and role information
        - User type (Employee or Organization Member)
        - All granted permissions
        - Account preferences (language, email notifications, workspace color)
        - Identifiers for employee/member accounts

        This endpoint is useful for determining the current user's identity, permissions, and account type before making other API calls.
      operationId: AccountController_info
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountInfoResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: User not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get current user information
      tags:
        - account
      x-required-scopes:
        - api:read
  /account/employee/data:
    get:
      description: |-
        Returns detailed employee profile information including personal data, contact information, and address.

        **What is returned:**
        - Personal information (name, email, phone, birth date)
        - Professional details (title, degree)
        - Complete address information
        - Digital signature file URL (if uploaded)

        **Note:** This endpoint is only available for employee accounts (not organization members). To check if a user is an employee, use GET /api/public/account/info and check the `type` field.
      operationId: AccountController_employeeData
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeDataResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee data not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get employee profile data
      tags:
        - account
        - employee-only
      x-required-scopes:
        - api:read
    post:
      description: |-
        Partially updates employee profile information. All fields are optional - only provided fields will be updated.

        **What can be updated:**
        - Personal information (name, email, phone, birth date)
        - Professional details (title, degree)
        - Complete address information
        - Avatar image (upload file first via POST /api/public/workspace/upload, then use file ID)
        - Digital signature (upload file first via POST /api/public/workspace/upload, then use file ID)

        **Note:** This endpoint is only available for employee accounts. To upload files (avatar or signature), first call POST /api/public/workspace/upload to upload the file and get a file ID, then use that ID in the `avatarId` or `signatureFileId` field.
      operationId: AccountController_updateEmployeeData
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEmployeeDataRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update employee profile data
      tags:
        - account
        - employee-only
      x-required-scopes:
        - api:write
  /account/workspace-color:
    post:
      description: Updates the user's workspace color theme preference. The workspace color affects the visual theme of the application interface for this user.
      operationId: AccountController_updateWorkspaceColor
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWorkspaceColorRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: User not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update workspace color preference
      tags:
        - account
      x-required-scopes:
        - api:write
  /account/language:
    post:
      description: 'Updates the user''s preferred language for the application interface. Supported languages: `en` (English) or `de` (German). This affects the language of UI elements, notifications, and other system messages.'
      operationId: AccountController_updateLanguage
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateLanguageRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: User not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update preferred language
      tags:
        - account
      x-required-scopes:
        - api:write
  /account/email-notifications:
    post:
      description: Enables or disables email notifications for the current user. When disabled, the user will not receive email notifications from the system, but will still see notifications within the application.
      operationId: AccountController_updateEmailNotifications
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEmailNotificationsRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: User not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update email notification preferences
      tags:
        - account
      x-required-scopes:
        - api:write
  /account/employee/bank-data:
    get:
      description: |-
        Returns employee bank account details including IBAN, BIC, bank name, and account owner name.

        **Note:** This endpoint is only available for employee accounts. Bank data is typically used for payroll processing.
      operationId: AccountController_getBankData
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankDataResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee bank data not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get employee bank account information
      tags:
        - account
        - employee-only
      x-required-scopes:
        - api:read
    post:
      description: |-
        Updates employee bank account details. All fields are optional - only provided fields will be updated.

        **What can be updated:**
        - IBAN (International Bank Account Number)
        - BIC (Bank Identifier Code)
        - Bank name
        - Account owner name

        **Note:** This endpoint is only available for employee accounts. Bank data is typically used for payroll processing.
      operationId: AccountController_updateBankData
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateBankDataRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update employee bank account information
      tags:
        - account
        - employee-only
      x-required-scopes:
        - api:write
  /account/org-member/data:
    get:
      description: |-
        Returns detailed organization member profile information including personal data, contact information, and address.

        **What is returned:**
        - Personal information (name, email, phone, birth date)
        - Professional details (title, degree, position)
        - Complete address information

        **Note:** This endpoint is only available for organization member accounts (not employees). Organization members are external contacts (e.g., clients, partners) who have access to the workspace.
      operationId: AccountController_getOrgMemberData
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrgMemberDataResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Organization member data not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization member profile data
      tags:
        - account
        - org-member-only
      x-required-scopes:
        - api:read
    post:
      description: |-
        Partially updates organization member profile information. All fields are optional - only provided fields will be updated.

        **What can be updated:**
        - Personal information (name, email, phone, birth date)
        - Professional details (title, degree, position)
        - Complete address information
        - Avatar image (upload file first via POST /api/public/workspace/upload, then use file ID)

        **Note:** This endpoint is only available for organization member accounts. Organization members are external contacts (e.g., clients, partners) who have access to the workspace. To upload an avatar, first call POST /api/public/workspace/upload to upload the file and get a file ID, then use that ID in the `avatarId` field.
      operationId: AccountController_updateOrgMemberData
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOrgMemberDataRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Organization member not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization member profile data
      tags:
        - account
        - org-member-only
      x-required-scopes:
        - api:write
  /account/employee/vacations/future:
    get:
      description: |-
        Returns all future vacation requests for the current employee.

        **What is returned:**
        - Vacation request ID
        - Start and end dates
        - Current status (Pending, Approved, or Rejected)

        **Note:** This endpoint is only available for employee accounts. Future vacations are those with a start date in the future.
      operationId: AccountController_getFutureVacations
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FutureVacationResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get future vacation requests
      tags:
        - account
        - employee-only
        - vacations
      x-required-scopes:
        - api:read
  /account/employee/vacations/stats:
    get:
      description: |-
        Returns comprehensive vacation statistics for the current employee for a specific year.

        **What is returned:**
        - Vacation entitlement by contract
        - Days periods (when vacation days were added/deducted)
        - Pending vacation days (requests awaiting approval)
        - Approved future vacation days
        - Approved past vacation days (already taken)
        - Vacation days from overtime conversion
        - Added/deducted days (manual adjustments)
        - Total vacation days available
        - Total used vacation days
        - Total remaining vacation days
        - Potential vacation days (including pending requests)

        **Note:** This endpoint is only available for employee accounts. If no year is provided in the query parameter, the current year is used.
      operationId: AccountController_getVacationStats
      parameters:
        - name: year
          required: false
          in: query
          description: Year for vacation stats
          schema:
            example: 2024
            type: number
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VacationStatsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get vacation statistics
      tags:
        - account
        - employee-only
        - vacations
      x-required-scopes:
        - api:read
  /account/employee/vacations/history:
    get:
      description: |-
        Returns a paginated list of all vacation requests for the current employee.

        **What is returned:**
        - Complete history of all vacation requests (past and future)
        - Request dates, status, and approval information
        - Reason for vacation (if provided)
        - Who approved/rejected and when

        **Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.
      operationId: AccountController_getVacationHistory
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Vacation request ID
            - **dateFrom** (date): Vacation start date
            - **dateTo** (date): Vacation end date
            - **status** (set): Vacation request status
            - **employeeId** (string): Employee ID
            - **statusChangedAt** (date): Status change timestamp
            - **statusChangedBy** (string): User ID who changed status

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Vacation request ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **dateFrom** (date): Vacation start date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **dateTo** (date): Vacation end date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **status** (set): Vacation request status (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **statusChangedAt** (date): Status change timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **statusChangedBy** (string): User ID who changed status (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Vacation request ID
            - **dateFrom**: Vacation start date
            - **dateTo**: Vacation end date
            - **status**: Vacation request status
            - **employeeId**: Employee ID
            - **statusChangedAt**: Status change timestamp
            - **statusChangedBy**: User ID who changed status

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** dateFrom (desc)
          schema:
            example: '[{"field":"dateFrom","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, dateFrom, dateTo, status, employeeId, statusChangedAt, statusChangedBy, reason
          schema:
            example: id,dateFrom
            type: array
            items: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VacationHistoryGridResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get vacation request history
      tags:
        - account
        - employee-only
        - vacations
      x-required-scopes:
        - api:read
  /account/employee/vacations/compensation:
    get:
      description: |-
        Returns a paginated list of all vacation compensation entries for the current employee.

        **What is vacation compensation?**
        Vacation compensation includes:
        - Unused vacation days carried over to the next year
        - Vacation days paid out instead of taken
        - Vacation days converted from overtime hours
        - Manual adjustments (added or deducted days)

        **What is returned:**
        - Compensation type
        - Year and target year (for carryovers)
        - Number of days
        - Creation date

        **Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.
      operationId: AccountController_getVacationCompensation
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Compensation entry ID
            - **employeeId** (string): Employee ID
            - **type** (set): Compensation type
            - **year** (number): Compensation year
            - **toYear** (number): Target year for compensation
            - **days** (number): Number of compensation days
            - **createdAt** (date): Creation timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Compensation entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Compensation type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **year** (number): Compensation year (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **toYear** (number): Target year for compensation (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **days** (number): Number of compensation days (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Compensation entry ID
            - **employeeId**: Employee ID
            - **type**: Compensation type
            - **year**: Compensation year
            - **toYear**: Target year for compensation
            - **days**: Number of compensation days
            - **createdAt**: Creation timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, employeeId, type, year, toYear, days, createdAt
          schema:
            example: id,employeeId
            type: array
            items: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VacationCompensationGridResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get vacation compensation history
      tags:
        - account
        - employee-only
        - vacations
      x-required-scopes:
        - api:read
  /account/employee/vacations/request:
    post:
      description: |-
        Creates a new vacation request for the current employee.

        **How vacation requests work:**
        1. Employee submits a vacation request with start and end dates
        2. Request status is set to "Pending"
        3. Manager reviews and approves or rejects the request
        4. System automatically checks for scheduling conflicts

        **Validation rules:**
        - Start date cannot be more than one month in the past
        - End date must be after start date
        - System checks for overlapping events (other vacations, sick days, etc.)

        **Note:** This endpoint is only available for employee accounts. Only pending vacations can be modified or canceled.
      operationId: AccountController_requestVacation
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestVacationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      dateFrom:
                        type: array
                        items:
                          type: string
                        example:
                          - Vacation start date cannot be more than one month in the past.
                      dateTo:
                        type: array
                        items:
                          type: string
                        example:
                          - Vacation end date must be after start date.
                      globalErrors:
                        type: array
                        items:
                          type: string
                        example:
                          - There are already other events for this period. Please select another one.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Request vacation
      tags:
        - account
        - employee-only
        - vacations
      x-required-scopes:
        - api:write
  /account/employee/sick-days:
    get:
      description: |-
        Returns a paginated list of all sick day records for the current employee.

        **What is returned:**
        - Start and end dates of sick periods
        - Comments/notes about the illness
        - Attached medical certificate file IDs (if provided)

        **Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.
      operationId: AccountController_getSickDays
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Sickness entry ID
            - **dateFrom** (date): Sick leave start date
            - **dateTo** (date): Sick leave end date
            - **employeeId** (string): Employee ID

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Sickness entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **dateFrom** (date): Sick leave start date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **dateTo** (date): Sick leave end date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Sickness entry ID
            - **dateFrom**: Sick leave start date
            - **dateTo**: Sick leave end date
            - **employeeId**: Employee ID

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** dateFrom (desc)
          schema:
            example: '[{"field":"dateFrom","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, dateFrom, dateTo, employeeId, comment, attachedFilesIds
          schema:
            example: id,dateFrom
            type: array
            items: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SicknessGridResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Employee not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get sick days history
      tags:
        - account
        - employee-only
        - sick-days
      x-required-scopes:
        - api:read
  /account/employee/sick-days/request:
    post:
      description: |-
        Creates a new sick days record for the current employee.

        **How sick days work:**
        1. Employee reports sick days with start and end dates
        2. Optional comment can be added
        3. Medical certificates can be attached (upload files first, then provide file IDs)
        4. System automatically checks for overlapping events

        **To upload medical certificates:**
        1. Call POST /api/public/workspace/upload for each file
        2. Get the file ID from the response
        3. Include the file IDs in the `attachedFilesIds` array

        **Note:** This endpoint is only available for employee accounts. The system validates that there are no overlapping events (vacations, other sick days, etc.) for the specified period.
      operationId: AccountController_requestSickness
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestSicknessDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      dateFrom:
                        type: array
                        items:
                          type: string
                        example:
                          - Date from is required.
                      dateTo:
                        type: array
                        items:
                          type: string
                        example:
                          - Date to is required.
                      globalErrors:
                        type: array
                        items:
                          type: string
                        example:
                          - There are already other events for this period. Please select another one.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Request sick days
      tags:
        - account
        - employee-only
        - sick-days
      x-required-scopes:
        - api:write
  /account/journal:
    get:
      description: |-
        Returns a paginated list of journal entries for the current user.

        **What is a Journal?**
        The journal is a personal note-taking system where users can document:
        - Personal notes and observations
        - Feedback and conversations
        - Goal agreements and reminders
        - Mood tracking (Happy, Neutral, Sad)

        **What is returned:**
        - Entry content (formatted as HTML)
        - Creation date and creator
        - Mood indicator
        - Reminder date (if set)

        **Note:** Available for both employees and organization members. Supports server-side pagination, sorting, and filtering via grid query parameters.
      operationId: AccountController_getJournal
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Journal entry ID
            - **createdAt** (date): Journal entry creation timestamp
            - **createdBy** (string): Creator user ID
            - **mood** (set): Journal entry mood
            - **reminder** (date): Reminder date

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Journal entry ID
            - **createdAt**: Journal entry creation timestamp
            - **createdBy**: Creator user ID
            - **mood**: Journal entry mood
            - **reminder**: Reminder date

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, createdAt, createdBy, mood, reminder, body
          schema:
            example: id,createdAt
            type: array
            items: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JournalGridResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get journal entries
      tags:
        - account
        - journal
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new journal entry for the current user.

        **What is a Journal Entry?**
        A journal entry is a personal note that can include:
        - Formatted text content (HTML or Markdown)
        - Mood indicator (Happy 😊, Neutral 😐, or Sad 😢)
        - Optional reminder date for follow-up

        **Use cases:**
        - Document personal notes and observations
        - Track feedback and conversations
        - Set reminders for goal reviews
        - Mood tracking over time

        **Note:** Available for both employees and organization members. The body field accepts HTML or Markdown input and is stored internally in IDoc format.
      operationId: AccountController_createJournal
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JournalEntryResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      body:
                        type: array
                        items:
                          type: string
                        example:
                          - Body is required.
                      reminder:
                        type: array
                        items:
                          type: string
                        example:
                          - Reminder must be a valid ISO8601 date.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create journal entry
      tags:
        - account
        - journal
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - createdAt
        allowedFields:
          - id
          - createdAt
          - createdBy
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    put:
      description: |-
        Updates an existing journal entry. All fields can be updated.

        **What can be updated:**
        - Entry content (body)
        - Mood indicator
        - Reminder date

        **Note:** You can only update your own journal entries. The body field accepts HTML or Markdown input and is stored internally in IDoc format.
      operationId: AccountController_updateJournal
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JournalEntryResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      id:
                        type: array
                        items:
                          type: string
                        example:
                          - ID is required for updates.
                      body:
                        type: array
                        items:
                          type: string
                        example:
                          - Body is required.
                      reminder:
                        type: array
                        items:
                          type: string
                        example:
                          - Reminder must be a valid ISO8601 date.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update journal entry
      tags:
        - account
        - journal
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - createdAt
        allowedFields:
          - id
          - createdAt
          - createdBy
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /account/journal/{id}:
    patch:
      description: |-
        Updates only the provided fields of an existing journal entry. Fields not provided will remain unchanged.

        **What can be updated:**
        - Entry content (body) - optional
        - Mood indicator - optional
        - Reminder date - optional

        **Note:** You can only update your own journal entries. The body field accepts HTML or Markdown input and is stored internally in IDoc format.
      operationId: AccountController_patchJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JournalEntryResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      body:
                        type: array
                        items:
                          type: string
                        example:
                          - Body is required.
                      reminder:
                        type: array
                        items:
                          type: string
                        example:
                          - Reminder must be a valid ISO8601 date.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update journal entry
      tags:
        - account
        - journal
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - createdAt
        allowedFields:
          - id
          - createdAt
          - createdBy
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Permanently deletes a journal entry.

        **Note:** You can only delete your own journal entries. This action cannot be undone.
      operationId: AccountController_deleteJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      id:
                        type: array
                        items:
                          type: string
                        example:
                          - Journal entry not found.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete journal entry
      tags:
        - account
        - journal
      x-required-scopes:
        - api:write
  /account/employee/vacations/{id}:
    put:
      description: |-
        Updates an existing vacation request. Only pending vacation requests can be updated.

        **What can be updated:**
        - Start date
        - End date

        **Validation rules:**
        - Start date cannot be more than one month in the past
        - End date must be after start date
        - System checks for overlapping events

        **Note:** This endpoint is only available for employee accounts. Once a vacation is approved or rejected, it cannot be updated. To cancel an approved vacation, use DELETE /api/public/account/employee/vacations/:id.
      operationId: AccountController_updateVacation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateVacationRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      dateFrom:
                        type: array
                        items:
                          type: string
                        example:
                          - Vacation start date cannot be more than one month in the past.
                      dateTo:
                        type: array
                        items:
                          type: string
                        example:
                          - Vacation end date must be after start date.
                      globalErrors:
                        type: array
                        items:
                          type: string
                        example:
                          - There are already other events for this period. Please select another one.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update vacation request
      tags:
        - account
        - employee-only
        - vacations
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Cancels an existing vacation request.

        **When can vacations be canceled:**
        - Pending vacation requests (awaiting approval)
        - Future approved vacations (not yet started)

        **Note:** This endpoint is only available for employee accounts. Past vacations or vacations that have already started cannot be canceled.
      operationId: AccountController_cancelVacation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Vacation not found or cannot be canceled
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Cancel vacation request
      tags:
        - account
        - employee-only
        - vacations
      x-required-scopes:
        - api:write
  /account/employee/sick-days/{id}:
    put:
      description: |-
        Updates an existing sick days record.

        **What can be updated:**
        - Start and end dates
        - Comment/notes
        - Attached medical certificate file IDs

        **To update medical certificates:**
        1. Upload new files via POST /api/public/workspace/upload
        2. Get file IDs from responses
        3. Include all file IDs (old and new) in `attachedFilesIds` array

        **Note:** This endpoint is only available for employee accounts. The system validates that there are no overlapping events for the specified period.
      operationId: AccountController_updateSickness
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSicknessRequest'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      dateFrom:
                        type: array
                        items:
                          type: string
                        example:
                          - Date from is required.
                      dateTo:
                        type: array
                        items:
                          type: string
                        example:
                          - Date to is required.
                      attachedFilesIds:
                        type: array
                        items:
                          type: string
                        example:
                          - Attached files IDs are required.
                      globalErrors:
                        type: array
                        items:
                          type: string
                        example:
                          - There are already other events for this period. Please select another one.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update sick days record
      tags:
        - account
        - employee-only
        - sick-days
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Permanently deletes a sick days record.

        **Note:** This endpoint is only available for employee accounts. This action cannot be undone. Use with caution.
      operationId: AccountController_deleteSickness
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Sick days record not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete sick days record
      tags:
        - account
        - employee-only
        - sick-days
      x-required-scopes:
        - api:write
  /account/overtime/stats:
    get:
      description: |-
        Returns comprehensive overtime statistics for the current employee.

        **What is Overtime?**
        Overtime is automatically calculated from daily time tracking. If an employee works more hours than their contract specifies, positive overtime is created. If they work fewer hours, the balance goes negative.

        **What is returned:**
        - **Overtime balance**: Current balance of all plus or minus hours
        - **Total recorded**: Total sum of all logged working hours
        - **Contract**: Weekly or daily target hours according to the contract
        - **Total compensated**: Sum of hours that have already been balanced out
        - **Converted to vacation**: Hours that were turned into extra vacation days
        - **Paid in cash**: Overtime hours that were paid out
        - **Ignored**: Overtime marked as expired or without compensation

        **Note:** This endpoint is only available for employee accounts.
      operationId: AccountController_getOvertimeStats
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OvertimeStatsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get overtime statistics
      tags:
        - account
        - employee-only
        - overtime
      x-required-scopes:
        - api:read
  /account/overtime/compensation-history:
    get:
      description: |-
        Returns a paginated list of all overtime compensation entries for the current employee.

        **What is Overtime Compensation?**
        Overtime can be compensated in three ways:
        - **Paid in cash**: Overtime is paid out
        - **Turned into vacation days**: Overtime is swapped for days off
        - **Ignored**: Overtime is removed without compensation

        **What is returned:**
        - Compensation type
        - Date of compensation
        - Number of hours compensated
        - Related information (e.g., target year for vacation conversions)

        **Note:** This endpoint is only available for employee accounts. Supports server-side pagination, sorting, and filtering via grid query parameters.
      operationId: AccountController_getOvertimeCompensationHistory
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Overtime compensation ID
            - **employeeId** (string): Employee ID
            - **type** (set): Compensation type
            - **year** (number): Compensation year
            - **toYear** (number): Target year
            - **hours** (number): Number of overtime hours
            - **createdAt** (date): Creation timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Overtime compensation ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Compensation type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **year** (number): Compensation year (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **toYear** (number): Target year (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **hours** (number): Number of overtime hours (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Overtime compensation ID
            - **employeeId**: Employee ID
            - **type**: Compensation type
            - **year**: Compensation year
            - **toYear**: Target year
            - **hours**: Number of overtime hours
            - **createdAt**: Creation timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, employeeId, type, year, toYear, hours, createdAt
          schema:
            example: id,employeeId
            type: array
            items: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OvertimeCompensationGridResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get overtime compensation history
      tags:
        - account
        - employee-only
        - overtime
      x-required-scopes:
        - api:read
  /account/notifications:
    get:
      description: |-
        Returns a paginated list of notifications for the current user.

        **What are Notifications?**
        Notifications inform users about important events in the system:
        - Project updates
        - Task assignments
        - Vacation request approvals/rejections
        - Comments and mentions
        - System announcements
        - And more...

        **What is returned:**
        - Notification type and entity information
        - Human-readable message
        - Read/unread status
        - Creation date
        - Creator information

        **Query parameters:**
        - `unreadOnly`: Set to `true` to return only unread notifications

        **Note:** Supports server-side pagination, sorting, and filtering via grid query parameters.
      operationId: AccountController_getNotifications
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Notification ID
            - **type** (set): Notification type
            - **entity** (string): Related entity type
            - **entityId** (string): Related entity ID
            - **creatorId** (string): Creator user ID
            - **isRead** (boolean): Read status
            - **createdAt** (date): Creation timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Notification ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Notification type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **entity** (string): Related entity type (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **entityId** (string): Related entity ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **creatorId** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **isRead** (boolean): Read status (boolean filter). Available comparisons: equal, not_equal
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Notification ID
            - **type**: Notification type
            - **entity**: Related entity type
            - **entityId**: Related entity ID
            - **creatorId**: Creator user ID
            - **isRead**: Read status
            - **createdAt**: Creation timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, type, entity, entityId, creatorId, isRead, createdAt, message
          schema:
            example: id,type
            type: array
            items: {}
      responses:
        '200':
          description: Paginated notifications with human-readable messages
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        type:
                          type: string
                        entity:
                          type: string
                        entityId:
                          type: string
                        creatorId:
                          type: string
                        isRead:
                          type: boolean
                        createdAt:
                          type: string
                          format: date-time
                        message:
                          type: string
                          description: Human-readable message
                  total:
                    type: number
                  page:
                    type: number
                  pageSize:
                    type: number
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      unreadOnly:
                        type: array
                        items:
                          type: string
                        example:
                          - unreadOnly must be a boolean value
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get user notifications
      tags:
        - account
        - notifications
      x-required-scopes:
        - api:read
  /account/notifications/{id}/read:
    post:
      description: |-
        Marks a specific notification as read for the current user.

        **What happens:**
        - The notification's `isRead` status is set to `true`
        - The notification will no longer appear in unread-only queries
        - The read status persists across sessions

        **Note:** You can only mark your own notifications as read.
      operationId: AccountController_markNotificationAsRead
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      id:
                        type: array
                        items:
                          type: string
                        example:
                          - Notification not found.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Mark notification as read
      tags:
        - account
        - notifications
      x-required-scopes:
        - api:write
  /account/notifications/read-all:
    post:
      description: |-
        Marks all unread notifications as read for the current user.

        **What happens:**
        - All unread notifications for the current user are marked as read
        - Returns the count of notifications that were marked as read
        - Useful for bulk "mark all as read" functionality

        **Response includes:**
        - `success`: Boolean indicating operation success
        - `count`: Number of notifications marked as read
      operationId: AccountController_markAllNotificationsAsRead
      parameters: []
      responses:
        '200':
          description: Number of notifications marked as read
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  count:
                    type: number
                    example: 5
                    description: Number of notifications marked as read
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Mark all notifications as read
      tags:
        - account
        - notifications
      x-required-scopes:
        - api:write
  /agent-chats:
    get:
      description: Returns paginated in-app agent chats for the authenticated user in the current workspace. Pinned chats are returned first, ordered by most recently pinned. Pass `q` to search by chat title instead of browsing history.
      operationId: AgentChatsController_list
      parameters:
        - name: page
          required: false
          in: query
          schema:
            minimum: 0
            default: 0
            type: number
        - name: pageSize
          required: false
          in: query
          schema:
            minimum: 1
            maximum: 100
            default: 30
            type: number
        - name: q
          required: false
          in: query
          description: Optional title search query
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentChatListPageDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List agent chats
      tags:
        - agent-chats
      x-required-scopes:
        - api:read
  /agent-chats/{chatId}:
    get:
      description: Returns sanitized transcript events and metadata for a single in-app agent chat owned by the authenticated user.
      operationId: AgentChatsController_getChat
      parameters:
        - name: chatId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Chat details retrieved successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Chat not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get agent chat details
      tags:
        - agent-chats
      x-required-scopes:
        - api:read
    patch:
      description: Renames an in-app agent chat owned by the authenticated user.
      operationId: AgentChatsController_renameChat
      parameters:
        - name: chatId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RenameAgentChatDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentChatSuccessResponseDto'
        '400':
          description: Invalid title
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Chat not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Rename an agent chat
      tags:
        - agent-chats
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
        allowedFields:
          - success
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: Deletes an in-app agent chat owned by the authenticated user.
      operationId: AgentChatsController_deleteChat
      parameters:
        - name: chatId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentChatSuccessResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Chat not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete an agent chat
      tags:
        - agent-chats
      x-required-scopes:
        - api:write
  /agent-chats/{chatId}/pin:
    patch:
      description: Pins or unpins an in-app agent chat for the authenticated user. Pin state is per-user and only affects the caller's chat history.
      operationId: AgentChatsController_pinChat
      parameters:
        - name: chatId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,pinnedAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PinAgentChatDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentChatPinResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Chat not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Pin or unpin an agent chat
      tags:
        - agent-chats
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - pinnedAt
        allowedFields:
          - success
          - pinnedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /agent-connectors/setup/claim:
    post:
      description: Used by external agent runtimes such as OpenClaw to finish agent connector setup. The setup token is generated in the authenticated Leadtime app, but this claim endpoint does not require Bearer auth. Claiming the token enables the agent webhooks/sessions connection, creates a fresh agent PAT, stores the webhook URL, and returns one-time secrets to the installer.
      operationId: AgentConnectorsController_claim
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClaimAgentConnectorSetupDto'
      responses:
        '201':
          description: Connector setup was claimed and one-time secrets were returned.
        '401':
          description: Setup token is invalid, expired, or already used.
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      summary: Claim a one-time agent connector setup token
      tags:
        - agent-connectors
  /agent-sessions/{runId}/context:
    get:
      operationId: AgentSessionsController_getContext
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get agent session context for a self-hosted wrapper
      tags:
        - agent-sessions
      x-required-scopes:
        - api:read
  /agent-sessions/{runId}/activities:
    post:
      operationId: AgentSessionsController_appendActivity
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AppendAgentSessionActivityDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:write
        - bearer: []
      summary: Append activity to an agent session
      tags:
        - agent-sessions
      x-required-scopes:
        - api:write
    get:
      operationId: AgentSessionsController_listActivities
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:read
        - bearer: []
      summary: List agent session activities
      tags:
        - agent-sessions
      x-required-scopes:
        - api:read
  /agent-sessions/{runId}/status:
    patch:
      operationId: AgentSessionsController_updateStatus
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAgentSessionStatusDto'
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update agent session status
      tags:
        - agent-sessions
      x-required-scopes:
        - api:write
  /agent-sessions/{runId}:
    get:
      operationId: AgentSessionsController_getSession
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get an agent session
      tags:
        - agent-sessions
      x-required-scopes:
        - api:read
  /agent-sessions/{runId}/transcript:
    get:
      operationId: AgentSessionsController_getTranscript
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get sanitized agent session transcript
      tags:
        - agent-sessions
      x-required-scopes:
        - api:read
  /agent-sessions/{runId}/diagnostics:
    get:
      description: Returns session metadata, transcript events, tool calls/results, errors, and status summary. Full raw provider payloads require agent management permission.
      operationId: AgentSessionsController_getDiagnostics
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
        - name: includeRaw
          required: false
          in: query
          description: Include full raw provider payload fields. Requires agent management permission.
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get diagnostic details for an agent session
      tags:
        - agent-sessions
      x-required-scopes:
        - api:read
  /agent-sessions/{runId}/webhook-deliveries:
    get:
      operationId: AgentSessionsController_listWebhookDeliveries
      parameters:
        - name: runId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:read
        - bearer: []
      summary: List agent session webhook deliveries
      tags:
        - agent-sessions
      x-required-scopes:
        - api:read
  /account/favorites/toggle:
    post:
      description: |-
        Toggles a favorite for a specific entity (task, project, organization, or command). If the entity is currently favorited, it will be removed from favorites. If it is not favorited, it will be added to favorites.

        **Entity Types:**
        - `Project`: Favorite a project
        - `Task`: Favorite a task
        - `Organization`: Favorite an organization
        - `Command`: Favorite a command (for command palette)

        Favorites are user-specific and workspace-scoped. Each user maintains their own list of favorites. When adding a new favorite, it is automatically appended to the end of the favorites list. The response indicates whether the entity is now favorited (`isFavorite: true`) or not (`isFavorite: false`).
      operationId: FavoritesController_toggle
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: isFavorite
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ToggleFavoriteRequestDto'
      responses:
        '200':
          description: Favorite status toggled successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ToggleFavoriteResponseDto'
        '400':
          description: Invalid entity ID or entity type
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Toggle favorite on/off
      tags:
        - account
        - favorites
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - isFavorite
        allowedFields:
          - isFavorite
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /account/favorites:
    get:
      description: |-
        Returns all favorites for the authenticated user, sorted by custom sort order (if set) or creation date. Each favorite includes entity-specific metadata:

        **Task favorites:** Include `shortNumber` and `title`
        **Project favorites:** Include `shortNumber`, `shortName` (e.g., "ORG-123"), and `name`
        **Organization favorites:** Include `shortName` and `name`
        **Command favorites:** Include `id` and `name`

        Deleted entities are automatically excluded from the results. The favorites list is user-specific and workspace-scoped. Use the toggle endpoint to add or remove favorites.
      operationId: FavoritesController_list
      parameters: []
      responses:
        '200':
          description: Favorites list retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FavoritesListResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get list of favorites
      tags:
        - account
        - favorites
      x-required-scopes:
        - api:read
  /administration/form-templates-available:
    get:
      description: 'Same data as GET `/workspace/form-templates`: categories plus template list items (including each template `id`). Use a template `id` as `templateId` in POST `/tasks/{identifier}/form-instances`. Prefer GET `/workspace/form-templates` for new integrations; field schemas are available from GET `/workspace/form-templates/{id}`.'
      operationId: FormTemplatesController_getFormTemplatesAvailable
      parameters: []
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List form categories and templates (read-only, for attaching forms to tasks)
      tags:
        - administration
        - form-templates
      x-required-scopes:
        - api:read
  /administration/form-categories:
    get:
      operationId: FormTemplatesController_getFormCategories
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FormCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List form categories
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: FormTemplatesController_createFormCategory
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFormCategoryDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create form category
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-categories/{id}:
    put:
      operationId: FormTemplatesController_updateFormCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateFormCategoryDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update form category
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: FormTemplatesController_deleteFormCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete form category
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-categories/sort:
    post:
      operationId: FormTemplatesController_sortFormCategories
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortFormCategoriesDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder form categories
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-templates/sort:
    post:
      operationId: FormTemplatesController_sortFormTemplates
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortFormTemplatesDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder form templates
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-templates/{templateId}/fields:
    get:
      operationId: FormTemplatesController_getFormTemplateFields
      parameters:
        - name: templateId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FormTemplateFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List fields for a form template
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: FormTemplatesController_createFormTemplateField
      parameters:
        - name: templateId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFormTemplateFieldDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create form template field
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-templates/{templateId}/fields/sort:
    post:
      operationId: FormTemplatesController_sortFormTemplateFields
      parameters:
        - name: templateId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortFormTemplateFieldsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder fields on a form template
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-templates/{templateId}/fields/{fieldId}:
    put:
      operationId: FormTemplatesController_updateFormTemplateField
      parameters:
        - name: templateId
          required: true
          in: path
          schema:
            type: string
        - name: fieldId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateFormTemplateFieldDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update form template field
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: FormTemplatesController_deleteFormTemplateField
      parameters:
        - name: templateId
          required: true
          in: path
          schema:
            type: string
        - name: fieldId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete form template field
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-templates:
    get:
      operationId: FormTemplatesController_getFormTemplates
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FormTemplateListItemResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List form templates
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: FormTemplatesController_createFormTemplate
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFormTemplateDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create form template
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/form-templates/{id}:
    get:
      operationId: FormTemplatesController_getFormTemplate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormTemplateDetailResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get form template with fields
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      operationId: FormTemplatesController_updateFormTemplate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateFormTemplateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update form template
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: FormTemplatesController_deleteFormTemplate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete form template
      tags:
        - administration
        - form-templates
      x-required-permissions:
        - formTemplates.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /attendance:
    post:
      description: |-
        Creates a new attendance record or updates an existing one for the current employee.

        **What is Attendance Tracking?**
        Attendance tracking records when employees clock in and clock out (check in/check out). It captures the start and end times of work periods, allowing the system to:
        - Calculate total hours worked
        - Track presence and absence
        - Monitor team availability
        - Integrate with time tracking and overtime calculations

        **What can be recorded:**
        - **Date**: The work date
        - **Start time** (timeFrom): When the employee clocked in (required, format: HH:mm)
        - **End time** (timeTo): When the employee clocked out (optional, format: HH:mm)
        - **Mood**: Employee mood for the day (Good, Neutral, Bad)
        - **Comment**: Optional notes about the workday

        **How it works:**
        - If an attendance record already exists for the date, it will be updated
        - If no record exists, a new one will be created
        - Total hours are automatically calculated from start and end times
        - The system validates that attendance doesn't overlap with vacation or sick days

        **Note:** This endpoint is only available for employee accounts. Attendance tracking must be enabled in the workspace settings.
      operationId: AttendanceController_createOrUpdate
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: date,timeFrom,timeTo,totalHours,mood,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttendanceRequestDto'
      responses:
        '200':
          description: Attendance record created or updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttendanceResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      date:
                        type: array
                        items:
                          type: string
                        example:
                          - Date is required.
                      timeFrom:
                        type: array
                        items:
                          type: string
                        example:
                          - Time from is required.
                      mood:
                        type: array
                        items:
                          type: string
                        example:
                          - Mood is required.
                      globalErrors:
                        type: array
                        items:
                          type: string
                        example:
                          - Attendance overlaps with vacation or sickness period.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Not an employee or attendance not enabled in workspace
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 403
                  message:
                    type: string
                    example: Forbidden
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update attendance record
      tags:
        - attendance
        - employee-only
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - date
          - timeFrom
          - timeTo
          - totalHours
          - mood
          - type
        allowedFields:
          - date
          - timeFrom
          - timeTo
          - totalHours
          - mood
          - comment
          - type
          - deletedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes an attendance record for a specific date.

        **What happens:**
        - The attendance record for the specified date is permanently deleted
        - This action cannot be undone
        - Deleted records are marked with a deletion timestamp

        **Note:** This endpoint is only available for employee accounts. Attendance tracking must be enabled in the workspace settings. You can only delete your own attendance records.
      operationId: AttendanceController__delete
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeleteAttendanceRequestDto'
      responses:
        '200':
          description: Attendance record deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    properties:
                      date:
                        type: array
                        items:
                          type: string
                        example:
                          - Date is required.
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Not an employee or attendance not enabled in workspace
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 403
                  message:
                    type: string
                    example: Forbidden
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete attendance record
      tags:
        - attendance
        - employee-only
      x-required-scopes:
        - api:write
  /agents/models:
    get:
      description: Returns enabled model aliases for native Leadtime agents. Save one of these aliases as config.defaultModelId on native agent settings, or use modelId on internal chat stream requests.
      operationId: AgentsController_listNativeModels
      parameters: []
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List native Leadtime agent models
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /agents/provider-api-keys:
    get:
      operationId: AgentsController_listProviderApiKeys
      parameters:
        - name: provider
          required: true
          in: query
          schema:
            type: string
            enum:
              - cursorCloud
              - claudeManaged
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List saved hosted-agent provider API keys
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: AgentsController_createProviderApiKey
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,provider,label
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWorkspaceProviderApiKeyDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a saved hosted-agent provider API key
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - provider
          - label
        allowedFields:
          - id
          - provider
          - label
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/provider-api-keys/{id}:
    delete:
      operationId: AgentsController_deleteProviderApiKey
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a saved hosted-agent provider API key
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/skills:
    get:
      description: Lists shared workspace custom skills that can be selected by native custom agents and Leadtime-managed Claude agents.
      operationId: AgentsController_listWorkspaceSkills
      parameters: []
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List workspace custom agent skills
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: Creates a shared workspace skill. Attach the returned skill id to native custom agents with config.workspaceSkillIds on /agents/{id}/native, or to Leadtime-managed Claude agents with config.managedAgent.skills on /agents/{id}/claude.
      operationId: AgentsController_createWorkspaceSkill
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,displayTitle
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWorkspaceAgentSkillDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a workspace custom agent skill
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - displayTitle
        allowedFields:
          - id
          - name
          - displayTitle
          - description
          - body
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/skills/{skillId}:
    patch:
      description: Updates a shared workspace skill used by native custom agents and Leadtime-managed Claude agents.
      operationId: AgentsController_updateWorkspaceSkill
      parameters:
        - name: skillId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,displayTitle
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update a workspace custom agent skill
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - displayTitle
        allowedFields:
          - id
          - name
          - displayTitle
          - description
          - body
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: Archives a shared workspace skill so it is no longer selectable for native custom agents or Leadtime-managed Claude agents.
      operationId: AgentsController_archiveWorkspaceSkill
      parameters:
        - name: skillId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Archive a workspace custom agent skill
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents:
    get:
      description: Lists agents in this workspace, including their enabled state and hosted-agent provider key when configured.
      operationId: AgentsController_list
      parameters: []
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List agents
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: Creates an agent. For normal user-created custom agents, set connectionProvider to native. Use claudeManaged only when the user explicitly asks for Anthropic/Claude Managed, cursorCloud only for Cursor Cloud, and selfHosted only for external wrappers/webhooks. Native custom-agent instructions and workspace skills are configured through the native settings endpoint.
      operationId: AgentsController_create
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,connectionProvider,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAgentDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create an agent
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - connectionProvider
          - enabled
        allowedFields:
          - id
          - name
          - email
          - roleId
          - enabled
          - connectionProvider
          - avatar
          - avatarConfig
          - settings
          - nativeConfig
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/runs:
    get:
      description: Lists agent sessions for one agent. Agent managers can see all sessions; other callers only see sessions they can access through their own chats or readable tasks.
      operationId: AgentsController_listAgentRuns
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: targetType
          required: false
          in: query
          schema:
            type: string
            enum:
              - task
              - taskComment
              - appChat
              - slackThread
              - editorSession
              - automationRun
        - name: status
          required: false
          in: query
          schema:
            type: string
            enum:
              - queued
              - running
              - waitingInput
              - waitingApproval
              - done
              - failed
              - canceled
        - name: mine
          required: false
          in: query
          description: When true, return only sessions owned by the current user. Non-admin callers are always limited to their accessible sessions.
          schema:
            type: string
        - name: from
          required: false
          in: query
          description: ISO date lower bound for run creation time.
          schema:
            type: string
        - name: to
          required: false
          in: query
          description: ISO date upper bound for run creation time.
          schema:
            type: string
        - name: limit
          required: false
          in: query
          description: Maximum sessions to return, capped at 100.
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List sessions for an agent
      tags:
        - agents
      x-required-scopes:
        - api:read
  /agents/{id}/chats:
    get:
      description: Lists app-chat sessions with one agent. Non-admin callers are limited to their own chats.
      operationId: AgentsController_listAgentChats
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: mine
          required: false
          in: query
          description: When true, return only chats owned by the current user. Non-admin callers are always limited to their own chats.
          schema:
            type: string
        - name: limit
          required: false
          in: query
          description: Maximum chats to return, capped at 100.
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List app chats with an agent
      tags:
        - agents
      x-required-scopes:
        - api:read
  /agents/{id}:
    get:
      operationId: AgentsController_get
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get agent details
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: Updates an agent. For normal custom agents, keep or set connectionProvider to native. Do not switch a native custom agent to claudeManaged unless the user explicitly asks for Anthropic/Claude Managed.
      operationId: AgentsController_update
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,connectionProvider,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAgentBodyDto'
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an agent
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - connectionProvider
          - enabled
        allowedFields:
          - id
          - name
          - email
          - roleId
          - enabled
          - connectionProvider
          - avatar
          - avatarConfig
          - settings
          - nativeConfig
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: AgentsController__delete
      parameters: []
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/status:
    patch:
      operationId: AgentsController_setStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPublicAgentStatusDto'
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Enable or disable an agent
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - enabled
        allowedFields:
          - id
          - name
          - email
          - roleId
          - enabled
          - connectionProvider
          - avatar
          - avatarConfig
          - settings
          - nativeConfig
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/connection-provider:
    post:
      description: Sets the agent provider. Use native for normal Leadtime custom agents running on the internal native agent engine. Use claudeManaged, cursorCloud, or selfHosted only when the user explicitly requests that external/provider mode.
      operationId: AgentsController_setConnectionProvider
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,connectionProvider,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPublicAgentConnectionProviderDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Set agent connection provider
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - connectionProvider
          - enabled
        allowedFields:
          - id
          - name
          - email
          - roleId
          - enabled
          - connectionProvider
          - avatar
          - avatarConfig
          - settings
          - nativeConfig
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/agent-settings:
    get:
      description: Returns native custom-agent, self-hosted, Cursor Cloud, or Claude Managed settings for the agent depending on its selected connection provider.
      operationId: AgentsController_getAgentSettings
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get selected agent settings
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /agents/{id}/native:
    get:
      description: Returns instructions, selected workspaceSkillIds, and agent-scoped MCP connections for a native custom agent. Use this endpoint for native custom agents, not /agents/{id}/claude.
      operationId: AgentsController_getNative
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get native custom-agent settings for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: Saves custom instructions and selected workspace skill ids for a native custom agent running on Leadtime internal agent engine. Use this for normal custom agents. Send instructions under config.instructions and selected shared workspace skills under config.workspaceSkillIds. Do not use /agents/{id}/claude unless the agent is explicitly Anthropic/Claude Managed.
      operationId: AgentsController_saveNative
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,botUserId,enabled,config
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SavePublicNativeCustomAgentSettingsDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Save native custom-agent settings for an agent
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - botUserId
          - enabled
          - config
        allowedFields:
          - id
          - botUserId
          - enabled
          - config
          - provider
          - connectionProvider
          - webhookUrl
          - webhookSecret
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/native/mcp-connections:
    post:
      description: Creates or updates an agent-scoped MCP connection for a native custom agent. Credentials are stored separately per agent even when multiple agents use the same MCP server URL.
      operationId: AgentsController_saveNativeMcpConnection
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,botUserId,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SavePublicNativeCustomAgentMcpConnectionDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update a native custom-agent MCP connection
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - botUserId
          - enabled
        allowedFields:
          - id
          - botUserId
          - enabled
          - config
          - provider
          - connectionProvider
          - webhookUrl
          - webhookSecret
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/native/mcp-connections/{connectionId}:
    delete:
      operationId: AgentsController_deleteNativeMcpConnection
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: connectionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a native custom-agent MCP connection
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/native/mcp-connections/{connectionId}/disconnect:
    post:
      operationId: AgentsController_disconnectNativeMcpConnection
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: connectionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Disconnect native custom-agent MCP credentials
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/native/mcp-connections/{connectionId}/oauth-connect-url:
    post:
      description: Creates a browser OAuth login URL for one native custom-agent MCP connection. Use after saving the MCP row when the server authMode is oauth.
      operationId: AgentsController_getNativeMcpOAuthConnectUrl
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: connectionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicNativeCustomAgentMcpOAuthConnectUrlDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create an OAuth connect URL for a native custom-agent MCP connection
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/self-hosted:
    get:
      operationId: AgentsController_getSelfHosted
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get self-hosted agent settings
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: AgentsController_saveSelfHosted
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,botUserId,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePublicAgentSettingsDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Save self-hosted agent settings
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - botUserId
          - enabled
        allowedFields:
          - id
          - botUserId
          - enabled
          - config
          - provider
          - connectionProvider
          - webhookUrl
          - webhookSecret
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/self-hosted/enabled:
    post:
      operationId: AgentsController_setSelfHostedEnabled
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,botUserId,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPublicAgentConnectionEnabledDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Enable or disable the self-hosted agent connection
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - botUserId
          - enabled
        allowedFields:
          - id
          - botUserId
          - enabled
          - config
          - provider
          - connectionProvider
          - webhookUrl
          - webhookSecret
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/self-hosted/webhook-secret/rotate:
    post:
      operationId: AgentsController_rotateSelfHostedWebhookSecret
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,botUserId,webhookSecret
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Rotate the self-hosted agent webhook secret
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - botUserId
          - webhookSecret
        allowedFields:
          - id
          - botUserId
          - enabled
          - config
          - provider
          - connectionProvider
          - webhookUrl
          - webhookSecret
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/connector-setup-token:
    post:
      operationId: AgentsController_createConnectorSetupToken
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a one-time connector setup token for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/pat-tokens:
    get:
      operationId: AgentsController_listPatTokens
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List personal access tokens for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: AgentsController_createPatToken
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,token,label
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a personal access token for an agent
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - token
          - label
        allowedFields:
          - id
          - token
          - label
          - createdAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/pat-tokens/{tokenId}:
    delete:
      operationId: AgentsController_revokePatToken
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: tokenId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Revoke a personal access token for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/cursor:
    get:
      operationId: AgentsController_getCursor
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get Cursor Cloud settings for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: AgentsController_saveCursor
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,botUserId,enabled,config
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SavePublicCursorCloudAgentSettingsDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Save Cursor Cloud settings for an agent
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - botUserId
          - enabled
          - config
        allowedFields:
          - id
          - botUserId
          - enabled
          - config
          - provider
          - connectionProvider
          - webhookUrl
          - webhookSecret
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/cursor/validate:
    post:
      operationId: AgentsController_validateCursor
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidatePublicCursorCloudConnectionDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Validate Cursor Cloud credentials
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/cursor/repositories/refresh:
    post:
      operationId: AgentsController_refreshCursorRepositories
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidatePublicCursorCloudConnectionDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Refresh Cursor Cloud repository choices for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/cursor/models/refresh:
    post:
      operationId: AgentsController_refreshCursorModels
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidatePublicCursorCloudConnectionDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Refresh Cursor Cloud model choices for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude:
    get:
      description: Returns the selected provider API key, connection status, Leadtime-managed agent configuration, model cache, custom MCP servers, and GitHub repository defaults. Agents should read this before deciding whether the agent is empty or asking what to improve.
      operationId: AgentsController_getClaude
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get Claude Managed Agent settings for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: 'Saves Claude Managed settings. Prefer providerApiKeyId from /agents/provider-api-keys when a saved Anthropic key exists. Put Leadtime-managed model, editor-field instructions, and custom MCP servers under config.managedAgent. Instructions are a standard Leadtime editor field: send HTML, Markdown, or ProseMirror IDoc JSON, and Leadtime converts it to internal IDoc before syncing HTML to Claude. Put GitHub repository defaults under config.githubRepositoryDefaults. Use config.toolMode=basic by default when the agent only needs task context, comments, and status updates while doing external work or analysis. Use config.toolMode=full only when the agent is meant to perform broad Leadtime workspace actions such as creating or editing projects, tasks, organizations, contacts, forms, planning data, settings, or other company-management records. Re-read this endpoint after saving to verify the selected model, tool mode, MCP servers, and repositories.'
      operationId: AgentsController_saveClaude
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,botUserId,enabled,config
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SavePublicClaudeManagedAgentSettingsDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Save Claude Managed Agent settings for an agent
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - botUserId
          - enabled
          - config
        allowedFields:
          - id
          - botUserId
          - enabled
          - config
          - provider
          - connectionProvider
          - webhookUrl
          - webhookSecret
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/validate:
    post:
      operationId: AgentsController_validateClaude
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidatePublicClaudeManagedConnectionDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Validate Claude Managed Agent credentials
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/resources/refresh:
    post:
      description: Refreshes Claude resource metadata with the configured Anthropic API key. Use this after selecting or adding an API key when model choices are missing, then save config.managedAgent.modelId using one of the returned model ids.
      operationId: AgentsController_refreshClaudeResources
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidatePublicClaudeManagedConnectionDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Refresh Claude agents, environments, vaults, and models for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/models/refresh:
    post:
      description: Lists Claude models using the agent configured Anthropic key, or an optional providerApiKeyId/anthropicApiKey in the request body. When using a saved key, the agent model cache is updated so the UI and agents can select config.managedAgent.modelId from the same list.
      operationId: AgentsController_refreshClaudeModels
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidatePublicClaudeManagedConnectionDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Refresh Claude model choices for an agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/skills/catalog:
    post:
      description: Returns Anthropic built-in skills plus shared workspace custom agent skills for the Claude picker UI. Native custom agents do not need this catalog endpoint; use GET /agents/skills to list shared workspace skills and save selected ids under /agents/{id}/native.
      operationId: AgentsController_listClaudeSkillCatalog
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: List Anthropic and workspace agent skills for Claude configuration
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/github/install-url:
    post:
      operationId: AgentsController_getClaudeGithubInstallUrl
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a GitHub App installation URL for Claude Managed GitHub access
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/github/repositories/refresh:
    post:
      operationId: AgentsController_refreshClaudeGithubRepositories
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Refresh GitHub repositories available to a Claude Managed agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/github/disconnect:
    post:
      operationId: AgentsController_disconnectClaudeGithub
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Disconnect GitHub from a Claude Managed agent
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/{id}/claude/managed-mcp/oauth-connect-url:
    post:
      operationId: AgentsController_getClaudeManagedMcpOAuthConnectUrl
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicClaudeManagedMcpOAuthConnectUrlDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create an OAuth connect URL for a Claude Managed custom MCP server
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/project-agent-provider-settings/{projectId}:
    get:
      operationId: AgentsController_listProjectAgentProviderSettings
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project-level hosted-agent overrides
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /agents/project-agent-provider-settings:
    post:
      operationId: AgentsController_saveProjectAgentProviderSettings
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,projectId,botUserId,provider
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveProjectAgentProviderSettingsDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Save project-level hosted-agent overrides
      tags:
        - agents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - projectId
          - botUserId
          - provider
        allowedFields:
          - id
          - projectId
          - botUserId
          - provider
          - config
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /agents/project-agent-provider-settings/validate:
    post:
      operationId: AgentsController_validateProjectAgentProviderSettings
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateProjectAgentProviderSettingsDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Validate resolved project-level hosted-agent settings
      tags:
        - agents
      x-required-permissions:
        - publicApi.manageBots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /workspace/manual-position-categories:
    get:
      description: |-
        Retrieves all manual position categories for the workspace, sorted alphabetically by name.

        **What are Manual Position Categories?**
        Manual position categories are organizational labels used to group and structure manual positions (custom invoice line items) in projects. Categories help organize manual positions for better clarity in offers and invoices, making it easier to understand different types of services or costs.

        **What is returned:**
        - List of all active categories with:
          - `id`: Unique category identifier (UUID)
          - `name`: Category name (e.g., "Development", "Consulting", "Travel Expenses")

        **How it works:**
        - Categories are workspace-wide and can be used across all projects
        - Categories are sorted alphabetically by name for consistent ordering
        - Only active (non-deleted) categories are returned
        - Categories can be assigned to manual positions when creating or updating them

        **Use cases:**
        - Organizing manual positions by service type (e.g., "Development", "Design", "Consulting")
        - Grouping costs by nature (e.g., "Travel Expenses", "External Services", "Licenses")
        - Improving invoice readability by categorizing line items
      operationId: ManualPositionCategoriesController_listCategories
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ManualPositionCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all manual position categories
      tags:
        - workspace
        - manual-position-categories
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new manual position category or updates an existing one. The operation is determined by whether an `id` is provided in the request body.

        **How it works:**
        - **Create**: Omit the `id` field (or set it to `null`) to create a new category
        - **Update**: Include the `id` field with an existing category UUID to update that category

        **What is returned:**
        - The created or updated category with:
          - `id`: Category identifier (UUID)
          - `name`: Category name

        **Validation rules:**
        - `name`: Required, must be a non-empty string
        - `id`: Optional, must be a valid UUID if provided

        **Error handling:**
        - If updating with an `id` that doesn't exist, returns `404 Not Found`
        - If validation fails, returns `400 Bad Request` with detailed error messages

        **Note:** This endpoint requires the `manageManualPositions` permission.
      operationId: ManualPositionCategoriesController_createOrUpdateCategory
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrUpdateManualPositionCategoryDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManualPositionCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update manual position category
      tags:
        - workspace
        - manual-position-categories
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageManualPositions
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /workspace/manual-position-categories/{id}:
    delete:
      description: |-
        Soft deletes a manual position category by setting its `deletedAt` timestamp. All manual positions that were assigned to this category will have their `categoryId` automatically unset to `null`.

        **How it works:**
        - The category is marked as deleted (soft delete) and will no longer appear in category lists
        - All manual positions that were using this category are automatically updated to have `categoryId = null`
        - Manual positions themselves are not deleted, only their category assignment is removed
        - The category cannot be recovered after deletion

        **What is returned:**
        - `success`: Boolean indicating the operation completed successfully

        **Error handling:**
        - If the category ID doesn't exist, returns `404 Not Found`
        - The `id` parameter must be a valid UUID

        **Use cases:**
        - Removing obsolete categories that are no longer needed
        - Consolidating categories by removing duplicates
        - Cleaning up workspace organization

        **Note:** This endpoint requires the `manageManualPositions` permission.
      operationId: ManualPositionCategoriesController_deleteCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete manual position category
      tags:
        - workspace
        - manual-position-categories
      x-required-permissions:
        - projects.manageManualPositions
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /workspace/details:
    get:
      description: Returns comprehensive information about the current workspace, including branding settings, features, and configuration. The workspace is the top-level organizational unit in Leadtime that contains all users, projects, tasks, and other entities. This endpoint provides essential workspace metadata such as company name, domain, enabled features, appearance settings (colors, logos), custom icons, sprint configuration, and billing status. User-specific data is excluded from this response and is available via the /users/me endpoint. The attendanceEnabled field is only true if attendance tracking is enabled for the workspace AND the current user is an Employee type. The isBillingLocked field indicates whether the workspace is locked due to past due billing status.
      operationId: WorkspaceController_details
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceDetailsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Workspace not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get workspace details
      tags:
        - workspace
      x-required-scopes:
        - api:read
  /workspace/object-settings:
    get:
      description: Returns all object types with nested statuses and per-type custom field definitions. Use this to build object forms and integrations without admin object-type permissions.
      operationId: WorkspaceController_objectSettings
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceObjectSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Workspace object settings (read-only)
      tags:
        - workspace
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /workspace/upload:
    post:
      description: Uploads a file to the workspace and returns file metadata including a unique file ID and public URL. This endpoint is used for uploading various file types such as logos, avatars, documents, or custom icons. The uploaded file is associated with the workspace and can be referenced in other API endpoints using the returned file ID. The file is stored securely and a public URL is generated for accessing it. Common use cases include uploading workspace logos (for light and dark themes), user avatars, or custom icon images. The optional type parameter can be used to categorize the file (e.g., "logo", "avatar", "document") for organizational purposes.
      operationId: WorkspaceController_uploadFile
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,filename
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: File to upload
                type:
                  type: string
                  description: File type/category (e.g., "logo", "avatar", "document")
                  example: logo
              required:
                - file
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileUploadResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Upload file to workspace
      tags:
        - workspace
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - filename
        allowedFields:
          - id
          - filename
          - mimetype
          - size
          - url
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /workspace/files/{fileId}:
    get:
      description: Downloads a private file from the authenticated workspace, including chat attachments and generated artifacts.
      operationId: WorkspaceController_downloadFile
      parameters:
        - name: fileId
          required: true
          in: path
          description: File ID to download
          schema:
            type: string
      responses:
        '200':
          description: The requested file bytes
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: File not found in this workspace
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Download a workspace file
      tags:
        - workspace
      x-required-scopes:
        - api:read
  /workspace/users:
    get:
      description: 'Returns a list of all users in the workspace, including employees, organization members, and support users. This endpoint is optimized for common list data retrieval and provides essential user information for dropdowns, user pickers, and other UI components that need to display workspace users. The response includes user identification (ID, name, avatar), position/title, employee ID (if applicable), user type, login capability, and deletion status. Organization members have restricted visibility: they only see users from their own organization, not all workspace users. Use the type query parameter to filter by user type (Employee, OrganizationMember, LeadtimeSupport, Agent). By default, deleted users are included in the response; set includeDeleted=false to exclude them.'
      operationId: WorkspaceController_listUsers
      parameters:
        - name: type
          required: false
          in: query
          description: 'Filter users by user type. Options: Employee (internal workspace employees), OrganizationMember (external organization members), LeadtimeSupport (Leadtime support staff), Agent (automation/service accounts). If not specified, all user types are returned.'
          schema:
            enum:
              - Employee
              - OrganizationMember
              - LeadtimeSupport
              - HelpdeskUser
              - Agent
            type: string
        - name: includeDeleted
          required: false
          in: query
          description: Whether to include deleted users in the response. Default is true (deleted users are included). Set to false to exclude deleted users from the results.
          schema:
            default: true
            type: boolean
      responses:
        '200':
          description: Workspace users retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WorkspaceUserDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all workspace users
      tags:
        - workspace
        - common-lists
      x-required-scopes:
        - api:read
  /workspace/quick-search:
    get:
      description: Performs a unified search across multiple workspace entity types in a single query. This endpoint searches through tasks, projects, employees, organizations, organization members, teams, contacts, and the workspace itself. The search matches the query term against entity names, titles, and other searchable fields. Results are automatically filtered based on user permissions and workspace scoping, ensuring users only see entities they have access to. Each result includes the entity type, title, metadata (including ID), color, icon, and short name (if applicable). This endpoint is commonly used for command palettes, global search functionality, and entity pickers that need to search across multiple types. The search is case-insensitive and performs partial matching on entity names and titles.
      operationId: WorkspaceController_quickSearch
      parameters:
        - name: term
          required: true
          in: query
          description: 'Search term to match against entity names, titles, and other searchable fields. The search is case-insensitive and performs partial matching. Can be a single word or multiple words. Examples: "project", "John Smith", "Acme Corp".'
          schema:
            example: project
            type: string
      responses:
        '200':
          description: Search results matching the query term
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/QuickSearchResultDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Quick search across workspace entities
      tags:
        - workspace
      x-required-scopes:
        - api:read
  /workspace/product-list:
    get:
      description: 'Returns a list of all catalog products in the workspace. Catalog products are products that are not yet assigned to any project or task. These are the base products available in the product catalog that can be added to projects or tasks later. This endpoint is optimized for common list data retrieval and provides essential product information including name, category, logo, and pricing. Products can have three types of pricing: fixed (one-time payment), subscription (recurring payment), or per-unit pricing. All three pricing fields are optional and can be null if not applicable to the product. The response is sorted alphabetically by product name. This endpoint requires the Projects.manageProducts permission to access.'
      operationId: WorkspaceController_listProducts
      parameters: []
      responses:
        '200':
          description: Catalog products retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProductListItemDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all catalog products
      tags:
        - workspace
        - common-lists
      x-required-permissions:
        - projects.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /workspace/form-templates:
    get:
      description: 'Returns all form categories and form templates in the workspace. Form templates are reusable definitions that can be attached to tasks as form instances. Each template row includes metadata (name, category, tags, field count) but not individual field definitions. Use GET /workspace/form-templates/{id} to load the full field schema (types, labels, required flags, select options, settings) before attaching a template via POST /tasks/{identifier}/form-instances (body.templateId) or when building PATCH payloads: each `values` key matches the field `key` when set, otherwise the field `id` — the same strings appear as `valueKey` on GET /tasks/{identifier}/form-instances. Requires only the api:read scope (no FormTemplates.manage permission). For backward compatibility, GET /administration/form-templates-available returns the same list shape.'
      operationId: WorkspaceController_listFormTemplates
      parameters: []
      responses:
        '200':
          description: Form categories and templates retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceFormTemplatesResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all form templates
      tags:
        - workspace
        - common-lists
      x-required-scopes:
        - api:read
  /workspace/form-templates/{id}:
    get:
      description: 'Returns one form template including all non-deleted fields in sort order. Each field includes `type`, `label`, optional `key`, `id`, `required`, placeholder, help text, type-specific `settings` JSON, and `selectOptions` for Select/MultiSelect/Radio. When calling PATCH `/tasks/{identifier}/form-instances/{formInstanceId}`, use property names in `values` equal to `key` when present, otherwise the field `id` (GET form instances exposes this as `fields[].valueKey`). Typical flow: GET this endpoint to plan values → POST attach with this template `id` → PATCH with those keys. Requires only the api:read scope (no FormTemplates.manage permission).'
      operationId: WorkspaceController_getFormTemplate
      parameters:
        - name: id
          required: true
          in: path
          description: Form template UUID
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Form template with fields retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormTemplateDetailResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Form template not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get form template with fields
      tags:
        - workspace
        - common-lists
      x-required-scopes:
        - api:read
  /administration/document-templates:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of all document templates in the workspace.

        **What are Document Templates?**
        Document templates standardize recurring documents (like contracts, cover letters, requirement specs) and automatically fill them at runtime with project, client, and company data. Templates consist of static text plus variables that get replaced with actual values when generating a document.

        **What is returned:**
        - Template identification (ID, name, description)
        - Template metadata (language, type, creation/update dates)
        - Creator and updater information

        **Filtering and search:**
        - Quick search across template name and description
        - Advanced filtering by language, type, and date ranges
        - Sorting by any sortable field
        - Server-side pagination for large template lists

        **Note:** This endpoint requires the `DocumentTemplates.canManage` permission. Templates are used in projects to generate standardized documents with dynamic content.
      operationId: DocumentTemplatesController_listDocumentTemplates
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: name'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Template ID
            - **name** (string): Template name
            - **language** (string): Template language
            - **type** (set): Template type
            - **createdAt** (date): Creation timestamp
            - **updatedAt** (date): Last update timestamp
            - **createdBy** (string): Creator user ID
            - **updatedBy** (string): Last updater user ID

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Template ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **name** (string): Template name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **language** (string): Template language (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Template type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **updatedBy** (string): Last updater user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Template ID
            - **name**: Template name
            - **language**: Template language
            - **type**: Template type
            - **createdAt**: Creation timestamp
            - **updatedAt**: Last update timestamp
            - **createdBy**: Creator user ID
            - **updatedBy**: Last updater user ID

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, name, description, language, type, createdAt, updatedAt, createdBy, updatedBy
          schema:
            example: id,name
            type: array
            items: {}
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List document templates
      tags:
        - administration
        - document-templates
        - administration
      x-required-permissions:
        - documentTemplates.canManage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new document template for generating standardized documents with dynamic content.

        **What are Document Templates?**
        Document templates standardize recurring documents (contracts, cover letters, requirement specs) by combining static text with variables that get automatically replaced with actual values when generating documents in projects.

        **Step-by-step process:**
        1. Provide template metadata (name, description, language, type)
        2. Define the template content as HTML or Markdown
        3. Optionally define custom variables for template-specific placeholders
        4. The system converts your content to internal format and stores the template

        **Content format:**
        You can provide content as HTML or Markdown. The content supports:
        - Rich text formatting (headings, lists, tables, bold, italic, etc.)
        - System variables (e.g., `#project.name`, `#organization.companyName`, `#currentUser.firstName`)
        - Custom variables (defined in the `customVariables` array)
        - Conditional sections that show/hide based on variable values
        - Page breaks for document structure

        **Using variables in content:**
        Variables are represented as HTML spans: `<span data-type="variable" data-id="project.name">...</span>`
        Custom variables use the format: `<span data-type="variable" data-id="custom.variable_name">...</span>`

        **Custom variables:**
        Custom variables allow you to add template-specific fields that aren't covered by standard system variables. Each variable has a name, type (Text, Number, Date, Boolean, Select, etc.), optional default value, and can be marked as required. When generating a document, users will be prompted to fill in these values.

        **What is returned:**
        The created template with content converted back to HTML format and all custom variables included.
      operationId: DocumentTemplatesController_createDocumentTemplate
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDocumentTemplateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentTemplateApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create document template
      tags:
        - administration
        - document-templates
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - language
          - type
          - content
          - workspaceId
          - createdAt
          - updatedAt
          - createdBy
          - updatedBy
          - documentCustomVariables
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - documentTemplates.canManage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/document-templates/{id}:
    get:
      description: |-
        Returns the complete document template including its content and custom variables.

        **What is returned:**
        - Full template metadata (name, description, language, type)
        - Template content as HTML (converted from internal IDoc format)
        - All custom variables defined for this template
        - Creation and update timestamps with user information

        **Content format:**
        The content is returned as HTML, which may include:
        - Static text and formatting (headings, lists, tables, etc.)
        - Variable placeholders (e.g., `<span data-type="variable" data-id="project.name">...</span>`)
        - Conditional sections that show/hide based on variable values
        - Page breaks for document structure

        **Custom variables:**
        Each template can define custom variables that extend beyond the standard system variables. These are returned with their type, default values, and whether they are required.
      operationId: DocumentTemplatesController_getDocumentTemplate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentTemplateApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get document template details
      tags:
        - administration
        - document-templates
        - administration
      x-required-permissions:
        - documentTemplates.canManage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates an existing document template. All fields must be provided as this is a full replacement operation.

        **What gets updated:**
        - Template metadata (name, description, language, type)
        - Template content (replaced entirely with new HTML or Markdown)
        - Custom variables (replaced entirely with new array)

        **Content format:**
        Provide content as HTML or Markdown with support for:
        - Rich text formatting (headings, lists, tables, etc.)
        - System variables (e.g., `#project.name`, `#organization.companyName`)
        - Custom variables (defined in the `customVariables` array)
        - Conditional sections and page breaks

        **Using variables:**
        Variables are represented as HTML spans: `<span data-type="variable" data-id="project.name">...</span>`
        Custom variables use: `<span data-type="variable" data-id="custom.variable_name">...</span>`

        **Custom variables:**
        The entire custom variables array is replaced. Each variable defines a template-specific field with name, type, optional default value, and required flag.

        **What is returned:**
        The updated template with content converted to HTML format and all custom variables included.

        **Note:** For partial updates (only content or custom variables), use the PATCH endpoint instead.
      operationId: DocumentTemplatesController_updateDocumentTemplate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDocumentTemplateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentTemplateApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update document template
      tags:
        - administration
        - document-templates
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - language
          - type
          - content
          - workspaceId
          - createdAt
          - updatedAt
          - createdBy
          - updatedBy
          - documentCustomVariables
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - documentTemplates.canManage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Updates only the content and/or custom variables of a document template. All other fields remain unchanged.

        **What can be updated:**
        - Template content (optional - only if provided)
        - Custom variables (optional - only if provided)
        - Metadata fields (name, description, language, type) are NOT updated via this endpoint

        **Content format:**
        If provided, content should be HTML or Markdown with support for:
        - Rich text formatting (headings, lists, tables, etc.)
        - System variables (e.g., `#project.name`, `#organization.companyName`)
        - Custom variables (defined in the `customVariables` array)
        - Conditional sections and page breaks

        **Using variables:**
        Variables are represented as HTML spans: `<span data-type="variable" data-id="project.name">...</span>`
        Custom variables use: `<span data-type="variable" data-id="custom.variable_name">...</span>`

        **Custom variables:**
        If provided, the entire custom variables array replaces the existing one. Each variable defines a template-specific field with name, type, optional default value, and required flag.

        **What is returned:**
        The updated template with content converted to HTML format and all custom variables included.

        **Note:** For full updates including metadata, use the PUT endpoint instead.
      operationId: DocumentTemplatesController_patchDocumentTemplate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchDocumentTemplateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentTemplateApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update document template
      tags:
        - administration
        - document-templates
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - language
          - type
          - content
          - workspaceId
          - createdAt
          - updatedAt
          - createdBy
          - updatedBy
          - documentCustomVariables
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - documentTemplates.canManage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a document template from the workspace.

        **What happens:**
        - The template is marked as deleted (soft delete)
        - It will no longer appear in list queries
        - It cannot be used for generating new documents
        - The template data is preserved in the database

        **Note:** This is a soft delete operation. The template is not permanently removed and can potentially be restored if needed. This endpoint requires the `DocumentTemplates.canManage` permission.
      operationId: DocumentTemplatesController_deleteDocumentTemplate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete document template
      tags:
        - administration
        - document-templates
        - administration
      x-required-permissions:
        - documentTemplates.canManage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /automations:
    get:
      description: Returns a paginated, filterable, and sortable grid of automations in the workspace. Supports filtering by scope (personal/workspace). Requires AGENT feature and either managePersonalAutomations or manageWorkspaceAutomations.
      operationId: AutomationsController_listAutomations
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: title'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Automation ID
            - **title** (string): Automation title
            - **scope** (set): Scope (personal or workspace)
            - **executionType** (set): Execution type (agent or workflow)
            - **executorUserId** (string): Executor user ID
            - **ownerUserId** (string): Owner user ID (for personal scope)
            - **isEnabled** (boolean): Whether automation is enabled
            - **createdAt** (date): Creation timestamp
            - **updatedAt** (date): Last update timestamp
            - **createdBy** (string): Creator user ID
            - **updatedBy** (string): Last updater user ID

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Automation ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **title** (string): Automation title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **scope** (set): Scope (personal or workspace) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **executionType** (set): Execution type (agent or workflow) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **executorUserId** (string): Executor user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **ownerUserId** (string): Owner user ID (for personal scope) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **isEnabled** (boolean): Whether automation is enabled (boolean filter). Available comparisons: equal, not_equal
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **updatedBy** (string): Last updater user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Automation ID
            - **title**: Automation title
            - **scope**: Scope (personal or workspace)
            - **executionType**: Execution type (agent or workflow)
            - **executorUserId**: Executor user ID
            - **ownerUserId**: Owner user ID (for personal scope)
            - **isEnabled**: Whether automation is enabled
            - **createdAt**: Creation timestamp
            - **updatedAt**: Last update timestamp
            - **createdBy**: Creator user ID
            - **updatedBy**: Last updater user ID

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, title, scope, executionType, executorUserId, ownerUserId, isEnabled, lastRunAt, lastRunStatus, createdAt, updatedAt, createdBy, updatedBy
          schema:
            example: id,title
            type: array
            items: {}
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List automations
      tags:
        - automations
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
    post:
      description: 'Creates a new automation. Executor must be a bot/agent user (automations always execute as an agent). Personal scope requires managePersonalAutomations; workspace requires manageWorkspaceAutomations. userPrompt accepts HTML or Markdown (automation prompt format: basic nodes, mentions, images; no files or videos).'
      operationId: AutomationsController_createAutomation
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,scope,isEnabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAutomationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AutomationApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create automation
      tags:
        - automations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - scope
          - isEnabled
        allowedFields:
          - id
          - workspaceId
          - title
          - executionType
          - userPrompt
          - executionConfig
          - scope
          - ownerUserId
          - executorUserId
          - isEnabled
          - createdAt
          - updatedAt
          - createdBy
          - updatedBy
          - triggers
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
  /automations/{id}:
    get:
      description: 'Returns the full automation including triggers. userPrompt is returned as HTML (automation prompt format: basic nodes, mentions, images; no files or videos).'
      operationId: AutomationsController_getAutomation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AutomationApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get automation details
      tags:
        - automations
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
    put:
      description: 'Updates an existing automation. Executor may remain the existing human executor. This PUT expects the full automation payload again (including title, executorUserId, and triggers). For event triggers, matching filters must be stored in triggers[].definition.filters, not executionConfig. userPrompt accepts HTML or Markdown (automation prompt format: basic nodes, mentions, images; no files or videos).'
      operationId: AutomationsController_updateAutomation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,scope,isEnabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAutomationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AutomationApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update automation
      tags:
        - automations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - scope
          - isEnabled
        allowedFields:
          - id
          - workspaceId
          - title
          - executionType
          - userPrompt
          - executionConfig
          - scope
          - ownerUserId
          - executorUserId
          - isEnabled
          - createdAt
          - updatedAt
          - createdBy
          - updatedBy
          - triggers
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
    delete:
      description: Soft deletes an automation.
      operationId: AutomationsController_deleteAutomation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete automation
      tags:
        - automations
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
  /automations/{id}/test-run:
    post:
      description: Creates and enqueues a test run for the automation. Optional testContextDoc (IDoc) is merged with the base prompt. Returns the created run id.
      operationId: AutomationsController_testRun
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,automationId,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TestRunAutomationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AutomationRunCreatedResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create test run
      tags:
        - automations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - automationId
          - status
        allowedFields:
          - id
          - automationId
          - status
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
  /automations/{id}/runs:
    get:
      description: Returns a paginated grid of runs for an automation.
      operationId: AutomationsController_listAutomationRuns
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List automation runs
      tags:
        - automations
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
  /automations/{id}/runs/{runId}:
    get:
      description: Returns run metadata and, when linked, agent conversation details.
      operationId: AutomationsController_getAutomationRun
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: runId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AutomationRunApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get automation run details
      tags:
        - automations
      x-required-permissions:
        - automations.managePersonalAutomations
        - automations.manageWorkspaceAutomations
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
  /administration/product-catalog:
    get:
      description: |-
        **What is the Product Catalog?**
        The product catalog in Leadtime is used for the central management of all standardizable services and products of a company. It helps you manage all your standardisable services and products in one place, structure offers, set prices, and define variations or options. The catalog is the base for sales, quotes, and billing of recurring services.

        **What is a Product?**
        A product is a standardized, reusable unit of service for your business. It can be a physical item, a software license, or a clearly defined service that is offered with fixed or recurring prices. Unlike projects that are tailored to customer needs, a product describes a predefined, repeatable offering that can be sold multiple times and used directly in proposals, invoices, or projects.

        **What data is returned:**
        This endpoint returns a paginated, filterable, and sortable list of all catalog products in your workspace. Each product includes:
        - Basic information: name, category, logo
        - Pricing: fixed price, subscription price, per-unit price
        - Structure: number of variants and options
        - Metadata: creation date, last update

        **Supported operations:**
        - Filter by category, name, pricing type, or any other field
        - Sort by any column (name, price, category, etc.)
        - Search across product names
        - Paginate through large catalogs

        Retrieves a paginated grid of product catalog with filtering and sorting capabilities. Quick search available on: name. Filterable fields: id, name, categoryId, logoId, optionsCount, variantsCount, priceFixed, priceSubscription, pricePerUnit. Sortable fields: id, name, categoryId, optionsCount, variantsCount, priceFixed, priceSubscription, pricePerUnit.
      operationId: ProductCatalogController_listProductCatalog
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: name'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Product ID
            - **name** (string): Product name
            - **categoryId** (string): Category ID
            - **logoId** (string): Logo file ID
            - **optionsCount** (number): Number of product options
            - **variantsCount** (number): Number of product variants
            - **priceFixed** (number): Fixed price
            - **priceSubscription** (number): Subscription price
            - **pricePerUnit** (number): Price per unit

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Product ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **name** (string): Product name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **categoryId** (string): Category ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **logoId** (string): Logo file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **optionsCount** (number): Number of product options (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **variantsCount** (number): Number of product variants (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **priceFixed** (number): Fixed price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **priceSubscription** (number): Subscription price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **pricePerUnit** (number): Price per unit (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Product ID
            - **name**: Product name
            - **categoryId**: Category ID
            - **optionsCount**: Number of product options
            - **variantsCount**: Number of product variants
            - **priceFixed**: Fixed price
            - **priceSubscription**: Subscription price
            - **pricePerUnit**: Price per unit

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** name (asc)
          schema:
            example: '[{"field":"name","direction":"asc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, name, categoryId, logoId, optionsCount, variantsCount, priceFixed, priceSubscription, pricePerUnit
          schema:
            example: id,name
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of product catalog items
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List product catalog
      tags:
        - administration
        - products
        - administration
      x-required-permissions:
        - productsCatalog.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        **What this does:**
        Creates a new catalog product with variants and options. Catalog products are standardized, reusable offerings that can be added to projects, tickets, quotes, and invoices.

        **Product structure:**
        A product consists of:
        1. **Product details**: Name, category, logo, description, and base pricing
        2. **Variants** (optional): Different configurations or performance levels (e.g., Standard, Pro, Enterprise) with individual prices
        3. **Options** (optional): Extra services or add-ons that customers can optionally select (e.g., "Add workshop" or "Enable premium support")

        **Pricing:**
        You can set one or more pricing types:
        - **Fixed price**: One-time payment (e.g., for hardware or one-time services)
        - **Subscription price**: Recurring payment (e.g., monthly SaaS subscription)
        - **Price per unit**: Flexible billing by quantity (e.g., per user, per hour)

        **Description format:**
        - Descriptions can be provided as HTML or Markdown
        - The system automatically converts them to internal format (IDoc) for storage
        - Supported formatting: tables, callouts, emojis, and basic text formatting

        **Logo upload:**
        To add a logo to your product:
        1. First, call POST /api/public/workspace/upload to upload the image file
        2. The upload endpoint returns a file ID
        3. Use that file ID in the logoId field when creating the product

        **Validation:**
        - Product name is required
        - Category ID must be a valid UUID
        - At least one variant must be provided (even if just one)
        - Options array is required (can be empty)
        - If pricePerUnit is set, priceUnitName is required
        - If pricePerUnit or priceSubscription is set, priceFrequency must be 1, 3, 6, or 12 (months)

        **Response:**
        Returns the created product with all details, including:
        - Descriptions converted to HTML
        - Logo URL (if logo was uploaded)
        - All variants and options with their configurations
      operationId: ProductCatalogController_createProduct
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProductDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IProductApiResponse'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      name:
                        - Name is required
                      variants[0].priceFixed:
                        - Price must be a positive number
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create product
      tags:
        - administration
        - products
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - categoryId
          - logoId
          - logoUrl
          - priceFixed
          - priceSubscription
          - pricePerUnit
          - priceFrequency
          - priceUnitName
          - isActivated
          - descriptionHtml
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - productsCatalog.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/product-catalog/{id}:
    get:
      description: |-
        **What is returned:**
        Returns complete details for a single catalog product, including all variants, options, and pricing information. Product and variant descriptions are automatically converted from internal format (IDoc) to HTML for easy display.

        **Product structure:**
        A product consists of three main sections:
        1. **Product details**: Name, category, logo, description, and base pricing
        2. **Variants**: Different performance levels or configurations (e.g., Standard, Pro, Enterprise) with their own prices and descriptions
        3. **Options**: Extra services or add-ons that customers can optionally book (e.g., "Add workshop" or "Enable premium support")

        **Pricing models:**
        - **Fixed price**: One-time payment amount
        - **Subscription price**: Recurring amount at set intervals (monthly, quarterly, yearly)
        - **Price per unit**: Flexible billing by quantity (e.g., per user, per hour)

        **Response format:**
        - All descriptions are returned as HTML (converted from internal format)
        - Logo URL is included if a logo is set (points to public file endpoint)
        - Variants include their activation status
        - Options include their configuration (required, multiple selection)

        **Note:** This endpoint only returns catalog products (products without projectId or taskId). Products assigned to specific projects or tasks are not accessible through this endpoint.
      operationId: ProductCatalogController_getProductDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IProductApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get product details
      tags:
        - administration
        - products
        - administration
      x-required-permissions:
        - productsCatalog.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        **What this does:**
        Updates an existing catalog product. This is a full update - all fields must be provided, even if they are not changing. For partial updates, use the PATCH endpoint instead.

        **Important:**
        - This endpoint only works with catalog products (products without projectId or taskId)
        - All fields must be provided (use PATCH for partial updates)
        - Variants and options can be updated by including their IDs in the request
        - To remove variants or options, omit them from the arrays

        **Description format:**
        - Descriptions can be provided as HTML or Markdown
        - The system automatically converts them to internal format (IDoc) for storage
        - Supported formatting: tables, callouts, emojis, and basic text formatting

        **Logo upload:**
        To change the logo:
        1. Call POST /api/public/workspace/upload to upload the new image file
        2. Use the returned file ID in the logoId field
        3. To remove the logo, set logoId to null

        **Variant and option updates:**
        - Include variant/option IDs to update existing ones
        - Omit IDs to create new variants/options
        - Omit variants/options entirely to remove them

        **Validation:**
        - Same validation rules apply as for creation
        - Product must exist and be a catalog product

        **Response:**
        Returns the updated product with all details, including:
        - Descriptions converted to HTML
        - Logo URL (if logo is set)
        - All variants and options with their configurations
      operationId: ProductCatalogController_updateProduct
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProductDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IProductApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update product
      tags:
        - administration
        - products
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - categoryId
          - logoId
          - logoUrl
          - priceFixed
          - priceSubscription
          - pricePerUnit
          - priceFrequency
          - priceUnitName
          - isActivated
          - descriptionHtml
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - productsCatalog.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What this does:**
        Soft deletes a catalog product. The product is marked as deleted but not permanently removed from the database. This allows for data recovery if needed.

        **Important:**
        - Only catalog products can be deleted through this endpoint (products without projectId or taskId)
        - Products that are assigned to projects or tasks cannot be deleted here
        - The product must exist in your workspace

        **After deletion:**
        - The product will no longer appear in the product catalog list
        - It will not be available for selection in projects, tickets, or quotes
        - Historical data (e.g., invoices that used this product) remains intact

        **Response:**
        Returns a success confirmation.
      operationId: ProductCatalogController_deleteProduct
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete product
      tags:
        - administration
        - products
        - administration
      x-required-permissions:
        - productsCatalog.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        **What this does:**
        Partially updates a catalog product. Only the fields you provide will be updated; all other fields remain unchanged. This is useful for making small changes without resending the entire product structure.

        **Important:**
        - Only catalog products can be updated through this endpoint (products without projectId or taskId)
        - Omit fields you do not want to change
        - To clear optional fields (like logoId), set them to null explicitly

        **Description updates:**
        - If description is provided, it can be HTML or Markdown
        - The system automatically converts it to internal format (IDoc)
        - If description is omitted, the existing description remains unchanged

        **Variant updates:**
        - If variants array is provided, it replaces all existing variants
        - Include variant IDs to update existing variants
        - Omit IDs to create new variants
        - To keep existing variants unchanged, omit the variants field entirely

        **Option updates:**
        - If options array is provided, it replaces all existing options
        - Include option IDs to update existing options
        - Omit IDs to create new options
        - To keep existing options unchanged, omit the options field entirely

        **Logo updates:**
        To change the logo:
        1. Call POST /api/public/workspace/upload to upload the new image
        2. Use the returned file ID in the logoId field
        3. To remove the logo, set logoId to null explicitly

        **Validation:**
        - Only provided fields are validated
        - If pricePerUnit is updated, priceUnitName must also be provided
        - If pricePerUnit or priceSubscription is updated, priceFrequency must be 1, 3, 6, or 12 (months)

        **Response:**
        Returns the updated product with all details, including:
        - Descriptions converted to HTML
        - Logo URL (if logo is set)
        - All variants and options with their configurations
      operationId: ProductCatalogController_patchProduct
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProductDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IProductApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update product
      tags:
        - administration
        - products
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - categoryId
          - logoId
          - logoUrl
          - priceFixed
          - priceSubscription
          - pricePerUnit
          - priceFrequency
          - priceUnitName
          - isActivated
          - descriptionHtml
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - productsCatalog.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/product-categories:
    get:
      description: |-
        Retrieves all product categories configured for the workspace.

        **What are Product Categories?**
        Product categories organize your product catalog into logical groups (e.g., Hardware, Software, Services, Subscriptions). They help structure products and make it easier to assign products in quotes, invoices, and projects.

        **Where are product categories used:**
        - Product catalog: Products are assigned to categories
        - Quotes and invoices: Categories help organize line items
        - Projects: Products added to projects retain their category
        - Tickets: Products added to tickets are organized by category

        **What is returned:**
        - Category ID, name, icon, and description
        - Sort order for display
        - Multilingual translations (name and description in different languages)
        - Default key (for system-defined categories)
        - Creation and deletion timestamps

        **Note:** This endpoint requires the manageSettings permission. Categories can be customized per workspace and support multilingual labels.
      operationId: ProductCategoriesController_getProductCategories
      parameters: []
      responses:
        '200':
          description: Product categories retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProductCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List product categories
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new product category for organizing products in the catalog.

        **What are Product Categories?**
        Product categories organize your product catalog into logical groups (e.g., Hardware, Software, Services, Subscriptions). They help structure products and make it easier to assign products in quotes, invoices, and projects.

        **How to create a product category:**
        1. Provide a name (e.g., "Hardware", "Software", "Consulting")
        2. Choose an icon in format :icon_name: (e.g., :computer:, :package:, :office_worker:)
        3. Optionally add a description
        4. Provide translations for multilingual support (name and description in different languages)

        **Icon format:**
        - Use emoji-style format: :icon_name:
        - Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:
        - Default icon is :rocket: if not specified

        **Multilingual support:**
        - Provide translations array with language codes (e.g., "en", "de")
        - Each translation can have a translated name and description
        - Translations appear automatically in multilingual user interfaces and documents

        **Note:** This endpoint requires the manageSettings permission. Once created, the category can be used when creating or editing products in the catalog.
      operationId: ProductCategoriesController_createProductCategory
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProductCategoryDto'
      responses:
        '201':
          description: Product category created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create product category
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/product-categories/{id}:
    get:
      description: |-
        Retrieves detailed information about a specific product category by its ID.

        **What is returned:**
        - Category ID, name, icon, and description
        - Sort order for display
        - Multilingual translations (name and description in different languages)
        - Default key (for system-defined categories)
        - Creation and deletion timestamps

        **Note:** This endpoint requires the manageSettings permission. Returns 404 if the category does not exist or has been deleted.
      operationId: ProductCategoriesController_getProductCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Product category retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get product category details
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates all fields of an existing product category. All fields must be provided.

        **What can be updated:**
        - Name: Display name of the category
        - Icon: Icon in format :icon_name: (e.g., :computer:, :package:)
        - Description: Optional description of the category
        - Translations: Multilingual name and description translations

        **Icon format:**
        - Use emoji-style format: :icon_name:
        - Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:

        **Multilingual support:**
        - Provide complete translations array with language codes
        - Each translation can have a translated name and description
        - Translations appear automatically in multilingual user interfaces and documents

        **Note:** This endpoint requires the manageSettings permission. Use PATCH endpoint if you only want to update specific fields. All fields are required in PUT requests.
      operationId: ProductCategoriesController_updateProductCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProductCategoryDto'
      responses:
        '200':
          description: Product category updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update product category
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Partially updates an existing product category. Only provided fields will be updated.

        **What can be updated:**
        - Name: Display name of the category (optional)
        - Icon: Icon in format :icon_name: (optional)
        - Description: Optional description of the category (optional)
        - Translations: Multilingual name and description translations (optional)

        **Icon format:**
        - Use emoji-style format: :icon_name:
        - Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:

        **Multilingual support:**
        - Provide translations array with language codes
        - Each translation can have a translated name and description
        - If translations are provided, they replace existing translations
        - Translations appear automatically in multilingual user interfaces and documents

        **Note:** This endpoint requires the manageSettings permission. Only fields provided in the request body will be updated. Fields not provided will remain unchanged. Returns 404 if the category does not exist.
      operationId: ProductCategoriesController_patchProductCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProductCategoryDto'
      responses:
        '200':
          description: Product category updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update product category
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a product category using soft delete. The category is marked as deleted but not permanently removed.

        **What happens when deleting:**
        - Category is marked as deleted (soft delete)
        - Category will no longer appear in lists
        - Products assigned to this category may need to be reassigned
        - Category can potentially be restored if needed

        **Note:** This endpoint requires the manageSettings permission. Consider the impact on existing products before deleting a category. Returns 404 if the category does not exist.
      operationId: ProductCategoriesController_deleteProductCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Product category deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete product category
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/product-categories/sort:
    post:
      description: |-
        Updates the display order of product categories by providing an ordered list of category IDs.

        **How to reorder categories:**
        1. Get the current list of categories using GET /administration/product-categories
        2. Create an array of category IDs in the desired display order
        3. Send the ordered array in the request body

        **Example:**
        If you want categories in order: Hardware, Software, Consulting
        Provide: ["hardware-id", "software-id", "consulting-id"]

        **What happens:**
        - Categories are reordered according to the provided array
        - First ID in array becomes first category, second ID becomes second, etc.
        - Sort order is updated for all categories in the workspace

        **Note:** This endpoint requires the manageSettings permission. The order affects how categories appear in dropdowns and lists throughout the application.
      operationId: ProductCategoriesController_sortProductCategories
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortProductCategoriesDto'
      responses:
        '200':
          description: Product categories reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder product categories
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/product-categories/restore-defaults:
    post:
      description: |-
        Restores the default product categories provided by Leadtime. This resets categories to the original system defaults.

        **What are default categories?**
        Leadtime provides pre-configured categories such as:
        - External Costs (Additional Services)
        - Software (Our Software)
        - Hardware (The Hardware products we sell)
        - Consulting (Our general consulting services)

        **How it works:**
        1. System default categories are restored
        2. Optionally keep custom categories you created (set keepCustom to true)
        3. If keepCustom is false, all custom categories are removed
        4. Default categories include multilingual translations (English and German)

        **When to use:**
        - You want to start fresh with default categories
        - You made changes and want to revert to defaults
        - You want a clean starting point for product organization

        **Note:** This endpoint requires the manageSettings permission. Use keepCustom: true to preserve your custom categories while restoring defaults. This action cannot be undone, so consider the impact on existing products.
      operationId: ProductCategoriesController_restoreProductCategoriesDefaults
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RestoreProductCategoriesDto'
      responses:
        '200':
          description: Product categories restored to defaults successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Restore default product categories
      tags:
        - administration
        - product-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/roles:
    get:
      description: |-
        **What are Roles?**
        Roles define what users can see and do in the workspace. Each role has a set of permissions that control access to features like creating projects, managing tasks, viewing reports, and more.

        **What is returned:**
        This endpoint returns all roles available in the workspace, including:
        - **Base roles**: System-defined roles that cannot be modified (Root, CEO, Team Leader, Staff, Guest)
        - **Custom roles**: User-created roles that can be edited and deleted

        **Role properties:**
        - Each role includes name, description, icon, and type (normal or guest)
        - The readOnly flag indicates if a role is a base role (cannot be modified)
        - Parent role relationships show permission inheritance hierarchy
        - Roles are returned in the user's preferred language

        **Use cases:**
        - Display role selection dropdowns in user management interfaces
        - Show available roles when assigning permissions to users
        - Build role management interfaces that distinguish between base and custom roles
      operationId: RolesController_listRoles
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RoleListItem'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List roles
      tags:
        - administration
        - roles
      x-required-permissions:
        - roles.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        **What this does:**
        Creates a new custom role in the workspace. Custom roles can be edited and deleted, unlike base roles which are system-defined and read-only.

        **Role types:**
        - **normal**: For internal team members (employees, managers, etc.)
        - **guest**: For external users like clients or partners with restricted access

        **Permission inheritance:**
        - Set a parent role ID to inherit permissions from that role
        - Inherited permissions can be overridden by explicitly setting them in the permissions map
        - This allows creating role variants without duplicating permission configurations

        **Permission validation:**
        - Permissions are automatically validated against disallowed permissions for the role type
        - Guest roles have restrictions on certain permissions (e.g., cannot manage workspace settings)
        - Invalid permissions are rejected with appropriate error messages

        **Required fields:**
        - name: Display name for the role
        - icon: Icon identifier (e.g., "ri-user-star-line" or ":person_in_tuxedo:")
        - type: Either "normal" or "guest"

        **Optional fields:**
        - description: Human-readable description of the role's purpose
        - parentId: ID of parent role for permission inheritance
        - permissions: Map of permission keys to boolean values (inherited from parent if not specified)

        **What is returned:**
        Returns the complete role details including the generated role ID, creation timestamp, and all permissions (including inherited ones).

        **Use cases:**
        - Create specialized roles for specific teams or departments
        - Set up guest roles for external collaborators
        - Build role templates that can be customized per workspace
      operationId: RolesController_createRole
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRoleDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleDetailsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create role
      tags:
        - administration
        - roles
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - icon
          - parentId
          - type
          - permissions
          - readOnly
          - createdBy
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - roles.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/permissions:
    get:
      description: |-
        **What are Permissions?**
        Permissions are granular access controls that determine what actions users can perform. Each permission represents a specific capability like creating tasks, deleting projects, or managing employees.

        **What is returned:**
        This endpoint returns all available permissions in the system, organized by functional groups:
        - **Tasks**: Create, edit, delete, move tasks, manage comments, log time
        - **Projects**: Create, edit, delete projects, manage project settings
        - **Employees**: Manage employee data, assign roles, view employee information
        - **Organizations**: View, create, edit external business partners
        - **Roles**: Create, edit, delete custom roles
        - **Company**: Manage company settings and workspace configuration
        - And many more functional areas

        **Permission structure:**
        - Each permission has a unique key (e.g., "projects.create", "tasks.delete")
        - Permissions are grouped by functional area for easier management
        - Permission keys use dot notation: "category.action"

        **Use cases:**
        - Build permission management interfaces that show all available permissions
        - Display permission checkboxes grouped by functional area
        - Validate permission keys before assigning them to roles
        - Generate permission documentation or help text
      operationId: RolesController_listPermissions
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PermissionItem'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List available permissions
      tags:
        - administration
        - roles
      x-required-permissions:
        - roles.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /administration/permission-definitions:
    get:
      description: Returns compact canonical permission definitions. `scope=mine` is resolved from the current authenticated user only; it accepts no user or role identifier. Definitions explain what each permission contributes, not a promise that a requested action or record will be allowed.
      operationId: RolesController_listPermissionDefinitions
      parameters:
        - name: keys
          required: false
          in: query
          description: Comma-separated permission keys to return.
          schema:
            type: array
            items:
              type: string
        - name: group
          required: false
          in: query
          description: Exact shared RBAC permission group.
          schema:
            type: string
        - name: search
          required: false
          in: query
          description: Case-insensitive search over key, group, subgroup, name, and description.
          schema:
            type: string
        - name: scope
          required: false
          in: query
          description: Restrict results to the authenticated caller’s effective permission keys.
          schema:
            type: string
            enum:
              - mine
        - name: limit
          required: false
          in: query
          description: Maximum entries. Defaults to 25 for unscoped catalog browsing and 200 for scope=mine.
          schema:
            minimum: 1
            maximum: 200
            type: number
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PermissionDefinitionItem'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Look up permission definitions for the authenticated caller
      tags:
        - administration
        - roles
      x-required-scopes:
        - api:read
  /administration/roles/{id}:
    get:
      description: |-
        **What is returned:**
        Returns complete information about a specific role, including all its permissions and metadata.

        **Role details include:**
        - Basic information: name, description, icon, type (normal or guest)
        - Permission map: complete list of all permissions with their allowed/disallowed status
        - Inheritance: parent role ID if permissions are inherited
        - Metadata: creation date, last edit date, creator (for custom roles)
        - Read-only status: indicates if this is a base role that cannot be modified

        **Permission values:**
        - true: Permission is explicitly allowed
        - false: Permission is explicitly disallowed
        - undefined/missing: Permission is inherited from parent role (if parent exists)

        **Base roles:**
        Base roles (Root, CEO, Team Leader, Staff, Guest) are read-only and cannot be modified. Their permissions are predefined by the system.

        **Use cases:**
        - Display role details in role management interfaces
        - Show permission matrix for a specific role
        - Edit role permissions (for custom roles only)
        - Understand permission inheritance from parent roles
      operationId: RolesController_getRoleDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleDetailsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get role details
      tags:
        - administration
        - roles
      x-required-permissions:
        - roles.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        **What this does:**
        Completely replaces a custom role with new data. All fields must be provided - this is a full replacement, not a partial update.

        **Important restrictions:**
        - Base roles (Root, CEO, Team Leader, Staff, Guest) cannot be updated
        - Only custom roles created by users can be modified
        - Attempting to update a base role will return an error

        **What must be provided:**
        - name: Role display name
        - icon: Icon identifier
        - type: Role type (normal or guest)
        - description: Role description (can be empty string)
        - parentId: Parent role ID (can be undefined)
        - permissions: Complete permission map (all permissions must be specified)

        **Permission validation:**
        - All permissions are validated against disallowed permissions for the role type
        - Guest roles cannot have certain permissions (e.g., workspace administration)
        - Invalid permission combinations are rejected

        **Use cases:**
        - Completely redesign a role's permissions and properties
        - Migrate a role from one parent to another
        - Update role metadata (name, description, icon) along with permissions

        **Note:** For partial updates, use PATCH /roles/:id instead.
      operationId: RolesController_updateRole
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateRoleDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleDetailsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update role
      tags:
        - administration
        - roles
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - icon
          - parentId
          - type
          - permissions
          - readOnly
          - createdBy
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - roles.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        **What this does:**
        Updates only the specified fields of a custom role. Fields that are not provided remain unchanged. This is useful for making small adjustments without resending the entire role configuration.

        **Important restrictions:**
        - Base roles (Root, CEO, Team Leader, Staff, Guest) cannot be updated
        - Only custom roles can be modified
        - Permissions cannot be updated via this endpoint (use PUT /roles/:id/permissions or PATCH /roles/:id/permissions/:permissionKey)

        **What can be updated:**
        - name: Change the role display name
        - description: Update the role description
        - icon: Change the role icon
        - parentId: Change the parent role (permissions will be inherited from new parent)
        - type: Change role type between normal and guest

        **What happens:**
        - Only provided fields are updated
        - Unspecified fields keep their current values
        - If parentId is changed, permissions are recalculated based on the new parent
        - Role metadata (editedAt) is automatically updated

        **Use cases:**
        - Rename a role without changing permissions
        - Update role description or icon
        - Change role type (e.g., convert normal role to guest role)
        - Reassign parent role for permission inheritance

        **Note:** To update permissions, use PUT /roles/:id/permissions or PATCH /roles/:id/permissions/:permissionKey.
      operationId: RolesController_patchRole
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchRoleDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleDetailsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update role
      tags:
        - administration
        - roles
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - icon
          - parentId
          - type
          - permissions
          - readOnly
          - createdBy
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - roles.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What this does:**
        Soft deletes a custom role from the workspace. The role is marked as deleted but not permanently removed from the database.

        **Important restrictions:**
        - Base roles (Root, CEO, Team Leader, Staff, Guest) cannot be deleted
        - Only custom roles created by users can be deleted
        - Attempting to delete a base role will return an error

        **What happens to users:**
        - All users currently assigned to this role are automatically reassigned to the default Staff role
        - This ensures no users are left without a valid role assignment
        - The reassignment happens automatically and cannot be prevented

        **What happens to child roles:**
        - If other roles inherit from this role (have this role as parentId), they will need to be updated separately
        - Child roles are not automatically updated when parent is deleted
        - You should update child roles before deleting the parent, or reassign them to a different parent

        **Use cases:**
        - Remove obsolete roles that are no longer needed
        - Clean up test roles created during development
        - Consolidate roles by removing redundant ones
        - Archive roles that are temporarily not in use

        **Note:** This is a soft delete - the role can potentially be restored if needed (depending on your data retention policies).
      operationId: RolesController_deleteRole
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete role
      tags:
        - administration
        - roles
      x-required-permissions:
        - roles.delete
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/roles/{id}/permissions:
    put:
      description: |-
        **What this does:**
        Completely replaces the permissions map for a custom role. This is a full replacement - all permissions must be specified in the request.

        **Important restrictions:**
        - Base roles cannot have their permissions modified
        - Only custom roles can be updated
        - All permissions must be provided (this is a complete replacement)

        **Permission handling:**
        - Permissions are automatically normalized (removed if false, kept if true)
        - Permissions are validated against disallowed permissions for the role type
        - Guest roles have restrictions on certain permissions
        - Invalid permissions are rejected with error messages

        **What must be provided:**
        - permissions: Complete map of all permissions with boolean values
          - true: Permission is allowed
          - false: Permission is disallowed (will be removed from the map)

        **Permission inheritance:**
        - If the role has a parent, you can override inherited permissions
        - Setting a permission to false explicitly disallows it (even if parent allows it)
        - Setting a permission to true explicitly allows it (overrides parent if parent disallows)

        **Use cases:**
        - Bulk update all permissions for a role
        - Copy permissions from another role by providing its complete permission map
        - Reset role permissions to a known state
        - Apply permission templates to roles

        **Note:** For updating a single permission, use PATCH /roles/:id/permissions/:permissionKey instead.
      operationId: RolesController_replacePermissions
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReplacePermissionsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleDetailsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Replace role permissions
      tags:
        - administration
        - roles
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - icon
          - parentId
          - type
          - permissions
          - readOnly
          - createdBy
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - roles.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/roles/{id}/permissions/{permissionKey}:
    patch:
      description: |-
        **What this does:**
        Grants or revokes a single permission for a custom role. This is the most granular way to update role permissions - perfect for making individual permission changes.

        **Important restrictions:**
        - Base roles cannot have their permissions modified
        - Only custom roles can be updated
        - The permission must be valid and allowed for the role type
        - Guest roles cannot be granted certain permissions (e.g., workspace administration)

        **How it works:**
        - Provide the permission key in the URL path (e.g., "projects.create", "tasks.delete")
        - Set the "allow" field to true to grant the permission, or false to revoke it
        - The permission is validated against disallowed permissions for the role type
        - All other permissions remain unchanged

        **Permission inheritance:**
        - If the role has a parent that grants this permission, setting allow=true explicitly allows it
        - If the role has a parent that grants this permission, setting allow=false explicitly disallows it (overrides inheritance)
        - If the role has a parent that disallows this permission, setting allow=true explicitly allows it (overrides inheritance)

        **What is returned:**
        Returns the complete role details with updated permissions, showing the new state of all permissions including the one that was changed.

        **Use cases:**
        - Grant a specific permission to a role (e.g., allow "projects.create")
        - Revoke a specific permission from a role (e.g., disallow "tasks.delete")
        - Fine-tune role permissions one at a time
        - Build permission management UIs with individual toggles

        **Example:**
        To grant "projects.create" permission: PATCH /roles/role-id/permissions/projects.create with {"allow": true}
        To revoke "tasks.delete" permission: PATCH /roles/role-id/permissions/tasks.delete with {"allow": false}
      operationId: RolesController_togglePermission
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: permissionKey
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TogglePermissionDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoleDetailsResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Toggle single permission
      tags:
        - administration
        - roles
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - icon
          - parentId
          - type
          - permissions
          - readOnly
          - createdBy
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - roles.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings:
    get:
      description: |-
        Retrieves the current task auto-generation settings for the workspace.

        **What are task auto-generation settings?**
        These settings control whether Leadtime automatically generates task titles, summaries, and icons using AI when creating new tasks. When enabled, the system analyzes the task description and automatically creates:
        - **Title**: A concise, descriptive title based on the description
        - **Summary**: A short summary extracted from the entered text
        - **Icon**: A matching icon that visually represents the task content

        **What is returned:**
        - `tasksAutoGenerateTitle`: Whether AI-generated titles are enabled
        - `tasksAutoGenerateSummary`: Whether AI-generated summaries are enabled
        - `tasksAutoGenerateIcon`: Whether AI-generated icons are enabled

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. These settings affect how tasks are created throughout the workspace.
      operationId: TaskSettingsController_getTaskSettings
      parameters: []
      responses:
        '200':
          description: Task auto-generation settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskAutoGenerationSettingsDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get task auto-generation settings
      tags:
        - administration
        - task-settings
        - task-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates the task auto-generation settings for the workspace. All fields are required.

        **What can be updated:**
        - `tasksAutoGenerateTitle`: Enable or disable automatic title generation
        - `tasksAutoGenerateSummary`: Enable or disable automatic summary generation
        - `tasksAutoGenerateIcon`: Enable or disable automatic icon generation

        **How it works:**
        When enabled, Leadtime uses AI to analyze task descriptions and automatically generate structured content. This saves time and ensures consistent, well-structured tickets throughout the workspace.

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect how all new tasks are created in the workspace.
      operationId: TaskSettingsController_updateTaskSettings
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: tasksAutoGenerateTitle,tasksAutoGenerateSummary,tasksAutoGenerateIcon
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskAutoGenerationSettingsDto'
      responses:
        '200':
          description: Task auto-generation settings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskAutoGenerationSettingsDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update task auto-generation settings
      tags:
        - administration
        - task-settings
        - task-settings
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - tasksAutoGenerateTitle
          - tasksAutoGenerateSummary
          - tasksAutoGenerateIcon
        allowedFields:
          - tasksAutoGenerateTitle
          - tasksAutoGenerateSummary
          - tasksAutoGenerateIcon
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/custom-fields:
    get:
      description: |-
        Retrieves all custom fields configured for tasks in the workspace.

        **What are custom fields?**
        Custom fields allow you to capture structured, project-specific data beyond standard task fields. They help organize information systematically and make data more consistent, reports more meaningful, and processes easier to automate.

        **Common use cases:**
        - Priority levels by customer group or contract type
        - Reference numbers from external systems
        - Technical parameters or version details
        - Checkboxes for internal approvals
        - Dropdowns for classifications, responsibilities, or error categories

        **What is returned:**
        An array of custom field objects, each containing:
        - Field identification (ID, name, description)
        - Field type (Text, TextArea, Number, Date, Checkbox, Select, MultiSelect)
        - Sort order for display
        - Multilingual translations for field name and description
        - Select options (for Select and MultiSelect types) with their own translations

        **Note:** Custom fields are assigned to specific task types and appear in the task form when creating or editing tasks of those types.
      operationId: TaskSettingsController_getTaskCustomFields
      parameters: []
      responses:
        '200':
          description: Task custom fields retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get task custom fields
      tags:
        - administration
        - task-settings
        - task-custom-fields
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new custom field for tasks. All fields are required.

        **Step-by-step process:**
        1. Provide a default name and description (used as fallback when no translation exists)
        2. Select the field type (Text, TextArea, Number, Date, Checkbox, Select, or MultiSelect)
        3. For Select or MultiSelect types, provide select options with default values
        4. Add multilingual translations for field name, description, and select options
        5. The field will be available to assign to task types

        **Field types:**
        - **Text**: Short text entries (e.g., "Responsible reviewer")
        - **TextArea**: Longer text entries (e.g., "Audit result")
        - **Number**: Numeric values
        - **Date**: Date values
        - **Checkbox**: Yes/No information
        - **Select**: Single selection from predefined options (requires selectOptions)
        - **MultiSelect**: Multiple selections from predefined options (requires selectOptions)

        **Multilingual support:**
        Provide translations for field name, description, and select options. Leadtime automatically displays the correct text based on the user language.

        **Note:** After creation, assign this custom field to specific task types to make it appear in task forms.
      operationId: TaskSettingsController_createTaskCustomField
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaskCustomFieldDto'
      responses:
        '201':
          description: Task custom field created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create task custom field
      tags:
        - administration
        - task-settings
        - task-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/custom-fields/{id}:
    put:
      description: |-
        Updates an existing task custom field. All fields are required - provide complete field configuration.

        **What can be updated:**
        - Field name and description
        - Field type (note: changing type may affect existing data)
        - Select options (for Select and MultiSelect types)
        - Multilingual translations

        **Important:** This is a full update - all fields must be provided. Use PATCH endpoints if you need partial updates (not available for custom fields).
      operationId: TaskSettingsController_updateTaskCustomField
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTaskCustomFieldDto'
      responses:
        '200':
          description: Task custom field updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update task custom field
      tags:
        - administration
        - task-settings
        - task-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a task custom field from the workspace.

        **Important considerations:**
        - The field will be removed from all task types where it was assigned
        - Existing task data in this field will be lost
        - This action cannot be undone

        **Note:** Consider removing the field from task types first if you want to preserve historical data.
      operationId: TaskSettingsController_deleteTaskCustomField
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Task custom field deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete task custom field
      tags:
        - administration
        - task-settings
        - task-custom-fields
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/custom-fields/sort:
    post:
      description: |-
        Updates the display order of task custom fields.

        **How it works:**
        Provide an array of custom field IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.

        **Example:**
        If you provide `["field-3", "field-1", "field-2"]`, the fields will be displayed in that order in task forms.

        **Note:** The order affects how fields appear in task creation and editing forms.
      operationId: TaskSettingsController_sortTaskCustomFields
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortTaskCustomFieldsDto'
      responses:
        '200':
          description: Task custom fields reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder task custom fields
      tags:
        - administration
        - task-settings
        - task-custom-fields
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/restore-defaults:
    post:
      description: |-
        Restores default task types, statuses, and work activities to their original state when Leadtime was first installed.

        **What gets restored:**
        - Default task types (Feature, Bug, etc.) are reset to original configuration
        - Default task statuses (New, In Progress, Feedback, Done, Closed, Backlog) are reset
        - Default work activities are reset

        **Options:**
        - `keepCustom: false`: All custom task types, statuses, and activities are removed
        - `keepCustom: true`: Custom items are preserved, but default items are reset

        **Important:** This action cannot be undone. All customizations to default items will be lost.

        **Use case:** Use this when you want to start fresh with default configurations or when customizations have become too complex.
      operationId: TaskSettingsController_restoreDefaults
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RestoreDefaultsDto'
      responses:
        '200':
          description: Task settings restored to defaults successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Restore default task settings
      tags:
        - administration
        - task-settings
        - task-settings
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/activities:
    get:
      description: |-
        Retrieves all work activities configured for the workspace.

        **What are work activities?**
        Work activities categorize the type of work performed on tasks for time tracking purposes. They help track what kind of work was done (e.g., Development, Testing, Management, Design).

        **How they are used:**
        - When users log time on tasks, they select a work activity
        - Activities are assigned to specific task types
        - Time logs are categorized by activity for reporting and analysis

        **What is returned:**
        An array of work activity objects, each containing:
        - Activity ID and name
        - Sort order for display
        - Multilingual translations for activity name

        **Default activities:**
        Leadtime comes with default activities like Development, Management, Design, and Testing. You can add company-specific activities to better match your workflows.
      operationId: TaskSettingsController_getWorkActivities
      parameters: []
      responses:
        '200':
          description: Work activities retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WorkActivityResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get work activities
      tags:
        - administration
        - task-settings
        - work-activities
      x-required-permissions:
        - task-settings.manage
        - tasks.logTime
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new work activity or updates an existing one. If an ID is provided, updates the existing activity; otherwise creates a new one.

        **Step-by-step process:**
        1. Provide a default activity name (used as fallback when no translation exists)
        2. Add multilingual translations for the activity name
        3. Optionally provide an ID to update an existing activity

        **Multilingual support:**
        Provide translations for activity name. Leadtime automatically displays the correct text based on the user language.

        **Examples of work activities:**
        - Development (for developers)
        - Management (for project leads)
        - Design (for designers)
        - Testing (for QA)
        - Review (for code reviews)

        **Note:** After creation, assign this activity to specific task types to make it available for time tracking.
      operationId: TaskSettingsController_saveWorkActivity
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWorkActivityDto'
      responses:
        '201':
          description: Work activity created/updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkActivityResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update work activity
      tags:
        - administration
        - task-settings
        - work-activities
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/activities/{id}:
    put:
      description: |-
        Updates an existing work activity. All fields are required - provide complete activity configuration.

        **What can be updated:**
        - Activity name
        - Multilingual translations

        **Important:** This is a full update - all fields must be provided.
      operationId: TaskSettingsController_updateWorkActivity
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWorkActivityDto'
      responses:
        '200':
          description: Work activity updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkActivityResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update work activity
      tags:
        - administration
        - task-settings
        - work-activities
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a work activity from the workspace.

        **Important considerations:**
        - The activity will be removed from all task types where it was assigned
        - Existing time logs using this activity will retain the activity reference
        - This action cannot be undone

        **Note:** Consider removing the activity from task types first if you want to prevent future use.
      operationId: TaskSettingsController_deleteWorkActivity
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Work activity deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete work activity
      tags:
        - administration
        - task-settings
        - work-activities
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/activities/sort:
    post:
      description: |-
        Updates the display order of work activities.

        **How it works:**
        Provide an array of work activity IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.

        **Example:**
        If you provide `["activity-2", "activity-1", "activity-3"]`, the activities will be displayed in that order in time tracking dropdowns.

        **Note:** The order affects how activities appear in time entry forms and reports.
      operationId: TaskSettingsController_sortWorkActivities
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortWorkActivitiesDto'
      responses:
        '200':
          description: Work activities reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder work activities
      tags:
        - administration
        - task-settings
        - work-activities
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/statuses:
    get:
      description: |-
        Retrieves all task statuses configured for the workspace.

        **What are task statuses?**
        Task statuses represent the progress or phase of a task in its workflow. They help track where tasks are in the process and organize work visually in Kanban boards and lists.

        **Default statuses:**
        Leadtime comes with standard statuses:
        - **New**: Task has been created but not started
        - **In Progress**: Work is actively being done
        - **Feedback**: Task needs input or review
        - **Resolved**: Task is completed but not yet closed
        - **Closed**: Task is finished and archived
        - **Backlog**: Task is planned for later

        **Custom statuses:**
        You can create custom statuses based on these types to match your specific workflows. For example, "Design in Progress" (based on In Progress) or "Awaiting Approval" (based on Feedback).

        **What is returned:**
        An array of task status objects, each containing:
        - Status ID, name, and icon
        - Status type (New, InProgress, Feedback, Resolved, Closed, Backlog)
        - Sort order for display
        - Multilingual translations for status name

        **Note:** Only non-deleted statuses are returned. Deleted statuses are soft-deleted and can be restored if needed.
      operationId: TaskSettingsController_getTaskStatuses
      parameters: []
      responses:
        '200':
          description: Task statuses retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskStatusResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get task statuses
      tags:
        - administration
        - task-settings
        - task-statuses
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new task status. All fields are required.

        **Step-by-step process:**
        1. Provide a status name (e.g., "In Review", "Awaiting Approval")
        2. Select an icon in format `:icon_name:` (e.g., `:hourglass_flowing_sand:`, `:check_mark:`)
        3. Choose the base status type (New, InProgress, Feedback, Resolved, Closed, or Backlog)
        4. Add multilingual translations for the status name

        **Status types:**
        Each custom status must be based on one of these types:
        - **New**: Initial state, task not started
        - **InProgress**: Active work phase
        - **Feedback**: Needs input or review
        - **Resolved**: Completed but not closed
        - **Closed**: Finished and archived
        - **Backlog**: Planned for later

        **Icon format:**
        Icons use emoji shortcodes in the format `:icon_name:`. Examples:
        - `:hourglass_flowing_sand:` for time-based statuses
        - `:check_mark:` for completed statuses
        - `:x:` for cancelled statuses
        - `:rocket:` for active statuses

        **Multilingual support:**
        Provide translations for status name. Leadtime automatically displays the correct text based on the user language.

        **Note:** After creation, assign this status to specific task types to make it available in their workflows.
      operationId: TaskSettingsController_saveTaskStatus
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaskStatusDto'
      responses:
        '201':
          description: Task status created/updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskStatusResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update task status
      tags:
        - administration
        - task-settings
        - task-statuses
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/statuses/{id}:
    put:
      description: |-
        Updates an existing task status. All fields are required - provide complete status configuration.

        **What can be updated:**
        - Status name
        - Icon
        - Status type
        - Multilingual translations

        **Important:** This is a full update - all fields must be provided. If a field is not provided, it will use the existing value. Use the PATCH endpoint for partial updates.
      operationId: TaskSettingsController_updateTaskStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTaskStatusDto'
      responses:
        '200':
          description: Task status updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskStatusResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update task status
      tags:
        - administration
        - task-settings
        - task-statuses
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a task status from the workspace.

        **How soft delete works:**
        - The status is marked as deleted but not permanently removed
        - Existing tasks using this status retain the status reference
        - The status no longer appears in status lists or can be assigned to new tasks
        - The status can potentially be restored if needed

        **Important considerations:**
        - The status will be removed from all task types where it was assigned
        - Tasks currently using this status will keep the status but it will not be available for new assignments
        - This action should be used carefully as it affects workflows

        **Note:** Consider updating tasks to use a different status before deleting if you want to maintain workflow continuity.
      operationId: TaskSettingsController_deleteTaskStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Task status deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete task status
      tags:
        - administration
        - task-settings
        - task-statuses
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Updates specific fields of an existing task status without requiring all fields.

        **What can be updated:**
        Any combination of the following (all optional):
        - Status name
        - Icon
        - Status type
        - Multilingual translations

        **How it works:**
        Only provide the fields you want to update. Fields not provided will remain unchanged. This is useful when you only need to update one aspect of a status (e.g., just the icon or just the name).

        **Example use cases:**
        - Update only the status icon
        - Add or update translations without changing other fields
        - Change status type while keeping name and icon

        **Note:** This endpoint merges provided fields with existing values, unlike PUT which requires all fields.
      operationId: TaskSettingsController_patchTaskStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTaskStatusDto'
      responses:
        '200':
          description: Task status updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskStatusResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update task status
      tags:
        - administration
        - task-settings
        - task-statuses
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/statuses/sort:
    post:
      description: |-
        Updates the display order of task statuses.

        **How it works:**
        Provide an array of task status IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.

        **Example:**
        If you provide `["status-new", "status-in-progress", "status-done"]`, the statuses will be displayed in that order in Kanban boards, dropdowns, and status lists.

        **Note:** The order affects how statuses appear in task workflows and visual boards. Typically, you want statuses ordered to reflect the natural workflow progression.
      operationId: TaskSettingsController_sortTaskStatuses
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortTaskStatusesDto'
      responses:
        '200':
          description: Task statuses reordered successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder task statuses
      tags:
        - administration
        - task-settings
        - task-statuses
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/types:
    get:
      description: |-
        Retrieves all task types configured for the workspace.

        **What are task types?**
        Task types are the backbone of task organization in Leadtime. They define what kind of tickets exist in the system (e.g., Feature, Bug, Support Case) and structure how tasks are created, documented, and handled.

        **What a task type includes:**
        - **Icon and name**: Recognizable symbol and name (e.g., "Server Crash", "Feature Request")
        - **Base type**: Feature (value-adding work) or Bug (bug fix, typically not billable)
        - **Status options**: Which statuses can be used in this task type workflow
        - **Custom fields**: Additional structured data fields beyond standard fields
        - **Work activities**: Which time tracking activities are allowed
        - **Task template**: Pre-filled description structure for new tasks
        - **Required fields**: Fields that must be filled when creating tasks of this type

        **Benefits:**
        - Structure workflows with unified statuses and fields
        - Ensure complete data capture with required fields
        - Enable precise analysis and reporting
        - Customize for different departments or teams

        **What is returned:**
        An array of task type objects, each containing:
        - Type identification (ID, name, icon, base type)
        - Sort order for display
        - Multilingual translations
        - Associated status IDs
        - Associated custom field IDs
        - Associated work activity IDs
        - Task template as HTML
        - Required field names

        **Note:** Task templates are returned as HTML, converted from the internal IDoc format.
      operationId: TaskSettingsController_getTaskTypes
      parameters: []
      responses:
        '200':
          description: Task types retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskTypeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get task types
      tags:
        - administration
        - task-settings
        - task-types
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new task type with the specified configuration. All fields are required.

        **Step-by-step process:**
        1. Provide a name and icon for the task type
        2. Select the base type (Feature or Bug)
        3. Add multilingual translations
        4. Assign status IDs that can be used with this task type
        5. Assign custom field IDs that should appear for this task type
        6. Provide a task template as HTML (will be converted to internal format)
        7. Specify required field names

        **Base types:**
        - **Feature**: Value-adding work (typically billable)
        - **Bug**: Bug fix or maintenance (typically not billable)

        **Task template:**
        Provide the template as HTML. It will be automatically converted to the internal IDoc format. The template structures the description field and can include:
        - Headings and formatting
        - Tables and checklists
        - Editor placeholders (hints that disappear after saving)

        **Required fields:**
        Specify which standard fields must be filled when creating tasks of this type. Common field names: "title", "description", "assignee", "dueDate".

        **Note:** After creation, assign this task type to projects to make it available for task creation.
      operationId: TaskSettingsController_createTaskType
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaskTypeDto'
      responses:
        '201':
          description: Task type created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskTypeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create task type
      tags:
        - administration
        - task-settings
        - task-types
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/types/{id}:
    put:
      description: |-
        Updates an existing task type with all fields. All fields are required - provide complete task type configuration.

        **What can be updated:**
        - Type name and icon
        - Base type (Feature or Bug)
        - Multilingual translations
        - Associated status IDs
        - Associated custom field IDs
        - Task template (provide HTML, will be converted)
        - Required field names

        **Important:** This is a full update - all fields must be provided. If a field is not provided, it will use the existing value. Use the PATCH endpoint for partial updates.

        **Task template handling:**
        If `templateDoc` is provided, it will replace the existing template. If not provided, the existing template is preserved.
      operationId: TaskSettingsController_updateTaskType
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTaskTypeDto'
      responses:
        '200':
          description: Task type updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskTypeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update task type
      tags:
        - administration
        - task-settings
        - task-types
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Updates specific fields of an existing task type without requiring all fields.

        **What can be updated:**
        Any combination of the following (all optional):
        - Type name and icon
        - Base type (Feature or Bug)
        - Multilingual translations
        - Associated status IDs
        - Associated custom field IDs
        - Task template (provide HTML, will be converted)
        - Required field names

        **How it works:**
        Only provide the fields you want to update. Fields not provided will remain unchanged. This is useful when you only need to update one aspect of a task type.

        **Example use cases:**
        - Update only the task template
        - Add or remove custom fields without changing other settings
        - Update translations without modifying workflow
        - Change required fields

        **Task template handling:**
        If `templateDoc` is provided, it will replace the existing template. If not provided, the existing template is preserved.

        **Note:** This endpoint merges provided fields with existing values, unlike PUT which requires all fields.
      operationId: TaskSettingsController_patchTaskType
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchTaskTypeDto'
      responses:
        '200':
          description: Task type updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskTypeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update task type
      tags:
        - administration
        - task-settings
        - task-types
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - type
          - sort
          - description
          - entity
          - translations
          - selectOptions
          - statuses
          - customFields
          - requiredFields
          - templateDoc
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a task type from the workspace.

        **How soft delete works:**
        - The task type is marked as deleted but not permanently removed
        - Existing tasks of this type retain the type reference
        - The task type no longer appears in type lists or can be assigned to new tasks
        - The task type can potentially be restored if needed

        **Important considerations:**
        - The task type will be removed from all projects where it was assigned
        - Tasks currently using this type will keep the type but it will not be available for new task creation
        - This action should be used carefully as it affects workflows

        **Note:** Consider updating existing tasks to use a different type before deleting if you want to maintain workflow continuity.
      operationId: TaskSettingsController_deleteTaskType
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Task type deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete task type
      tags:
        - administration
        - task-settings
        - task-types
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/task-settings/types/sort:
    post:
      description: |-
        Updates the display order of task types.

        **How it works:**
        Provide an array of task type IDs in the desired order. The first ID in the array will have sort order 1, the second will have sort order 2, and so on.

        **Example:**
        If you provide `["type-feature", "type-bug", "type-support"]`, the task types will be displayed in that order in task type dropdowns and selection lists.

        **Note:** The order affects how task types appear when creating new tasks or filtering by type.
      operationId: TaskSettingsController_sortTaskTypes
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortTaskTypesDto'
      responses:
        '200':
          description: Task types reordered successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder task types
      tags:
        - administration
        - task-settings
        - task-types
      x-required-permissions:
        - task-settings.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/object-custom-fields:
    get:
      operationId: ObjectCustomFieldsController_list
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ObjectCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all object custom field definitions (admin)
      tags:
        - administration
        - object-custom-fields
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: ObjectCustomFieldsController_create
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type,objectTypeId
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateObjectCustomFieldDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create object custom field (admin)
      tags:
        - administration
        - object-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
          - objectTypeId
        allowedFields:
          - id
          - name
          - description
          - sort
          - type
          - entity
          - objectTypeId
          - showInList
          - showInHeader
          - translations
          - selectOptions
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/object-custom-fields/sort/{typeId}:
    post:
      operationId: ObjectCustomFieldsController_saveCustomFieldSort
      parameters:
        - name: typeId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveSortDTO'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder object custom fields for an object type (admin)
      tags:
        - administration
        - object-custom-fields
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/object-custom-fields/{id}:
    put:
      operationId: ObjectCustomFieldsController_update
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type,objectTypeId
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateObjectCustomFieldDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update object custom field (admin)
      tags:
        - administration
        - object-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
          - objectTypeId
        allowedFields:
          - id
          - name
          - description
          - sort
          - type
          - entity
          - objectTypeId
          - showInList
          - showInHeader
          - translations
          - selectOptions
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: ObjectCustomFieldsController_remove
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete object custom field (admin)
      tags:
        - administration
        - object-custom-fields
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/categories:
    get:
      description: |-
        Retrieves all project categories configured for the workspace.

        **What are Project Categories?**
        Project categories are used to sort projects by type or purpose. Examples include:
        - New client project
        - Change request
        - Service order

        This categorization makes it easier to analyze project data, for example comparing revenue from existing clients versus new ones.

        **What is returned:**
        - Category ID, name, icon, and description
        - Sort order (for display ordering)
        - Multilingual translations (name and description in multiple languages)

        **Note:** All project categories support bilingual labels. The translations array contains localized names and descriptions that are automatically shown based on the user interface language.
      operationId: ProjectSettingsController_getProjectCategories
      parameters: []
      responses:
        '200':
          description: Project categories retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project categories
      tags:
        - administration
        - project-settings
        - project-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new project category for organizing projects by type or purpose.

        **Required fields:**
        - `name`: Display name for the category (e.g., "Development", "Support")
        - `icon`: Icon identifier in format :icon_name: (e.g., ":rocket:", ":wrench:")
        - `translations`: Array of translations for multilingual support

        **Optional fields:**
        - `description`: Additional context about when to use this category

        **Multilingual support:**
        Provide translations array with language codes (e.g., "en", "de") and corresponding names/descriptions. The system will automatically display the correct language based on user preferences.

        **Note:** Categories are workspace-specific. Once created, they appear as options when creating or editing projects.
      operationId: ProjectSettingsController_createProjectCategory
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectCategoryDto'
      responses:
        '201':
          description: Project category created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project category
      tags:
        - administration
        - project-settings
        - project-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/categories/{id}:
    get:
      description: |-
        Retrieves detailed information about a specific project category by its ID.

        **What is returned:**
        - Category ID, name, icon, and description
        - Sort order
        - Complete translations array with all language variants

        **Use cases:**
        - Display category details in a form
        - Verify category exists before updating
        - Retrieve full translation set for editing
      operationId: ProjectSettingsController_getProjectCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project category retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project category details
      tags:
        - administration
        - project-settings
        - project-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates an existing project category. All fields must be provided (full replacement).

        **Required fields:**
        - `name`: Updated display name
        - `icon`: Updated icon identifier
        - `translations`: Complete translations array (all languages)

        **Optional fields:**
        - `description`: Updated description (can be null)

        **Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields. All translations must be included in the request.
      operationId: ProjectSettingsController_updateProjectCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectCategoryDto'
      responses:
        '200':
          description: Project category updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project category
      tags:
        - administration
        - project-settings
        - project-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Partially updates an existing project category. Only provided fields will be updated.

        **Optional fields (update only what you need):**
        - `name`: Update display name
        - `icon`: Update icon identifier
        - `description`: Update or clear description
        - `translations`: Update translations (partial updates supported)

        **Note:** Fields not included in the request will remain unchanged. This is useful when you only need to update specific properties without fetching the full current state.
      operationId: ProjectSettingsController_patchProjectCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectCategoryDto'
      responses:
        '200':
          description: Project category updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCategoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update project category
      tags:
        - administration
        - project-settings
        - project-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a project category using soft delete (marked as deleted but not permanently removed).

        **Important considerations:**
        - The category will no longer appear in lists or be available for new projects
        - Existing projects using this category will retain their reference
        - Deleted categories can be restored using the restore defaults endpoint

        **Note:** This operation requires workspace settings management permission.
      operationId: ProjectSettingsController_deleteProjectCategory
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project category deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project category
      tags:
        - administration
        - project-settings
        - project-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/categories/sort:
    post:
      description: |-
        Updates the display order of project categories by providing an ordered array of category IDs.

        **How it works:**
        1. Provide an array of category IDs in the desired display order
        2. The first ID in the array will have sort order 0, second will have 1, etc.
        3. Categories not included in the array will maintain their current sort order

        **Example:**
        If you provide ["cat-3", "cat-1", "cat-2"], then:
        - cat-3 will appear first
        - cat-1 will appear second
        - cat-2 will appear third

        **Note:** This affects the order in which categories appear in dropdowns and lists throughout the application.
      operationId: ProjectSettingsController_sortProjectCategories
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortProjectCategoriesDto'
      responses:
        '200':
          description: Project categories reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder project categories
      tags:
        - administration
        - project-settings
        - project-categories
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/statuses:
    get:
      description: |-
        Retrieves all project statuses configured for the workspace.

        **What are Project Statuses?**
        Project statuses describe the major phases a project goes through from planning to completion. They represent the current state of work on a project.

        **Default status types include:**
        - Sales: Customer is interested and would like an offer
        - Requirements: Project is being specified and defined
        - Implementation: Project is being carried out technically or organizationally
        - Quality Management: Results are being checked
        - Billing: Project is being invoiced
        - Done: Job is completely finished

        **What is returned:**
        - Status ID, name, icon, type, and description
        - Sort order (for display ordering)
        - Multilingual translations

        **Note:** The status type determines billing eligibility. Only certain status types allow projects to be billed.
      operationId: ProjectSettingsController_getProjectStatuses
      parameters: []
      responses:
        '200':
          description: Project statuses retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectStatusResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project statuses
      tags:
        - administration
        - project-settings
        - project-statuses
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new project status to represent a phase in your project workflow.

        **Required fields:**
        - `name`: Display name for the status (e.g., "In Progress", "On Hold")
        - `icon`: Icon identifier in format :icon_name: (e.g., ":hourglass_flowing_sand:")
        - `type`: Status type enum value (Sales, Requirements, Implementation, QualityManagement, Billing, Done)
        - `translations`: Array of translations for multilingual support

        **Optional fields:**
        - `description`: Additional context about when to use this status

        **Status type meanings:**
        - Sales: Pre-contract phase, customer interest
        - Requirements: Specification and planning phase
        - Implementation: Active work phase
        - QualityManagement: Testing and review phase
        - Billing: Ready for invoicing
        - Done: Project completed

        **Note:** You can create multiple statuses with the same type to split phases into sub-phases (e.g., split Sales into Pitch, Negotiation, Contract closing).
      operationId: ProjectSettingsController_createProjectStatus
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectStatusDto'
      responses:
        '201':
          description: Project status created successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project status
      tags:
        - administration
        - project-settings
        - project-statuses
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/statuses/{id}:
    get:
      description: |-
        Retrieves detailed information about a specific project status by its ID.

        **What is returned:**
        - Status ID, name, icon, type, and description
        - Sort order
        - Complete translations array with all language variants

        **Use cases:**
        - Display status details in a form
        - Verify status exists before updating
        - Retrieve full translation set for editing
      operationId: ProjectSettingsController_getProjectStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project status retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectStatusResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project status details
      tags:
        - administration
        - project-settings
        - project-statuses
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates an existing project status. All fields must be provided (full replacement).

        **Required fields:**
        - `name`: Updated display name
        - `icon`: Updated icon identifier
        - `type`: Status type enum value
        - `translations`: Complete translations array (all languages)

        **Optional fields:**
        - `description`: Updated description (can be null)

        **Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields. Changing the status type may affect billing eligibility for projects using this status.
      operationId: ProjectSettingsController_updateProjectStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectStatusDto'
      responses:
        '200':
          description: Project status updated successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project status
      tags:
        - administration
        - project-settings
        - project-statuses
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Partially updates an existing project status. Only provided fields will be updated.

        **Optional fields (update only what you need):**
        - `name`: Update display name
        - `icon`: Update icon identifier
        - `type`: Update status type (may affect billing eligibility)
        - `description`: Update or clear description
        - `translations`: Update translations (partial updates supported)

        **Note:** Fields not included in the request will remain unchanged. This is useful when you only need to update specific properties without fetching the full current state.
      operationId: ProjectSettingsController_patchProjectStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectStatusDto'
      responses:
        '200':
          description: Project status updated successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update project status
      tags:
        - administration
        - project-settings
        - project-statuses
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a project status using soft delete (marked as deleted but not permanently removed).

        **Important considerations:**
        - The status will no longer appear in lists or be available for new projects
        - Existing projects using this status will retain their reference
        - Deleted statuses can be restored using the restore defaults endpoint

        **Note:** This operation requires workspace settings management permission. Ensure you have alternative statuses available before deleting commonly used ones.
      operationId: ProjectSettingsController_deleteProjectStatus
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project status deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project status
      tags:
        - administration
        - project-settings
        - project-statuses
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/statuses/sort:
    post:
      description: |-
        Updates the display order of project statuses by providing an ordered array of status IDs.

        **How it works:**
        1. Provide an array of status IDs in the desired display order
        2. The first ID in the array will have sort order 0, second will have 1, etc.
        3. Statuses not included in the array will maintain their current sort order

        **Example:**
        If you provide ["status-3", "status-1", "status-2"], then:
        - status-3 will appear first
        - status-1 will appear second
        - status-2 will appear third

        **Note:** This affects the order in which statuses appear in dropdowns and lists throughout the application.
      operationId: ProjectSettingsController_sortProjectStatuses
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortProjectStatusesDto'
      responses:
        '200':
          description: Project statuses reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder project statuses
      tags:
        - administration
        - project-settings
        - project-statuses
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/phases:
    get:
      description: |-
        Retrieves all project phases (sales pipeline stages) configured for the workspace.

        **What are Project Phases?**
        Project phases map out the steps a project or lead goes through in the sales process. They are used in the integrated Sales CRM and can be customized to match your sales workflow.

        **Default phase types include:**
        - Not contacted: Lead has not been approached yet
        - Research: Information is being gathered about the lead
        - Contact made: Lead has been reached
        - Reminder: A follow-up is planned
        - Not reached: Could not get in touch with the lead
        - Implementation: Project is confirmed and executed

        **Phase type values:**
        - new: Initial/starting phase
        - inProgress: Active work phase
        - done: Completed/closed phase

        **What is returned:**
        - Phase ID, name, icon, type, and description
        - Sort order (for display ordering)
        - Multilingual translations

        **Note:** Phases affect the lanes in the Sales Kanban board. The phase type determines which section of the pipeline the opportunity appears in.
      operationId: ProjectSettingsController_getProjectPhases
      parameters: []
      responses:
        '200':
          description: Project phases retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectPhaseResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project phases
      tags:
        - administration
        - project-settings
        - project-phases
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new project phase (sales pipeline stage) for tracking opportunities through your sales process.

        **Required fields:**
        - `name`: Display name for the phase (e.g., "Qualification", "Proposal Sent")
        - `icon`: Icon identifier in format :icon_name: (e.g., ":calendar:", ":phone:")
        - `type`: Phase type enum value (new, inProgress, done)
        - `translations`: Array of translations for multilingual support

        **Optional fields:**
        - `description`: Additional context about when to use this phase

        **Phase type meanings:**
        - new: Initial phase for new opportunities (appears in "New" column of Kanban)
        - inProgress: Active phase (appears in "In Progress" column of Kanban)
        - done: Completed phase (appears in "Done" column of Kanban)

        **Note:** Phases are used in the Sales CRM Kanban board. Create phases that match your sales workflow to track opportunities from initial contact through to closed deals.
      operationId: ProjectSettingsController_createProjectPhase
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectPhaseDto'
      responses:
        '201':
          description: Project phase created successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project phase
      tags:
        - administration
        - project-settings
        - project-phases
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/phases/{id}:
    get:
      description: |-
        Retrieves detailed information about a specific project phase by its ID.

        **What is returned:**
        - Phase ID, name, icon, type, and description
        - Sort order
        - Complete translations array with all language variants

        **Use cases:**
        - Display phase details in a form
        - Verify phase exists before updating
        - Retrieve full translation set for editing
      operationId: ProjectSettingsController_getProjectPhase
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project phase retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectPhaseResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project phase details
      tags:
        - administration
        - project-settings
        - project-phases
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates an existing project phase. All fields must be provided (full replacement).

        **Required fields:**
        - `name`: Updated display name
        - `icon`: Updated icon identifier
        - `type`: Phase type enum value (new, inProgress, done)
        - `translations`: Complete translations array (all languages)

        **Optional fields:**
        - `description`: Updated description (can be null)

        **Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields. Changing the phase type will affect which Kanban column opportunities appear in.
      operationId: ProjectSettingsController_updateProjectPhase
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectPhaseDto'
      responses:
        '200':
          description: Project phase updated successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project phase
      tags:
        - administration
        - project-settings
        - project-phases
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Partially updates an existing project phase. Only provided fields will be updated.

        **Optional fields (update only what you need):**
        - `name`: Update display name
        - `icon`: Update icon identifier
        - `type`: Update phase type (affects Kanban column placement)
        - `description`: Update or clear description
        - `translations`: Update translations (partial updates supported)

        **Note:** Fields not included in the request will remain unchanged. This is useful when you only need to update specific properties without fetching the full current state.
      operationId: ProjectSettingsController_patchProjectPhase
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectPhaseDto'
      responses:
        '200':
          description: Project phase updated successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update project phase
      tags:
        - administration
        - project-settings
        - project-phases
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a project phase using soft delete (marked as deleted but not permanently removed).

        **Important considerations:**
        - The phase will no longer appear in lists or be available for new opportunities
        - Existing opportunities using this phase will retain their reference
        - Deleted phases can be restored using the restore defaults endpoint

        **Note:** This operation requires workspace settings management permission. Ensure you have alternative phases available before deleting commonly used ones.
      operationId: ProjectSettingsController_deleteProjectPhase
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project phase deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project phase
      tags:
        - administration
        - project-settings
        - project-phases
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/phases/sort:
    post:
      description: |-
        Updates the display order of project phases by providing an ordered array of phase IDs.

        **How it works:**
        1. Provide an array of phase IDs in the desired display order
        2. The first ID in the array will have sort order 0, second will have 1, etc.
        3. Phases not included in the array will maintain their current sort order

        **Example:**
        If you provide ["phase-3", "phase-1", "phase-2"], then:
        - phase-3 will appear first
        - phase-1 will appear second
        - phase-2 will appear third

        **Note:** This affects the order in which phases appear in dropdowns and the Sales Kanban board.
      operationId: ProjectSettingsController_sortProjectPhases
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortProjectPhasesDto'
      responses:
        '200':
          description: Project phases reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder project phases
      tags:
        - administration
        - project-settings
        - project-phases
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/custom-fields:
    get:
      description: |-
        Retrieves all custom fields configured for projects in the workspace.

        **What are Custom Fields?**
        Custom fields allow you to capture additional information about projects that matters to your company. These fields are flexible and can hold different types of data.

        **Supported field types:**
        - Text: Single-line text input
        - Textarea: Multi-line text input
        - Number: Numeric values
        - Date: Date selection
        - Checkbox: Boolean true/false
        - Select: Single selection from predefined options
        - MultiSelect: Multiple selections from predefined options

        **Common use cases:**
        - Technical information (e.g., Server name, System environment)
        - Sales-relevant data (e.g., Lead source, Probability, Region)
        - Individual extra info for special project types

        **What is returned:**
        - Field ID, name, description, type, and entity (always "Project")
        - Sort order (for display ordering)
        - Multilingual translations
        - Select options (for Select and MultiSelect types)

        **Note:** Custom fields are especially useful in sales CRM and add project management features with specific data points tailored to your business needs.
      operationId: ProjectSettingsController_getProjectCustomFields
      parameters: []
      responses:
        '200':
          description: Project custom fields retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project custom fields
      tags:
        - administration
        - project-settings
        - project-custom-fields
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new custom field for capturing additional project information.

        **Required fields:**
        - `name`: Display name for the field (e.g., "Priority Level", "Region")
        - `type`: Field type enum value (Text, Textarea, Number, Date, Checkbox, Select, MultiSelect)
        - `translations`: Array of translations for multilingual support

        **Optional fields:**
        - `id`: Optional ID for the field (for updating existing fields)
        - `description`: Additional context about the field
        - `selectOptions`: Required for Select and MultiSelect types - array of option objects with:
          - `id`: Optional identifier (use value if not provided)
          - `value`: Display value for the option
          - `translations`: Array of translations for the option name
        - `translationsDescriptions`: Translations for the field description

        **Field type requirements:**
        - Select and MultiSelect types MUST include selectOptions array
        - Other types do not use selectOptions

        **Values in project writes:**
        Use the configured field ID as a key in project `customFields`, with a value matching this definition type. Custom fields store typed data; they do not create native scheduling, relationship, or billing behavior.

        **Note:** Once created, the custom field will appear when creating or editing projects. The field can be reordered using the sort endpoint.
      operationId: ProjectSettingsController_createProjectCustomField
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectCustomFieldDto'
      responses:
        '201':
          description: Project custom field created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project custom field
      tags:
        - administration
        - project-settings
        - project-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - sort
          - type
          - entity
          - translations
          - selectOptions
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/custom-fields/{id}:
    put:
      description: |-
        Updates an existing project custom field. All fields must be provided (full replacement).

        **Required fields:**
        - `name`: Updated display name
        - `type`: Field type enum value
        - `translations`: Complete translations array (all languages)

        **Optional fields:**
        - `description`: Updated description
        - `selectOptions`: Updated options array (required for Select and MultiSelect types)
        - `translationsDescriptions`: Updated description translations

        **Important:**
        - Changing field type may cause data loss if existing values are incompatible
        - For Select/MultiSelect types, all options must be included in the request
        - Removing options that are in use may cause validation errors

        **Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields.
      operationId: ProjectSettingsController_updateProjectCustomField
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectCustomFieldDto'
      responses:
        '200':
          description: Project custom field updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project custom field
      tags:
        - administration
        - project-settings
        - project-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - sort
          - type
          - entity
          - translations
          - selectOptions
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a project custom field using soft delete (marked as deleted but not permanently removed).

        **Important considerations:**
        - The field will no longer appear in project forms
        - Existing project data stored in this field will be retained but not displayed
        - Deleted fields can be restored using the restore defaults endpoint

        **Note:** This operation requires workspace settings management permission. Consider exporting data before deleting fields if you need to preserve historical information.
      operationId: ProjectSettingsController_deleteProjectCustomField
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project custom field deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project custom field
      tags:
        - administration
        - project-settings
        - project-custom-fields
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/custom-fields/sort:
    post:
      description: |-
        Updates the display order of project custom fields by providing an ordered array of field IDs.

        **How it works:**
        1. Provide an array of custom field IDs in the desired display order
        2. The first ID in the array will have sort order 0, second will have 1, etc.
        3. Fields not included in the array will maintain their current sort order

        **Example:**
        If you provide ["field-3", "field-1", "field-2"], then:
        - field-3 will appear first in project forms
        - field-1 will appear second
        - field-2 will appear third

        **Note:** This affects the order in which custom fields appear in project creation and editing forms.
      operationId: ProjectSettingsController_sortProjectCustomFields
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortProjectCustomFieldsDto'
      responses:
        '200':
          description: Project custom fields reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder project custom fields
      tags:
        - administration
        - project-settings
        - project-custom-fields
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/restore-defaults:
    post:
      description: |-
        Restores default project categories, statuses, and phases to their original Leadtime configuration.

        **What gets restored:**
        - Project categories: Default categories like "New Client Project", "Change Request", "Service Order"
        - Project statuses: Default statuses (Sales, Requirements, Implementation, Quality Management, Billing, Done)
        - Project phases: Default sales pipeline phases (Not contacted, Contact made, Won, Closed, etc.)

        **Request body:**
        - `keepCustom`: Boolean flag (default: false)
          - If false: All custom entries are deleted and defaults are restored
          - If true: Custom entries are preserved, defaults are added alongside them

        **Important considerations:**
        - This operation cannot be undone
        - Existing projects will retain their current category/status/phase assignments
        - Custom fields are NOT affected by this operation
        - Deleted default entries will be restored

        **Use cases:**
        - Reset to clean slate after experimentation
        - Restore accidentally deleted default entries
        - Standardize workspace configuration across multiple workspaces
      operationId: ProjectSettingsController_restoreProjectSettingsDefaults
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RestoreProjectSettingsDto'
      responses:
        '200':
          description: Project settings restored to defaults successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Restore default project settings
      tags:
        - administration
        - project-settings
        - project-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/support-contingents/{projectId}:
    get:
      description: |-
        Retrieves all support contingents (retainer plans) configured for a specific project.

        **What are Support Contingents?**
        Support contingents are retainer plans that define allocated hours and pricing for support periods. They help manage support contracts with predefined hours, rates, and transferability of unused hours.

        **What is returned:**
        - All support contingents for the project, ordered by fromDate and toDate
        - Each contingent includes: title, date range, frequency, price, hours, calculated rate, and transferability
        - Date periods must not overlap within the same project

        **Note:** Requires WsSettings.manageSettings permission.
      operationId: ProjectSettingsController_getSupportContingents
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Support contingents retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SupportContingentResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List support contingents for a project
      tags:
        - administration
        - project-settings
        - project-settings
        - support-contingents
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /administration/project-settings/support-contingents:
    post:
      description: |-
        Creates a new support contingent (retainer plan) for a project.

        **Required fields:**
        - `projectId`: ID of the project
        - `title`: Name of the contingent plan
        - `fromDate`: Start date (ISO 8601 date format)
        - `toDate`: End date (ISO 8601 date format)
        - `frequency`: Frequency in months
        - `price`: Total price for the period
        - `hours`: Total hours allocated

        **Optional fields:**
        - `transferable`: Whether unused hours can be transferred (default: false)

        **Validation:**
        - Date periods must not overlap with existing contingents in the same project
        - Rate is automatically calculated as price / hours
        - All numeric values must be non-negative

        **Note:** Requires WsSettings.manageSettings permission.
      operationId: ProjectSettingsController_createSupportContingent
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSupportContingentDto'
      responses:
        '201':
          description: Support contingent created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create support contingent
      tags:
        - administration
        - project-settings
        - project-settings
        - support-contingents
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-settings/support-contingents/{id}:
    put:
      description: |-
        Updates an existing support contingent. All fields must be provided (full replacement).

        **Required fields:**
        - `title`: Updated title
        - `fromDate`: Updated start date
        - `toDate`: Updated end date
        - `frequency`: Updated frequency
        - `price`: Updated price
        - `hours`: Updated hours

        **Optional fields:**
        - `transferable`: Updated transferability flag

        **Note:** This is a full update operation. Use PATCH endpoint for partial updates.
      operationId: ProjectSettingsController_updateSupportContingent
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSupportContingentDto'
      responses:
        '200':
          description: Support contingent updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update support contingent
      tags:
        - administration
        - project-settings
        - project-settings
        - support-contingents
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Partially updates an existing support contingent. Only provided fields will be updated.

        **Optional fields (update only what you need):**
        - `title`: Update title
        - `fromDate`: Update start date
        - `toDate`: Update end date
        - `frequency`: Update frequency
        - `price`: Update price
        - `hours`: Update hours
        - `transferable`: Update transferability

        **Note:** Fields not included in the request will remain unchanged.
      operationId: ProjectSettingsController_patchSupportContingent
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchSupportContingentDto'
      responses:
        '200':
          description: Support contingent updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update support contingent
      tags:
        - administration
        - project-settings
        - project-settings
        - support-contingents
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a support contingent using soft delete (marked as deleted but not permanently removed).

        **Important considerations:**
        - The contingent will no longer appear in lists
        - This operation cannot be undone

        **Note:** Requires WsSettings.manageSettings permission.
      operationId: ProjectSettingsController_deleteSupportContingent
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Support contingent deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete support contingent
      tags:
        - administration
        - project-settings
        - project-settings
        - support-contingents
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /imports-management/grid:
    get:
      description: |-
        Retrieves a paginated, filterable, and sortable list of all CSV imports in the workspace.

        **What are Imports?**
        The imports feature allows you to bulk import data from CSV files into Leadtime. You can import entities like Employees, Organizations, Projects, Tasks, and more. The system uses AI to automatically suggest field mappings, and you can preview results before executing.

        **What is returned:**
        - Paginated list of imports with metadata (file name, status, row/column counts, timestamps)
        - Supports server-side filtering, sorting, and pagination
        - Quick search available on file names

        **Import Status Values:**
        - `Uploaded`: Import created and ready for mapping/execution
        - `Processing`: Import is currently running in the background
        - `Completed`: Import finished successfully
        - `Failed`: Import encountered errors during processing

        Retrieves a paginated grid of imports with filtering and sorting capabilities. Quick search available on: fileName. Filterable fields: fileName, status, rowCount, columnCount, createdAt, importedAt. Sortable fields: fileName, status, rowCount, columnCount, createdAt, importedAt.
      operationId: ImportsManagementController_getImportsGrid
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: fileName'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **fileName** (string): Import file name
            - **status** (set): Import status (Uploaded, Processing, Completed, Failed)
            - **rowCount** (number): Number of rows in the imported file
            - **columnCount** (number): Number of columns in the imported file
            - **createdAt** (date): Import creation timestamp
            - **importedAt** (date): Import completion timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **fileName** (string): Import file name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **status** (set): Import status (Uploaded, Processing, Completed, Failed) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **rowCount** (number): Number of rows in the imported file (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **columnCount** (number): Number of columns in the imported file (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **createdAt** (date): Import creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **importedAt** (date): Import completion timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **fileName**: Import file name
            - **status**: Import status (Uploaded, Processing, Completed, Failed)
            - **rowCount**: Number of rows in the imported file
            - **columnCount**: Number of columns in the imported file
            - **createdAt**: Import creation timestamp
            - **importedAt**: Import completion timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** fileName, status, rowCount, columnCount, createdAt, importedAt
          schema:
            example: fileName,status
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved imports grid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportsGridResponse'
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get imports grid
      tags:
        - imports-management
      x-required-permissions:
        - ws.performImports
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /imports-management/upload:
    post:
      description: |-
        Uploads a CSV file and automatically creates an import with AI-powered field mapping suggestions.

        **What this does:**
        This is the first step in the import workflow. When you upload a CSV file, the system:
        1. Parses the CSV file and extracts column metadata (names, data types, sample values)
        2. Stores the file data and first 10 sample rows for preview
        3. Uses AI to analyze the CSV structure and suggest field mappings
        4. Generates mapping suggestions for supported entity types (Employee, Organization, Project, Task, etc.)
        5. Returns the import details with suggested mappings ready for review

        **Supported Entity Types:**
        - `Employee`: Import employee data (name, email, role, etc.)
        - `Organization`: Import organization/client data
        - `OrganizationMember`: Import organization membership relationships
        - `Project`: Import projects with custom fields support
        - `Task`: Import tasks with custom fields support
        - `TaskComment`: Import task comments

        **File Requirements:**
        - File must be CSV format (text/csv, application/csv, or text/plain MIME types)
        - First row should contain column headers
        - File will be parsed with automatic delimiter detection

        **AI Mapping Features:**
        - Automatically detects which columns map to which entity fields
        - Suggests value mappings for enums (e.g., "Manager" → "manager" role)
        - Handles custom fields for Projects and Tasks
        - Identifies relationships and dependencies (e.g., Organization for OrganizationMember)

        **Next Steps:**
        After upload, review the suggested mappings using GET /{importId}, then:
        1. Modify mappings if needed using PUT /{importId}/mappings
        2. Run a dry-run using POST /{importId}/dry-run to preview results
        3. Execute the import using POST /{importId}/execute to persist data

        **Note:** The AI mapping may not be perfect - always review and test with dry-run before executing.
      operationId: ImportsManagementController_uploadFile
      parameters: []
      requestBody:
        required: true
        description: CSV file to upload
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: CSV file (text/csv, application/csv, or text/plain)
      responses:
        '200':
          description: CSV file uploaded and AI mappings generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadImportResponseDto'
        '400':
          description: Invalid file type or CSV parsing error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Upload CSV file and generate AI mappings
      tags:
        - imports-management
      x-required-permissions:
        - ws.performImports
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /imports-management/{importId}:
    get:
      description: |-
        Retrieves comprehensive information about a specific import, including all mappings, column metadata, and processing statistics.

        **What is returned:**
        - **Import Metadata**: File name, row/column counts, status, creation/completion timestamps, file size
        - **Current Mappings**: All field mappings in AI format, showing which CSV columns map to which entity fields
        - **Column Metadata**: Details about each CSV column (name, data type, sample values)
        - **Sample Rows**: First 10 rows of data for preview
        - **Processing Statistics**: Counts of created/updated/skipped entities (available after execution)

        **Use Cases:**
        - Review mappings before running a dry-run or execution
        - Check import status and progress during background processing
        - Inspect processing results after completion
        - Debug mapping issues by examining column metadata and sample data

        **Import Status Values:**
        - `Uploaded`: Ready for mapping configuration and execution
        - `Processing`: Currently running in the background (check `processedEntities` vs `totalToProcess` for progress)
        - `Completed`: Finished successfully (see `totalCreatedEntities`, `totalUpdatedEntities`, `totalSkippedEntities`)
        - `Failed`: Encountered errors (check error message if available)

        **Mappings Format:**
        Mappings are returned in AI format, where each mapping specifies:
        - Entity type (e.g., "Employee", "Project")
        - Field mappings (which CSV column UUID maps to which entity field)
        - Value mappings (for enums, e.g., "Manager" → "manager")
        - Custom lookup fields (fields used to find existing records for updates)
      operationId: ImportsManagementController_getImportDetails
      parameters:
        - name: importId
          required: true
          in: path
          description: Unique identifier of the import
          schema:
            example: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
            type: string
      responses:
        '200':
          description: Import details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetImportDetailsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Import not found or access denied
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get import details
      tags:
        - imports-management
      x-required-permissions:
        - ws.performImports
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /imports-management/{importId}/mappings:
    put:
      description: |-
        Replaces all field mappings for an import in a single request. This is the primary way to configure how CSV columns map to entity fields.

        **What this does:**
        - Deletes all existing mappings for the import
        - Creates new mappings from your request
        - Automatically adds dependency mappings (e.g., if you map OrganizationMember, it will also create Organization mapping if needed)
        - Validates that all column IDs exist in the import
        - Triggers re-validation of all mappings
        - Returns the updated mappings in AI format

        **AI Mapping Format:**
        Each mapping specifies an entity type and its field mappings:
        ```json
        {
          "mappings": [
            {
              "type": "Employee",
              "fields": {
                "firstName": { "mapTo": "column-uuid-here" },
                "lastName": { "mapTo": "column-uuid-here" },
                "email": { "mapTo": "column-uuid-here" },
                "role": {
                  "mapTo": "column-uuid-here",
                  "valueMappings": { "Manager": "manager", "Developer": "developer" }
                }
              },
              "customLookupFields": ["email"]  // optional: fields to use for finding existing records
            }
          ]
        }
        ```

        **Field Mapping Properties:**
        - `mapTo`: **Required** - The UUID of the CSV column (get column IDs from GET /{importId})
        - `defaultValue`: Optional - Default value if CSV cell is empty
        - `valueMappings`: Optional - Map CSV values to entity values (e.g., "Yes" → true, "Manager" → "manager")

        **Custom Lookup Fields:**
        Specify which fields should be used to find existing records for updates. If a record matches on these fields, it will be updated; otherwise, a new record will be created.
        - Can include both built-in fields (e.g., "email", "firstName") and custom fields (e.g., "customField_abc123")
        - If not specified, uses the entity's default `requiredToUpdate` fields
        - Example: Use "email" for Employees, or a custom "externalId" field for Projects

        **Important Notes:**
        - `mapTo` must be a column UUID from the import (get from GET /{importId} response), not a column name
        - All column IDs must exist in the import
        - Dependencies are automatically created (e.g., Organization for OrganizationMember, Project for Task)
        - Custom fields are referenced as `customField_{fieldId}` in field names
        - After updating, run a dry-run to validate mappings before execution
      operationId: ImportsManagementController_updateMappings
      parameters:
        - name: importId
          required: true
          in: path
          description: Unique identifier of the import
          schema:
            example: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateImportMappingsRequestDto'
      responses:
        '200':
          description: Mappings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateImportMappingsResponseDto'
        '400':
          description: Invalid mapping structure or column IDs
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Import not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update all import mappings in bulk
      tags:
        - imports-management
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
        allowedFields:
          - success
          - mappings
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.performImports
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /imports-management/{importId}/dry-run:
    post:
      description: |-
        Executes a dry-run to preview exactly what will happen when the import is executed, without making any changes to the database.

        **What this does:**
        The dry-run performs a complete simulation of the import process:
        - Validates all field mappings and data types
        - Processes every row in the CSV file
        - Attempts to find existing records using lookup fields
        - Determines which entities will be created, updated, or skipped
        - Identifies validation errors and data quality issues
        - Returns detailed results for each entity type
        - **Does NOT persist any data** - completely safe to run multiple times

        **Why use dry-run?**
        - **Verify mappings**: Ensure CSV columns are correctly mapped to entity fields
        - **Check data quality**: Identify rows with missing required fields, invalid values, or format issues
        - **Estimate impact**: See how many records will be created vs updated before execution
        - **Debug issues**: Get detailed error messages explaining why certain rows will be skipped
        - **Validate lookup fields**: Confirm that custom lookup fields correctly identify existing records

        **What is returned:**
        - **Summary Statistics**: Total rows, counts of entities that will be created/updated/skipped, error count
        - **Detailed Results**: For each mapping type (Employee, Project, etc.), lists of:
          - `toCreate`: New entities that will be created
          - `toUpdate`: Existing entities that will be updated (with old and new values)
          - `ignored`: Rows that will be skipped (with reasons)
        - **Error Details**: List of all rows with errors, including row index, data, and error message

        **Best Practice:**
        Always run a dry-run after configuring mappings and before executing the import. This helps you:
        1. Catch mapping errors early
        2. Fix data quality issues in your CSV
        3. Understand the import impact
        4. Avoid accidentally creating duplicates or updating wrong records
      operationId: ImportsManagementController_dryRun
      parameters:
        - name: importId
          required: true
          in: path
          description: Unique identifier of the import
          schema:
            example: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: dryRun,summary
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: Dry-run completed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DryRunResponseDto'
        '400':
          description: Import has no mappings or invalid state
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Import not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Preview import results without persisting data
      tags:
        - imports-management
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - dryRun
          - summary
        allowedFields:
          - dryRun
          - summary
          - mappings
          - errors
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.performImports
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /imports-management/{importId}/execute:
    post:
      description: |-
        Executes the import to create and update entities in the database. This is the final step that actually persists your data.

        **What this does:**
        - Queues the import for background processing (avoids timeout for large files)
        - Returns immediately with queued status
        - Processes all rows asynchronously in the background
        - Creates new entities and updates existing ones based on lookup fields
        - Updates import status and progress statistics in real-time

        **Before Executing:**
        ⚠️ **Important**: Always run a dry-run first to preview results!
        - Verify mappings are correct using GET /{importId}
        - Run POST /{importId}/dry-run to preview what will happen
        - Review dry-run results and fix any issues
        - Only execute when you're confident the results are correct

        **How to Track Progress:**
        1. Call this endpoint to start execution (returns immediately)
        2. Poll GET /{importId} to check status and progress
        3. Monitor these fields:
           - `status`: Current import status
           - `processedEntities` / `totalToProcess`: Progress percentage
           - `totalCreatedEntities` / `totalUpdatedEntities` / `totalSkippedEntities`: Final counts

        **Import Status Values:**
        - `Uploaded`: Import created, ready for execution
        - `Processing`: Import is currently running (check `processedEntities` for progress)
        - `Completed`: Import finished successfully (see final statistics)
        - `Failed`: Import encountered errors (check error message if available)

        **Processing Performance:**
        - Processing time depends on row count and entity complexity
        - Typically 1-5 seconds per 100 rows
        - Large imports (1000+ rows) may take several minutes
        - Progress is updated in real-time, so you can monitor via GET /{importId}

        **What Happens During Execution:**
        - Each row is processed sequentially
        - System uses lookup fields to find existing records
        - If found: record is updated with new values
        - If not found: new record is created
        - Validation errors cause rows to be skipped
        - Dependencies are automatically created (e.g., Organization before OrganizationMember)
      operationId: ImportsManagementController_execute
      parameters:
        - name: importId
          required: true
          in: path
          description: Unique identifier of the import
          schema:
            example: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
            type: string
      responses:
        '200':
          description: Import queued for execution
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecuteImportResponseDto'
        '400':
          description: Import already processing or invalid state
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Import not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Execute import and persist data
      tags:
        - imports-management
      x-required-permissions:
        - ws.performImports
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /insights/project-chart:
    get:
      description: |-
        **What are Project Insights?**
        Project Insights provides detailed analysis of working hours spent on company projects over time. This endpoint returns data that can be visualized as charts or exported to Excel, showing how time is allocated across projects, team members, task types, and individual tasks.

        **What data is returned:**
        The response includes two "sheets" of data in JSON format (matching the Excel export structure):
        - **Summary sheet**: Wide format (pivot table style) with periods as rows and breakdown categories as columns. Perfect for quick overview and visual chart generation.
        - **Detailed sheet**: Long format (normalized/tidy data) with one row per period/breakdown combination. Ideal for further analysis, pivot tables, and BI tools.

        **Supported analysis options:**
        - **Project selection**: Analyze single project or compare multiple projects side-by-side
        - **Date range**: Auto-calculated from time log data if not provided, or specify custom range
        - **Model selection**: 
          - timeSeries: Shows data over time periods (trends and comparisons)
          - total: Shows aggregated totals across entire period (distribution analysis)
        - **Period granularity**: Group data by months or weeks
        - **Breakdown options**: 
          - none: No breakdown (total hours only)
          - byUser: Breakdown by team member
          - byTaskType: Breakdown by task category
          - byTask: Breakdown by individual task
          - byProject: Breakdown by project (only for multiple projects)
        - **View style** (single project time series only):
          - trend: Cumulative values showing progress over time (ladder-style bars)
          - periodComparison: Period-by-period values (non-cumulative bars)
        - **Display style** (multiple projects time series only):
          - groups: Separate lines/bars for each project (comparison view)
          - stacks: Stacked bars showing combined effort (aggregate view)

        **Data structure:**
        - Headers and titles are fully localized based on user's language preference
        - All entity names (users, projects, tasks) are human-readable (never UUIDs)
        - Numeric values include proper formatting (2 decimal places)
        - Metadata includes query execution time and data processing statistics

        **Use cases:**
        - Generate visual charts showing project time allocation trends
        - Export to Excel for detailed analysis and reporting
        - Track team member contributions over time
        - Analyze task type distribution across projects
        - Monitor project progress against budgets and estimates
      operationId: InsightsController_getProjectChart
      parameters:
        - name: projectIds
          required: true
          in: query
          description: Array of project UUIDs to analyze. Can select single project for detailed analysis or multiple projects for comparison. At least one project is required.
          schema:
            example:
              - 7acff2df-a904-4323-adcc-c2f24a769bde
            type: array
            items:
              type: array
        - name: startDate
          required: false
          in: query
          description: Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the earliest time log entry in the selected projects. Used to determine the beginning of the time series or total aggregation period.
          schema:
            example: '2024-01-01'
            type: string
        - name: endDate
          required: false
          in: query
          description: End date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the latest time log entry in the selected projects. Must be after startDate if both are provided. Used to determine the end of the time series or total aggregation period.
          schema:
            example: '2024-12-31'
            type: string
        - name: model
          required: true
          in: query
          description: Chart model type determining how data is aggregated and displayed. `timeSeries` shows data over time periods (trends and comparisons), while `total` shows aggregated totals across the entire period (distribution analysis). Time series is ideal for tracking progress over time, while total is better for understanding overall distribution.
          schema:
            example: timeSeries
            type: string
            enum:
              - timeSeries
              - total
        - name: period
          required: true
          in: query
          description: Time period granularity for grouping data. `months` groups data by calendar month, `weeks` groups data by week. This determines the x-axis intervals in time series charts and affects how data is aggregated. Months provide broader trends, weeks provide more detailed granularity.
          schema:
            example: months
            type: string
            enum:
              - months
              - weeks
        - name: breakdown
          required: false
          in: query
          description: Optional breakdown dimension for segmenting the data. `none` shows total hours without segmentation. `byUser` breaks down by team member, `byTaskType` by task category, `byTask` by individual task, and `byProject` by project (only available when multiple projects are selected). Breakdowns enable detailed analysis of how time is distributed across different dimensions.
          schema:
            example: byUser
            type: string
            enum:
              - none
              - byUser
              - byTaskType
              - byTask
              - byProject
              - byValueGroups
        - name: viewStyle
          required: false
          in: query
          description: View style for single project time series charts. `trend` shows cumulative values (ladder-style bars showing progress from start to each period), while `periodComparison` shows period-by-period values (non-cumulative bars comparing each period independently). Only applicable when a single project is selected and model is timeSeries.
          schema:
            example: trend
            type: string
            enum:
              - trend
              - periodComparison
        - name: displayStyle
          required: false
          in: query
          description: Display style for multiple projects time series charts. `groups` shows separate lines/bars for each project (comparison view), while `stacks` shows stacked bars combining all projects (aggregate view). Only applicable when multiple projects are selected and model is timeSeries.
          schema:
            example: groups
            type: string
            enum:
              - groups
              - stacks
      responses:
        '200':
          description: Project insights data in structured JSON format with summary and detailed sheets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectChartDataStructuredResponseDto'
        '400':
          description: Invalid request parameters (invalid dates, enum values, etc.)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User does not have permission to view project insights (requires Insights.viewProjectInsights)
        '404':
          description: One or more project IDs not found or user does not have access to them
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project insights data in structured JSON format
      tags:
        - insights
      x-required-permissions:
        - insights.viewProjectInsights
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /insights/staff-chart:
    get:
      description: |-
        **What are Staff Insights?**
        Staff Insights provides detailed analysis of employee time tracking data, showing how team members allocate their working hours across projects, task types, and value categories. This helps managers understand workload distribution, identify productivity patterns, and track employee performance against goals.

        **What data is returned:**
        The response includes two "sheets" of data in JSON format (matching the Excel export structure):
        - **Summary sheet**: 
          - For COMPARISON model: Wide format (pivot table style) with periods/staff as rows and breakdown categories as columns
          - For TOTAL model: Categories with hours and percentage distribution
        - **Detailed sheet**: 
          - For COMPARISON model: Long format (normalized) with one row per period/staff/breakdown combination
          - For TOTAL model: Same structure as summary (aggregated totals)

        **Supported analysis options:**
        - **Staff selection**: Analyze single employee or compare multiple team members
        - **Date range**: Auto-calculated from time log data if not provided, or specify custom range
        - **Model selection**: 
          - COMPARISON: Time-series analysis showing trends over time periods
          - TOTAL: Aggregated totals across entire period (distribution analysis)
        - **Period granularity**: Required for COMPARISON model - group by months or weeks
        - **Breakdown options**: 
          - byProject: Hours allocated per project
          - byTaskType: Hours by task category
          - byChargeableTime: Billable vs non-billable hours with target hours comparison
          - byValueGroups: Hours categorized by value groups (A, B, C, D)

        **Special features:**
        - **Chargeable time breakdown**: Includes target hours column based on employee goals and working time patterns, allowing comparison of actual vs target billable hours
        - **Goal integration**: When using chargeable time breakdown, compares actual hours against employee goals
        - **Localized output**: Headers and titles are fully localized based on user's language preference
        - **Human-readable data**: All entity names (employees, projects, task types) are human-readable (never UUIDs)

        **Use cases:**
        - Track individual employee productivity and time allocation
        - Compare team member workloads across projects
        - Analyze billable vs non-billable time distribution
        - Monitor employee performance against goals and targets
        - Export to Excel for detailed reporting and analysis
      operationId: InsightsController_getStaffChart
      parameters:
        - name: staffIds
          required: true
          in: query
          description: Array of staff member (employee) UUIDs to analyze. Can select single employee for detailed analysis or multiple employees for comparison. At least one employee is required. Goal data comes from employee goal settings and time tracking records.
          schema:
            example:
              - 7acff2df-a904-4323-adcc-c2f24a769bde
            type: array
            items:
              type: array
        - name: model
          required: true
          in: query
          description: Chart model type for staff time analysis. `COMPARISON` shows data over time periods (time-series analysis showing trends), while `TOTAL` shows aggregated totals across the entire period (distribution analysis). COMPARISON requires period granularity, TOTAL does not use periods.
          schema:
            example: comparison
            type: string
            enum:
              - comparison
              - total
        - name: startDate
          required: false
          in: query
          description: Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the earliest time log entry for the selected staff members. Used to determine the beginning of the time series or total aggregation period.
          schema:
            example: '2024-01-01'
            type: string
        - name: endDate
          required: false
          in: query
          description: End date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the latest time log entry for the selected staff members. Must be after startDate if both are provided. Used to determine the end of the time series or total aggregation period.
          schema:
            example: '2024-12-31'
            type: string
        - name: period
          required: true
          in: query
          description: Time period granularity for grouping staff time data. `months` groups data by calendar month, `weeks` groups data by week. Required for COMPARISON model to determine x-axis intervals in time-series charts. Not used for TOTAL model which shows aggregate distribution without time dimension.
          schema:
            example: months
            type: string
            enum:
              - months
              - weeks
        - name: breakdown
          required: true
          in: query
          description: Breakdown dimension for segmenting staff time data. `byProject` breaks down hours allocated per project, `byTaskType` by task category, `byChargeableTime` by billable vs non-billable hours (includes target hours comparison), and `byValueGroups` by value categories (A, B, C, D). Breakdowns enable detailed analysis of how employee time is distributed.
          schema:
            example: byProject
            type: string
            enum:
              - byProject
              - byTaskType
              - byChargeableTime
              - byValueGroups
      responses:
        '200':
          description: Staff insights data in structured JSON format with summary and detailed sheets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StaffChartDataStructuredResponseDto'
        '400':
          description: Invalid request parameters (invalid dates, enum values, missing period for COMPARISON model, etc.)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User does not have permission to view staff insights (requires Insights.viewStaffInsights)
        '404':
          description: One or more staff member IDs not found or user does not have access to them
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get staff insights data in structured JSON format
      tags:
        - insights
      x-required-permissions:
        - insights.viewStaffInsights
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /insights/turnover-chart:
    get:
      description: |-
        **What are Turnover Insights?**
        Turnover Insights provides detailed revenue analysis across projects, showing income from various revenue streams over time. This helps businesses understand revenue patterns, forecast future income, and analyze which projects or revenue types contribute most to the bottom line.

        **What data is returned:**
        The response includes two or three "sheets" of data in JSON format (matching the Excel export structure):
        - **Summary sheet**: Wide format (pivot table style) with periods as rows and breakdown categories as columns. Structure varies by model and breakdown selection.
        - **Detailed sheet**: Long format (normalized) with one row per period/project/revenue type combination. Perfect for pivot tables and further analysis.
        - **Forecast sheet** (optional): Only included when includeTrend=true and forecast data exists. Contains 6-month revenue forecast based on subscription billing cycles and project deadlines.

        **Supported analysis options:**
        - **Project selection**: Analyze single project or compare multiple projects
        - **Date range**: Auto-calculated from invoice data if not provided, or specify custom range
        - **Model selection**: 
          - COMPARISON: Compare revenue across multiple projects side-by-side
          - TOTAL: Aggregate revenue view showing combined totals
        - **Breakdown options** (both work with **COMPARISON** and **TOTAL**):
          - **byProject**: In **COMPARISON**, each period shows one bar per project (project total for that month). In **TOTAL**, periods show stacked contributions by project toward the combined total.
          - **byRevenueType**: In **COMPARISON**, each project’s bar is stacked by revenue type. In **TOTAL**, stacked layers are revenue types on the workspace aggregate.
        - **Forecast option**: includeTrend (only available for **TOTAL** model) — enables 6-month revenue forecast

        **Revenue types included:**
        The analysis includes all revenue types from invoices:
        - Support (hourly billing)
        - Fixed Subscriptions (recurring fixed amounts)
        - Variable Subscriptions (recurring variable amounts)
        - Express Quotations (quick quotes)
        - Products (one-time product sales)
        - Components (product components)
        - Manual Positions (custom invoice line items)

        **Important validation rules:**
        - **includeTrend** is only available for **TOTAL** model (forecast uses the aggregate series)
        - At least one project must be selected

        **Forecast calculation:**
        When includeTrend=true, the forecast sheet includes:
        - 6-month forward-looking revenue projection
        - Based on active subscription billing cycles
        - Includes upcoming project deadlines and milestones
        - Helps with cash flow planning and revenue forecasting

        **Use cases:**
        - Track revenue trends over time
        - Compare revenue performance across projects
        - Analyze revenue distribution by type (subscriptions vs one-time)
        - Forecast future revenue based on active subscriptions
        - Export to Excel for financial reporting and analysis
      operationId: InsightsController_getTurnoverChart
      parameters:
        - name: projectIds
          required: true
          in: query
          description: Array of project UUIDs to analyze for turnover/revenue. Can select single project for detailed analysis or multiple projects for comparison. At least one project is required. Revenue data comes from invoices associated with these projects.
          schema:
            example:
              - 7acff2df-a904-4323-adcc-c2f24a769bde
            type: array
            items:
              type: array
        - name: startDate
          required: false
          in: query
          description: Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the earliest invoice date in the selected projects. Used to determine the beginning of the revenue analysis period.
          schema:
            example: '2024-01-01'
            type: string
        - name: endDate
          required: false
          in: query
          description: End date for the analysis period in ISO 8601 format (YYYY-MM-DD). If not provided, automatically calculated from the latest invoice date in the selected projects. Must be after startDate if both are provided. Used to determine the end of the revenue analysis period.
          schema:
            example: '2024-12-31'
            type: string
        - name: model
          required: true
          in: query
          description: Chart model type for revenue analysis. `comparison` places projects side-by-side per period. `total` aggregates selected projects into one series per period. Use `total` with `includeTrend` for the 6-month forecast.
          schema:
            example: total
            type: string
            enum:
              - comparison
              - total
        - name: breakdown
          required: true
          in: query
          description: 'Breakdown dimension. `byProject`: comparison mode shows each project’s total per period (grouped bars); total mode stacks project contributions on one bar per period. `byRevenueType`: comparison mode stacks revenue types within each project’s bar; total mode stacks revenue types on the aggregate.'
          schema:
            example: byRevenueType
            type: string
            enum:
              - byProject
              - byRevenueType
        - name: includeTrend
          required: false
          in: query
          description: Enable 6-month revenue forecast. Only available for TOTAL model. When true, includes a forecast sheet with forward-looking revenue projections based on active subscription billing cycles and upcoming project deadlines. Helps with cash flow planning and revenue forecasting.
          schema:
            example: false
            type: boolean
      responses:
        '200':
          description: Turnover insights data in structured JSON format with summary, detailed, and optional forecast sheets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TurnoverChartDataStructuredResponseDto'
        '400':
          description: Invalid request parameters (invalid dates or enum values)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User does not have permission to view turnover insights (requires Insights.viewTurnoverInsights)
        '404':
          description: One or more project IDs not found or user does not have access to them
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get turnover insights data in structured JSON format
      tags:
        - insights
      x-required-permissions:
        - insights.viewTurnoverInsights
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /insights/goal-chart:
    get:
      description: |-
        **What are Goal Insights?**
        Goal Insights provides detailed analysis of employee time tracking goal progress, showing how team members perform against their attendance, booked hours, and billable hours targets. This helps managers track productivity, identify employees who need support, and ensure team members are meeting their performance goals.

        **What data is returned:**
        The response includes two "sheets" of data in JSON format (matching the Excel export structure):
        - **Goal Progress sheet**: Employee time tracking goals with goal and actual values for:
          - Attendance (optional, based on workspace settings)
          - Booked hours (total time logged)
          - Billable hours (chargeable time logged)
        - **Value Distribution sheet**: Breakdown of hours by value groups (A, B, C, D) showing how time is categorized by value

        **Supported display modes:**
        - **INDIVIDUAL mode**: One row per employee showing their individual goals and actuals. Perfect for tracking individual performance.
        - **TOTAL mode**: Single aggregated row with combined totals across all selected employees. Useful for team-level overview.

        **Time frame options:**
        - TODAY: Current day's goal progress
        - WEEK: Current week's goal progress
        - MONTH: Current month's goal progress
        - QUARTER: Current quarter's goal progress

        **Goal types explained:**
        - **Attendance Goal**: Target hours for employee presence (if attendance tracking is enabled)
        - **Booked Goal**: Target hours for total time logged (all work activities)
        - **Billable Goal**: Target hours for chargeable/billable work (revenue-generating activities)

        **Value groups:**
        Hours are categorized into value groups based on project and task settings:
        - **Group A**: Highest value work (premium projects/tasks)
        - **Group B**: High value work
        - **Group C**: Standard value work
        - **Group D**: Lower value work

        **Special features:**
        - Attendance columns are conditionally included based on workspace settings (only shown if attendance tracking is enabled)
        - All headers and titles are fully localized based on user's language preference
        - Human-readable employee names (never UUIDs)
        - Goal vs actual comparison makes it easy to identify performance gaps

        **Use cases:**
        - Track individual employee goal achievement
        - Monitor team-level goal performance
        - Identify employees who need support to meet goals
        - Analyze value group distribution to optimize time allocation
        - Export to Excel for performance reviews and reporting
      operationId: InsightsController_getGoalChart
      parameters:
        - name: staffIds
          required: true
          in: query
          description: Array of staff member (employee) UUIDs to analyze. Can select single employee for detailed analysis or multiple employees for comparison. At least one employee is required. Goal data comes from employee goal settings and time tracking records.
          schema:
            example:
              - 7acff2df-a904-4323-adcc-c2f24a769bde
            type: array
            items:
              type: array
        - name: timeFrame
          required: true
          in: query
          description: Time period for goal analysis. `TODAY` analyzes current day's goal progress, `WEEK` analyzes current week, `MONTH` analyzes current month, and `QUARTER` analyzes current quarter. Goals are compared against actual time logged during the selected period.
          schema:
            example: month
            type: string
            enum:
              - today
              - week
              - month
              - quarter
        - name: groupUsers
          required: false
          in: query
          description: Display mode for goal data. `INDIVIDUAL` shows one row per employee with their individual goals and actuals (default), perfect for tracking individual performance. `TOTAL` shows a single aggregated row with combined totals across all selected employees, useful for team-level overview.
          schema:
            example: individual
            type: string
            enum:
              - individual
              - total
      responses:
        '200':
          description: Goal insights data in structured JSON format with goal progress and value distribution sheets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GoalChartDataStructuredResponseDto'
        '400':
          description: Invalid request parameters (invalid time frame, enum values, etc.)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User does not have permission to view goal insights (requires Insights.viewGoalInsights)
        '404':
          description: One or more staff IDs not found or user does not have access to them
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get goal insights data in structured JSON format
      tags:
        - insights
      x-required-permissions:
        - insights.viewGoalInsights
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /insights/workload-chart:
    get:
      description: |-
        **What are Workload Insights?**
        Workload Insights provides detailed analysis of open task volumes across projects and categories over time. Unlike Project Insights which focuses on completed work (time spent), Workload Insights examines the number of open tickets to answer forward-looking questions: "How much work is still in the system?" and "Is demand for services increasing or decreasing?"

        **What data is returned:**
        The response includes two "sheets" of data in JSON format (matching the Excel export structure):
        - **Summary sheet**: Simple format with Period Label and Task Count columns. Quick overview of task volumes.
        - **Detailed sheet**: Long format (normalized) with one row per period/breakdown combination. Perfect for detailed analysis and pivot tables.

        **Supported analysis options:**
        - **Project selection**: Analyze single project or compare multiple projects
        - **Date range**: Required for OVER_TIME model, not used for TODAY model
        - **Model selection**: 
          - TODAY: Current snapshot of open tasks (similar to dashboard overview). Shows current state without time dimension.
          - OVER_TIME: Historical trend analysis showing how workload has changed over time. Helps identify if more work is coming in than being processed.
        - **Period granularity**: Required for OVER_TIME model - group by months or weeks
        - **Breakdown options**: 
          - byProject: Task count breakdown by project
          - byTaskType: Task count breakdown by task category
          - byStatus: Task count breakdown by current status (New, In Progress, Feedback, etc.)
          - byAge: Task count breakdown by age groups (how long tasks have been open)

        **What counts as "open" tasks:**
        A task is considered "open" when its status type is NOT Closed. This includes:
        - New tasks
        - Tasks In Progress
        - Tasks awaiting Feedback
        - Tasks in Backlog
        - Resolved tasks (not yet closed)

        Only tasks with status type Closed are excluded from the analysis.

        **Use cases:**
        - Monitor current workload across projects
        - Track workload trends over time to identify bottlenecks
        - Analyze task distribution by type, status, or age
        - Compare workload across multiple projects
        - Identify if demand is increasing faster than capacity
        - Export to Excel for capacity planning and resource allocation

        **Business value:**
        Workload Insights helps managers:
        - Understand current capacity utilization
        - Identify projects with excessive backlogs
        - Plan resource allocation based on workload trends
        - Detect early warning signs of capacity issues
        - Make data-driven decisions about hiring or project prioritization
      operationId: InsightsController_getWorkloadChart
      parameters:
        - name: projectIds
          required: true
          in: query
          description: Array of project UUIDs to analyze for turnover/revenue. Can select single project for detailed analysis or multiple projects for comparison. At least one project is required. Revenue data comes from invoices associated with these projects.
          schema:
            example:
              - 7acff2df-a904-4323-adcc-c2f24a769bde
            type: array
            items:
              type: array
        - name: model
          required: true
          in: query
          description: Chart model type for workload analysis. `TODAY` shows current snapshot of open tasks (similar to dashboard overview) without time dimension - perfect for understanding current capacity. `OVER_TIME` shows historical trend analysis revealing how workload has changed over time - helps identify if demand is increasing faster than capacity.
          schema:
            example: OVER_TIME
            type: string
            enum:
              - TODAY
              - OVER_TIME
        - name: startDate
          required: false
          in: query
          description: Start date for the analysis period in ISO 8601 format (YYYY-MM-DD). Required for OVER_TIME model to determine the beginning of the trend analysis. Not used for TODAY model which shows current snapshot only.
          schema:
            example: '2024-01-01'
            type: string
        - name: endDate
          required: false
          in: query
          description: End date for the analysis period in ISO 8601 format (YYYY-MM-DD). Required for OVER_TIME model to determine the end of the trend analysis. Must be after startDate if both are provided. Not used for TODAY model which shows current snapshot only.
          schema:
            example: '2024-12-31'
            type: string
        - name: periods
          required: false
          in: query
          description: Time period granularity for grouping workload data over time. `MONTHS` groups data by calendar month, `WEEKS` groups data by week. Required for OVER_TIME model to determine x-axis intervals. Not used for TODAY model. Months provide broader trends, weeks provide more detailed granularity.
          schema:
            example: MONTHS
            type: string
            enum:
              - MONTHS
              - WEEKS
        - name: breakdownBy
          required: false
          in: query
          description: Optional breakdown dimension for segmenting workload data. `PROJECT` breaks down by project, `TASK_TYPE` by task category, `STATUS` by current task status (New, In Progress, etc.), and `AGE` by how long tasks have been open (age groups). Breakdowns help identify bottlenecks and distribution patterns.
          schema:
            example: TASK_TYPE
            type: string
            enum:
              - PROJECT
              - TASK_TYPE
              - STATUS
              - AGE
      responses:
        '200':
          description: Workload insights data in structured JSON format with summary and detailed sheets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkloadChartDataStructuredResponseDto'
        '400':
          description: Invalid request parameters (invalid dates, enum values, missing required fields for OVER_TIME model, etc.)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User does not have permission to view workload insights (requires Insights.viewWorkloadInsights)
        '404':
          description: One or more project IDs not found or user does not have access to them
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get workload insights data in structured JSON format
      tags:
        - insights
      x-required-permissions:
        - insights.viewWorkloadInsights
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /administration/email-accounts:
    get:
      description: |-
        Retrieves all email accounts configured for the workspace. Email accounts enable sending and receiving emails through the platform, including integration with helpdesk functionality.

        **Account Types:**
        - **IMAP**: Traditional email accounts with IMAP/SMTP configuration. Can be fully managed via API (create, update, delete).
        - **Google**: Gmail/Google Workspace accounts via OAuth2. Can only be viewed and deleted via API for security reasons.
        - **Office365**: Microsoft 365 accounts via OAuth2. Can only be viewed and deleted via API for security reasons.

        **Response Details:**
        - Returns all accounts with their current status (active, token_expired, smtp_error, imap_error, disconnected)
        - Sensitive fields (passwords, tokens) are excluded from responses
        - Each account includes capabilities indicating what operations are allowed via API
        - Supports optional filtering by account type using the `type` query parameter
      operationId: EmailAccountsController_listEmailAccounts
      parameters:
        - name: type
          required: false
          in: query
          description: Optional filter to return only accounts of a specific type. Omit this parameter to retrieve all account types.
          schema:
            enum:
              - imap
              - google
              - office365
            type: string
      responses:
        '200':
          description: Email accounts retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EmailAccountResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List email accounts
      tags:
        - administration
        - email-accounts
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new IMAP/SMTP email account for sending and receiving emails.

        **Important Restrictions:**
        - Only IMAP/SMTP accounts can be created via API
        - OAuth accounts (Google, Office365) must be configured through the web application for security reasons

        **Required Configuration:**
        - Email address (must be unique within the workspace)
        - IMAP server settings (host, port, username, password) for receiving emails
        - SMTP server settings (host, port, username, password) for sending emails
        - Optional display name for the account

        **Validation:**
        - Email address must be valid and unique within the workspace
        - Ports must be between 1-65535
        - All server configuration fields are required

        **Response:**
        - Returns the created account with all details (excluding passwords)
        - Account status will be set to "active" upon successful creation
      operationId: EmailAccountsController_createEmailAccount
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,email,type,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEmailAccountDto'
      responses:
        '201':
          description: Email account created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountResponseDto'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create IMAP email account
      tags:
        - administration
        - email-accounts
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - email
          - type
          - status
        allowedFields:
          - id
          - email
          - displayName
          - type
          - status
          - lastError
          - createdAt
          - editedAt
          - capabilities
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/email-accounts/{id}:
    get:
      description: |-
        Retrieves detailed information about a specific email account by ID.

        **Response Includes:**
        - Account identification (ID, email, display name)
        - Account type (IMAP, Google, Office365)
        - Current status and last error message (if any)
        - Timestamps (created, last updated)
        - Capabilities indicating what operations are allowed via API

        **Security:**
        - Sensitive fields (passwords, OAuth tokens) are excluded from responses
        - For IMAP accounts, configuration details are visible but passwords are masked
        - For OAuth accounts, only basic information is returned
      operationId: EmailAccountsController_getEmailAccount
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Email account retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get email account details
      tags:
        - administration
        - email-accounts
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Fully updates an existing IMAP email account. This is a full replacement operation - all fields must be provided.

        **Important Restrictions:**
        - Only IMAP/SMTP accounts can be updated via API
        - Attempting to update OAuth accounts (Google, Office365) will result in a 403 Forbidden error
        - OAuth accounts must be updated through the web application for security reasons

        **Update Process:**
        - All fields must be provided (this replaces the entire account configuration)
        - Email address can be changed (must remain unique within workspace)
        - Server settings (IMAP/SMTP) can be updated
        - Passwords can be updated by providing new values

        **Validation:**
        - Same validation rules apply as for account creation
        - Email must be unique if changed
        - All required fields must be present

        **Response:**
        - Returns the updated account with all details (excluding passwords)
        - Account status may change based on new configuration
      operationId: EmailAccountsController_updateEmailAccount
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,email,type,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEmailAccountDto'
      responses:
        '200':
          description: Email account updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountResponseDto'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: OAuth accounts cannot be updated via API
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update IMAP email account
      tags:
        - administration
        - email-accounts
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - email
          - type
          - status
        allowedFields:
          - id
          - email
          - displayName
          - type
          - status
          - lastError
          - createdAt
          - editedAt
          - capabilities
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Partially updates an existing IMAP email account. Only the fields you provide will be updated; all other fields remain unchanged.

        **Important Restrictions:**
        - Only IMAP/SMTP accounts can be updated via API
        - Attempting to update OAuth accounts (Google, Office365) will result in a 403 Forbidden error
        - OAuth accounts must be updated through the web application for security reasons

        **Update Process:**
        - Provide only the fields you want to update
        - Fields not provided will retain their current values
        - You can update individual settings (e.g., just the SMTP password) without changing other fields
        - Email address can be changed (must remain unique within workspace)

        **Common Use Cases:**
        - Update password after expiration
        - Change server hostname or port
        - Update display name
        - Modify individual IMAP or SMTP settings

        **Validation:**
        - Only provided fields are validated
        - Email must be unique if changed
        - Port values must be between 1-65535 if provided

        **Response:**
        - Returns the updated account with all details (excluding passwords)
      operationId: EmailAccountsController_patchEmailAccount
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,email,type,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchEmailAccountDto'
      responses:
        '200':
          description: Email account updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountResponseDto'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: OAuth accounts cannot be updated via API
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update IMAP email account
      tags:
        - administration
        - email-accounts
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - email
          - type
          - status
        allowedFields:
          - id
          - email
          - displayName
          - type
          - status
          - lastError
          - createdAt
          - editedAt
          - capabilities
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes an email account from the workspace. The account is marked as deleted but not permanently removed from the database.

        **Supported Account Types:**
        - Works for all account types (IMAP, Google, Office365)
        - Unlike create/update operations, deletion is allowed for OAuth accounts via API

        **Deletion Process:**
        - Account is marked with a deletion timestamp
        - Account will no longer appear in listing endpoints
        - Associated data may be retained for audit purposes
        - Account cannot be used for sending/receiving emails after deletion

        **Response:**
        - Returns a success confirmation
        - Returns 404 if account does not exist
      operationId: EmailAccountsController_deleteEmailAccount
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Email account deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete email account
      tags:
        - administration
        - email-accounts
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/email-accounts/{id}/test-sending:
    post:
      description: |-
        Sends a test email to verify that the account's SMTP configuration is working correctly.

        **Purpose:**
        - Validates SMTP server connection and authentication
        - Tests email delivery functionality
        - Helps troubleshoot email sending issues

        **Process:**
        - Connects to the account's SMTP server using configured credentials
        - Sends a test email to the specified recipient address
        - Returns success or failure with error details

        **Supported Account Types:**
        - Works best with IMAP accounts (full SMTP configuration)
        - OAuth accounts (Google, Office365) may have limitations depending on token status

        **Request:**
        - Provide the email address where the test email should be sent

        **Response:**
        - Success: Confirms email was sent successfully
        - Failure: Returns error message describing what went wrong (connection issues, authentication failures, etc.)
      operationId: EmailAccountsController_testEmailSending
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,message
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TestEmailSendingDto'
      responses:
        '200':
          description: Test email sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestEmailSendingResponseDto'
        '400':
          description: Failed to send test email
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Test email sending
      tags:
        - administration
        - email-accounts
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - message
        allowedFields:
          - success
          - message
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/email-accounts/{id}/test-receiving:
    post:
      description: |-
        Tests the account's IMAP configuration by connecting to the email server and retrieving the latest email from the inbox.

        **Purpose:**
        - Validates IMAP server connection and authentication
        - Tests email retrieval functionality
        - Helps troubleshoot email receiving issues
        - Verifies that the account can access the mailbox

        **Process:**
        - Connects to the account's IMAP server using configured credentials
        - Retrieves the most recent email from the inbox
        - Returns email metadata (message ID, subject, sender, date)
        - Returns success or failure with error details

        **Supported Account Types:**
        - Works best with IMAP accounts (full IMAP configuration)
        - OAuth accounts (Google, Office365) may have limitations depending on token status

        **Response:**
        - Success: Returns details of the latest email (message ID, subject, sender, date)
        - Failure: Returns error message describing what went wrong (connection issues, authentication failures, no emails found, etc.)
      operationId: EmailAccountsController_testEmailReceiving
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,message
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: Successfully connected and retrieved email
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestEmailReceivingResponseDto'
        '400':
          description: Failed to connect or retrieve email
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Test email receiving
      tags:
        - administration
        - email-accounts
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - message
        allowedFields:
          - success
          - message
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageEmailAccounts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/vat-rates:
    get:
      description: |-
        Retrieves all VAT (Value Added Tax) rates configured for the workspace.

        **What are VAT rates?**
        VAT rates determine the tax percentage applied to invoices and quotes. Since tax rates can change by law, Leadtime allows you to configure multiple VAT periods with different effective dates. The system automatically uses the correct rate based on the date when creating invoices and quotes.

        **What is returned:**
        - All VAT rates for the workspace, sorted by effective date (dateFrom)
        - Each rate includes: ID, VAT percentage (0-100), effective start date, and whether it is the default rate

        **How VAT rates work:**
        - When creating an invoice or quote, the system finds the VAT rate with the most recent dateFrom that is less than or equal to the current date
        - Documents that have already been issued keep their original tax rate
        - You can plan future VAT changes by creating rates with future effective dates

        **Note:** This endpoint requires the WsSettings.manageSettings permission.
      operationId: VatRatesController_listVatRates
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/VatRateResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all VAT rates
      tags:
        - administration
        - vat-rates
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new VAT rate for the workspace. This allows you to configure tax rates that will be automatically applied to invoices and quotes based on their creation date.

        **Use cases:**
        - Set up a new VAT rate that becomes effective on a specific date
        - Plan future VAT changes in advance (e.g., when tax laws change)
        - Configure historical VAT rates for accurate invoicing

        **How to create a VAT rate:**
        1. Provide the VAT percentage (0-100, e.g., 21 for 21%)
        2. Set the effective date (dateFrom) when this rate should start applying
        3. Optionally mark it as the default rate

        **Validation rules:**
        - VAT percentage must be between 0 and 100
        - dateFrom must be a valid date
        - The system validates that dateFrom does not overlap with existing VAT rates
        - If validation fails, you will receive an error message explaining the issue

        **Important notes:**
        - Once the effective date arrives, the new rate automatically applies to all invoices and quotes created from that point forward
        - Documents already issued keep their original tax rate
        - The system finds the correct VAT rate by selecting the one with the most recent dateFrom that is less than or equal to the invoice/quote creation date

        **Note:** This endpoint requires the WsSettings.manageSettings permission.
      operationId: VatRatesController_createVatRate
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateVatRateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create VAT rate
      tags:
        - administration
        - vat-rates
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/vat-rates/{id}:
    put:
      description: |-
        Updates an existing VAT rate with a new percentage and/or effective date.

        **What can be updated:**
        - VAT percentage (0-100)
        - Effective date (dateFrom)

        **Validation rules:**
        - VAT percentage must be between 0 and 100
        - dateFrom must be a valid date
        - The system validates that the updated dateFrom does not overlap with other VAT rates
        - If the VAT rate does not exist, a NotFoundException is returned
        - If validation fails, you will receive an error message explaining the issue

        **Important notes:**
        - Changes to the effective date affect which invoices/quotes use this rate
        - Documents already issued keep their original tax rate
        - The system automatically recalculates which rate applies based on the updated dateFrom

        **Note:** This endpoint requires the WsSettings.manageSettings permission.
      operationId: VatRatesController_updateVatRate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateVatRateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update VAT rate
      tags:
        - administration
        - vat-rates
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a VAT rate from the workspace.

        **Restrictions:**
        - Default VAT rates cannot be deleted (isDefault: true)
        - Only non-default rates can be removed

        **What happens after deletion:**
        - The VAT rate is permanently removed from the workspace
        - Documents that were already issued with this rate are not affected
        - The system will use the next available VAT rate based on dateFrom when creating new invoices/quotes

        **Use cases:**
        - Remove a planned VAT rate that is no longer needed
        - Clean up incorrect or duplicate VAT rate entries

        **Note:** This endpoint requires the WsSettings.manageSettings permission. If you attempt to delete a default rate, the operation will be silently ignored.
      operationId: VatRatesController_deleteVatRate
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete VAT rate
      tags:
        - administration
        - vat-rates
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /help-center/search:
    get:
      description: |-
        Searches current Leadtime documentation with hybrid text and semantic ranking.

        **Use cases:**
        - Build in-product help search
        - Suggest relevant documentation from API clients
        - Resolve documentation slugs before fetching article Markdown

        **What is returned:**
        - Stable documentation slug
        - Breadcrumb and per-locale titles/descriptions
        - Available locales
        - Hybrid relevance score

        Use `GET /help-center/article` with the returned `slug` to fetch the full article Markdown.
      operationId: HelpCenterController_searchHelpCenter
      parameters:
        - name: searchTerm
          required: true
          in: query
          description: Search term used to find relevant help center articles.
          schema:
            example: task types
            type: string
      responses:
        '200':
          description: Matching help center articles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HelpCenterSearchResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '502':
          description: Documentation search request failed upstream
        '503':
          description: Help center search is not configured on this server
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Search help center articles
      tags:
        - help-center
        - common-lists
      x-required-scopes:
        - api:read
  /help-center/article:
    get:
      description: |-
        Fetches the Markdown source of a Leadtime documentation article.

        **How to use this endpoint:**
        1. Call `GET /help-center/search` to find a matching article
        2. Pass the returned `slug` to this endpoint
        3. Optionally set `language` to `de` or `en`

        **What is returned:**
        - Stable slug
        - Selected language
        - Complete article Markdown
        - Breadcrumb and sibling articles
        - A fallback flag when the requested locale was unavailable
      operationId: HelpCenterController_getHelpCenterArticle
      parameters:
        - name: slug
          required: true
          in: query
          description: Documentation slug from the search response.
          schema:
            example: administration/task-settings/custom-task-types
            type: string
        - name: language
          required: false
          in: query
          description: Preferred response language. Defaults to `de`.
          schema:
            enum:
              - de
              - en
            type: string
      responses:
        '200':
          description: Full help center article content
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HelpCenterArticleResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '500':
          description: Unexpected help center integration response
        '502':
          description: Documentation article request failed upstream
        '503':
          description: Help center integration is not configured on this server
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get help center article content
      tags:
        - help-center
        - common-lists
      x-required-scopes:
        - api:read
  /help-center/toc:
    get:
      description: Returns the documentation structure as breadcrumb groups with stable slugs, localized titles, and locale availability. Optionally scope the response to one subtree with `rootPath`.
      operationId: HelpCenterController_getHelpCenterToc
      parameters:
        - name: rootPath
          required: false
          in: query
          description: Optional documentation subtree path.
          schema:
            example: administration/task-settings
            type: string
      responses:
        '200':
          description: Help center documentation structure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HelpCenterTocResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '502':
          description: Documentation structure request failed upstream
        '503':
          description: Help center search is not configured on this server
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get the help center table of contents
      tags:
        - help-center
        - common-lists
      x-required-scopes:
        - api:read
  /administration/project-components/item-details/{itemId}/questions:
    post:
      description: |-
        Creates a new question for a component library item or updates an existing question if `id` is provided in the request body.

        **What are Questions?**
        Questions capture customer-specific requirements or project details in a structured way. They make work packages flexible and reusable by allowing later individualization in projects.

        **Question Types:**
        - **ShortText**: Single-line text input (name, title, keyword)
        - **Editor**: Formatted multi-line text (descriptions, free text)
        - **Checkbox**: Yes/No selection (can impact effort calculation)
        - **Radio**: Single choice from multiple options (can impact effort)
        - **Files**: Upload files (logos, documents, graphics)
        - **Datepicker**: Select a date (deadlines, delivery dates)
        - **Multiplier**: Dynamic calculation based on quantity (e.g., "How many pages?" × duration per page = total effort)
        - **Person**: Select person(s) from project team or clients

        **Key Features:**
        - Answers can automatically affect effort calculation
        - Questions can have conditions (shown only if other questions answered a certain way)
        - Multiplier questions calculate: Answer × Duration per element = Total additional effort
        - In library components, questions are defined but not answered

        For Checkbox/Radio types, at least one option must be provided. Description field accepts HTML or Markdown input and returns HTML format. Only library component items (component.projectId is null) are accessible through this endpoint.
      operationId: ComponentItemQuestionsController_createOrUpdateQuestion
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateQuestionDto'
      responses:
        '200':
          description: Question created or updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  id:
                    type: string
                    example: e57dc37b-7693-4d06-b49c-17084b773aff
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update a question for a library component item
      tags:
        - administration
        - project-components
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{itemId}/questions/{questionId}:
    patch:
      description: |-
        Updates specific fields of an existing question. Only provided fields will be updated.

        **What are Questions?**
        Questions capture customer-specific requirements or project details. They can have different types (ShortText, Editor, Checkbox, Radio, Files, Datepicker, Multiplier, Person) and can affect effort calculation.

        For Checkbox/Radio types, if options are provided, at least one option must be included. Description field accepts HTML or Markdown input and returns HTML format. Only library component item questions (component.projectId is null) are accessible through this endpoint.
      operationId: ComponentItemQuestionsController_updateQuestion
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: questionId
          required: true
          in: path
          description: Question UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateQuestionDto'
      responses:
        '200':
          description: Question updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update a question for a library component item
      tags:
        - administration
        - project-components
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a question from a component library item by setting its deletedAt timestamp.

        **What are Questions?**
        Questions capture customer-specific requirements or project details. They are attached to component items (Work Packages, Todo Lists, Test Suites) and can affect effort calculation when answered in projects.

        Only library component item questions (component.projectId is null) are accessible through this endpoint.
      operationId: ComponentItemQuestionsController_deleteQuestion
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: questionId
          required: true
          in: path
          description: Question UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: Question deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a question from a library component item
      tags:
        - administration
        - project-components
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{itemId}/questions/sort:
    post:
      description: |-
        Reorders questions within a component library item by providing a new sort order array of question IDs.

        **What are Questions?**
        Questions capture customer-specific requirements or project details. They are attached to component items and displayed in the order specified by their sort field.

        All question IDs in the newSortOrder array must belong to the specified item. Only library component items (component.projectId is null) are accessible through this endpoint.
      operationId: ComponentItemQuestionsController_sortQuestions
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortQuestionsDto'
      responses:
        '200':
          description: Questions reordered successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder questions within a library component item
      tags:
        - administration
        - project-components
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/components:
    post:
      description: |-
        Creates a new library component (reusable workspace template) with name, description, icon, tags, and internal notes.

        **What are Library Components?**
        Library components are workspace-wide reusable templates (projectId is null). They serve as blueprints that can be imported into projects to standardize recurring project types. Components connect requirements management, cost calculation, and execution in one unified workflow.

        **After Creating a Component:**
        Once created, you can add items (Epics, Work Packages, Todo Lists, Test Suites) to define the component structure. You can also add questions to capture customer requirements, todos for checklists, and test cases for acceptance testing.

        **Component Placement:**
        The component is automatically appended to the end of the library component list. You can reorder components later using the sort endpoint.

        **Content Format:**
        Description and internal note fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage. When retrieved, this content is converted back to HTML for easy consumption.
      operationId: ComponentLibraryController_createComponent
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateComponentDto'
      responses:
        '201':
          description: Component created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    example: e57dc37b-7693-4d06-b49c-17084b773aff
        '400':
          description: 'Invalid request: missing required fields or invalid UUID format for tags'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new component library component
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/items:
    post:
      description: |-
        Creates a new component item within a library component. Items are the building blocks that structure component templates.

        **What are Component Items?**
        Items are the building blocks within components that organize and structure project work. Each item can be one of four types:

        1. **Epic**: Major sections or phases that group related work packages around a common theme or goal
           - Example: "Technical Implementation", "Design & Development", "Project Kickoff & Preparation"
           - Can contain Work Packages, Todo Lists, or Test Suites
           - Can nest other Epics to create complex hierarchies (arbitrary nesting depth)
           - TimeFrame is not required for Epics

        2. **Work Package**: Self-contained, clearly defined tasks that deliver tangible results
           - Example: "Set up domain and hosting", "Design homepage layout", "Configure CMS"
           - Can have timeframes/estimates (required field)
           - Can contain Questions (to capture customer requirements), Todos (checklist items), and Test Cases (acceptance test steps)
           - Can nest other Work Packages for complex tasks

        3. **Todo List**: Checklists of sub-steps or checkpoints
           - Example: "Kickoff Checklist", "Quality Control Checklist", "Go-live Approval"
           - Perfect for recurring processes (tests, approvals, installation steps)
           - Each todo item within the list can be checked off individually
           - TimeFrame is required

        4. **Test Suite**: Formal acceptance tests for project results
           - Example: "Contact Form Tests", "Responsive Design Check", "SEO Review"
           - Contains multiple test cases with steps and expected results
           - TimeFrame is required

        **Item Placement:**
        Items can be created at the root level of a component or nested under parent items (Epics or other Work Packages).

        **Content Format:**
        The description and internalNote fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage.

        **Validation Rules:**
        TimeFrame is required for all item types except Epic. Only library component items (component.projectId is null) can be created through this endpoint.
      operationId: ComponentLibraryController_createItem
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateLibraryComponentItemDto'
      responses:
        '201':
          description: Component item created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateLibraryComponentItemResponseDto'
        '400':
          description: 'Invalid request: invalid UUID format, missing required fields, or validation errors'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Component not found or does not belong to library (has projectId)
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new component item (Epic, Work Package, Todo List, or Test Suite)
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/library-tree:
    get:
      description: |-
        Returns the complete hierarchical tree structure of the component library, including all components and their items.

        **What is the Component Library?**
        The component library is a workspace-wide catalog of reusable component templates. These templates can be imported into projects to standardize recurring project types.

        **Library Components vs Project Components:**
        - **Library Components** (this endpoint): Workspace-wide templates (projectId is null) - reusable blueprints with no live data
        - **Project Components**: Live instances in projects (projectId is set) - contain answers, evaluations, and can be connected to tasks

        **Component Structure:**
        - **Components**: Top-level containers (e.g., "Website Development", "CRM Onboarding")
        - **Items**: Can be Epics, Work Packages, Todo Lists, or Test Suites
        - Items can be nested to any depth

        This endpoint provides read-only access to the workspace component library for programmatic access.
      operationId: ComponentLibraryController_getLibraryTree
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentLibraryTreeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get component library tree structure
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /administration/project-components/componentsDetails/{id}:
    get:
      description: |-
        Returns full details of a component library component including all nested children and metadata.

        **What are Library Components?**
        Library components are workspace-wide reusable templates (projectId is null). They serve as blueprints that can be imported into projects. Unlike project components, library components contain no live data - no answers to questions, no task connections, and no evaluations.

        All ProseMirror editor content (description, internalNote) is converted to HTML format. Only library components (projectId is null) are accessible through this endpoint.
      operationId: ComponentLibraryController_getComponentDetails
      parameters:
        - name: id
          required: true
          in: path
          description: Component UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentDetailsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get component library component details
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    delete:
      description: |-
        Soft deletes a library component by setting the deletedAt timestamp. The component is marked as deleted but not permanently removed from the database. Automatically cleans up invalid condition targets and updates timeframes.

        **What are Library Components?**
        Library components are workspace-wide reusable templates (projectId is null). They serve as blueprints that can be imported into projects. Only library components (projectId is null) can be deleted through this endpoint.
      operationId: ComponentLibraryController_deleteComponent
      parameters:
        - name: id
          required: true
          in: path
          description: Component UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: Component deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid component ID format (not valid UUID)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Component not found, component does not belong to library (has projectId), or component already deleted
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete (soft delete) a component library component
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: Updates only the provided fields of an existing library component (name, description, icon, tags, internalNote). The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. Only library components (projectId is null) can be updated through this endpoint.
      operationId: ComponentLibraryController_patchComponent
      parameters:
        - name: id
          required: true
          in: path
          description: Component UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchComponentDto'
      responses:
        '200':
          description: Component updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    example: e57dc37b-7693-4d06-b49c-17084b773aff
        '400':
          description: 'Invalid request: invalid UUID format for component ID or tag IDs'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Component not found, component does not belong to library (has projectId), or component already deleted
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update a component library component
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{id}:
    get:
      description: |-
        Returns full details of a component library item including all test cases, todos, questions, conditions, and nested children.

        **What are Component Items?**
        Items are the building blocks within components. Each item can be:
        - **Epic**: Major section or phase (e.g., "Technical Implementation", "Design & Development")
        - **Work Package**: Specific task with deliverables (e.g., "Set up hosting", "Design homepage")
        - **Todo List**: Checklist of actionable items (e.g., "Kickoff Checklist", "Quality Control")
        - **Test Suite**: Acceptance tests for formal approval (e.g., "Contact Form Tests", "SEO Review")

        **Attached Resources:**
        - **Questions**: Capture customer requirements (defined but not answered in library)
        - **Todos**: Checklist items within a Work Package or Todo List
        - **Test Cases**: Individual test steps within a Test Suite

        All ProseMirror editor content is converted to HTML format. Only library component items (component.projectId is null) are accessible through this endpoint.
      operationId: ComponentLibraryController_getItemDetails
      parameters:
        - name: id
          required: true
          in: path
          description: Component item UUID
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentItemDetailsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get component library item details
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /administration/project-components/item-details/{itemId}/todos:
    post:
      description: |-
        Creates a new todo checklist item for a library component item.

        **What are Todos?**
        Todos are checklist items used to track actionable tasks within component items. They are perfect for:
        - Recurring processes (tests, approvals, installation steps)
        - Quality control checkpoints
        - Project preparation steps
        - Final checks before go-live

        Each todo can be checked off individually, supports comments, and tracks completion status. Todos can be attached to Work Packages or Todo List items.

        Description accepts HTML or Markdown format and is converted to IDoc for storage. Only library component items (component.projectId is null) are accessible.
      operationId: ComponentLibraryController_createTodo
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateComponentTodoDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOperationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new todo (checklist item) for a library component item
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{itemId}/todos/{todoId}:
    patch:
      description: Updates title and/or description of an existing todo item. Description accepts HTML or Markdown format and is converted to IDoc for storage. Only todos belonging to library component items are accessible.
      operationId: ComponentLibraryController_updateTodo
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: todoId
          required: true
          in: path
          description: Todo UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440011
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateComponentTodoDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOperationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an existing todo
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: Soft deletes a todo item by setting its deletedAt timestamp. Only todos belonging to library component items are accessible.
      operationId: ComponentLibraryController_deleteTodo
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: todoId
          required: true
          in: path
          description: Todo UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440011
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOperationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a todo (soft delete)
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{itemId}/todos/sort:
    post:
      description: Updates the sort order of todos within a library component item. All todo IDs in newSortOrder must belong to the specified item. Only library component items are accessible.
      operationId: ComponentLibraryController_sortTodos
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortComponentTodosDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOperationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder todos within an item
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{itemId}/testcases:
    post:
      description: |-
        Creates a new test case for a library component item.

        **What are Test Cases?**
        Test cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results.

        **Test Case Structure:**
        - **Title**: Short, clear title (e.g., "Check confirmation email destination")
        - **Description**: What is being tested and why it matters
        - **Steps**: Step-by-step instructions for running the test
        - **Expected Result**: Describes the expected or correct system behavior

        **Evaluation:**
        Test cases can be evaluated as Passed, Failed, or PassedWithReservations. The project is considered finished when all test cases have been passed successfully.

        Editor content (description, steps, expectedResult) accepts HTML or Markdown input and returns HTML output. Only library component items (component.projectId is null) can have testcases created.
      operationId: ComponentLibraryController_createTestCase
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateComponentTestCaseDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentTestCaseResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new test case (acceptance test step) for a library component item
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
        allowedFields:
          - id
          - title
          - description
          - steps
          - expectedResult
          - sort
          - testComment
          - testStatus
          - testedBy
          - testedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{itemId}/testcases/{testCaseId}:
    patch:
      description: Updates an existing test case for a library component item. All fields are optional for partial updates. Editor content (description, steps, expectedResult) accepts HTML or Markdown input and returns HTML output. Only library component test cases can be updated.
      operationId: ComponentLibraryController_updateTestCase
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: testCaseId
          required: true
          in: path
          description: Test case UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440010
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,testStatus
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateComponentTestCaseDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentTestCaseResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an existing testcase
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - testStatus
        allowedFields:
          - id
          - title
          - description
          - steps
          - expectedResult
          - sort
          - testComment
          - testStatus
          - testedBy
          - testedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: Deletes a test case from a library component item (soft delete - sets deletedAt timestamp). Only library component test cases can be deleted.
      operationId: ComponentLibraryController_deleteTestCase
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: testCaseId
          required: true
          in: path
          description: Test case UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440010
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a testcase (soft delete)
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/item-details/{itemId}/testcases/sort:
    post:
      description: Updates the sort order of test cases within a library component item. Provide an array of testcase IDs in the desired order. Only library component test cases can be reordered.
      operationId: ComponentLibraryController_sortTestCases
      parameters:
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortComponentTestCasesDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder testcases within an item
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/items/{id}:
    patch:
      description: Updates only the provided fields of an existing component library item (epic, work package, todo list, test suite). The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. When the conditions array is provided, it completely replaces existing conditions. Conditions with id are updated, conditions without id are created, and conditions not in the array are deleted. Only library component items (component.projectId is null) can be updated through this endpoint.
      operationId: ComponentLibraryController_patchItem
      parameters:
        - name: id
          required: true
          in: path
          description: Component item UUID
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchLibraryComponentItemDto'
      responses:
        '200':
          description: Item updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    example: 3047983b-d798-4aff-8dd4-628cf1900703
        '400':
          description: 'Invalid request: invalid UUID format for item ID, component ID, tag IDs, or invalid type/timeFrame combination'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Item not found, item does not belong to library component, or component not found
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update a component library item
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: Soft deletes a component library item by setting the deletedAt timestamp. The item is marked as deleted but not permanently removed from the database. Automatically cleans up invalid condition targets and updates timeframes. Only library component items (component.projectId is null) can be deleted through this endpoint.
      operationId: ComponentLibraryController_deleteItem
      parameters:
        - name: id
          required: true
          in: path
          description: Component item UUID
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      responses:
        '200':
          description: Item deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteComponentItemResponseDto'
        '400':
          description: Invalid component item ID format (not valid UUID)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Item not found, item does not belong to library component, or item already deleted
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete (soft delete) a component library item
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/tree/sort:
    post:
      description: Allows reordering components and items at the same level, reparenting items to different components or parent items, and moving items between components (automatically updates componentId for all descendants). When parent is null, sorts components. When parent is provided, sorts items within that parent. Automatically handles side-effects including conditions cleanup and timeframe updates. Only library components and items (component.projectId is null) can be reordered through this endpoint.
      operationId: ComponentLibraryController_saveComponentsSort
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveComponentsSortDto'
      responses:
        '200':
          description: Tree structure updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Products sort updated successfully
        '400':
          description: Invalid request format, invalid UUIDs, or invalid type values
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Component/item not found in library
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Move/reorder items in component library tree
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/components/{id}/duplicate:
    post:
      description: Creates a copy of an existing library component including all nested data (items, questions, todos, test cases, and conditions). The duplicated component is automatically named with "(Copy)" suffix and placed immediately after the original component in the sort order. Condition references are automatically updated to point to the new duplicated items. Recalculates conditions and timeframes. Only library components (projectId is null) can be duplicated through this endpoint.
      operationId: ComponentLibraryController_duplicateComponent
      parameters:
        - name: id
          required: true
          in: path
          description: Component UUID to duplicate
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '201':
          description: Component duplicated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DuplicateComponentResponseDto'
        '400':
          description: Invalid component ID format (not a valid UUID)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Component not found or does not belong to library (has projectId)
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Duplicate a component within the library
      tags:
        - administration
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/project-components/components/{id}/recalculate-estimates:
    post:
      description: |-
        Recalculates time estimates for all items (epics and non-epics) within a library component. This includes:
        - Recalculating individual item estimates based on timeFrame and question multipliers/extra hours
        - Recalculating epic estimates by summing their children's estimates
        - Updating the component's totalTimeFrame
        - Respecting visibility conditions (items with invalid conditions are excluded)

        Items are processed bottom-up (children first, then parents) to ensure accurate calculations. Only library components (projectId is null) can be recalculated through this endpoint.
      operationId: ComponentLibraryController_recalculateComponentEstimates
      parameters:
        - name: id
          required: true
          in: path
          description: Component UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: Estimates recalculated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid component ID format (not a valid UUID)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to edit component library
        '404':
          description: Component not found or does not belong to library (has projectId)
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Recalculate all estimates for a library component
      tags:
        - administration
      x-required-permissions:
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tags/{type}/list:
    get:
      description: |-
        Retrieves all tags for the specified type (task, project, or project-component) in the workspace.

        **What are Tags?**
        Tags in Leadtime enable cross-project organization, visual marking, and filtering of tasks, projects, and project components by topics or processes. They help you link related work across different projects and quickly find all entries with a specific tag.

        **Tag Types:**
        - **Task tags**: Used to label and organize individual tasks/tickets across all projects
        - **Project tags**: Used to categorize and organize entire projects
        - **Project component tags**: Used in project component library to label work packages, checklists, and test suites. These tags are automatically inherited by all elements underneath (cascading tags), making it easy to organize tasks that belong together.

        **What is returned:**
        Returns an array of tag objects, each containing:
        - Tag ID (UUID)
        - Tag name
        - Tag color (predefined color code, optional)
        - Deletion status (soft-deleted tags are marked but not removed)

        **Permission requirements:**
        - Task tags: No permission required (accessible to all authenticated users)
        - Project tags: No permission required (accessible to all authenticated users)
        - Project component tags: Requires ProjectComponentsLibrary.view OR Projects.manageComponents permission
      operationId: TagsController_list
      parameters:
        - name: type
          required: true
          in: path
          description: |-
            The type of tags to work with. Determines which tag category you are accessing:
            - "task": Tags for individual tasks/tickets across all projects
            - "project": Tags for categorizing entire projects
            - "project-component": Tags used in project component library (with cascading inheritance)
            - "organization": Tags for categorizing organizations
            - "object": Tags for workspace object instances
            - "form-template": Tags for workspace form templates (administration)
          schema:
            example: task
            type: string
            enum:
              - task
              - project
              - project-component
              - organization
              - object
              - form-template
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TagResponseDto'
        '400':
          description: Invalid tag type
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all tags of specified type
      tags:
        - tags
        - common-lists
      x-required-scopes:
        - api:read
  /tags/{type}/create:
    post:
      description: |-
        Creates a new tag of the specified type. If a tag with the same name already exists (case-insensitive), returns the existing tag instead of creating a duplicate.

        **How tag creation works:**
        - Provide a tag name (required)
        - The system checks for existing tags with the same name (case-insensitive)
        - If a tag exists, it is returned (no duplicate created)
        - If no tag exists, a new one is created
        - Tag color can be set later using the save endpoint

        **Tag Colors:**
        Tags use predefined color codes, not hex values. Available colors: `default`, `red`, `yellow`, `green`, `blue`, `purple`, `fuchsia`, `pink`, `magenta`.

        **Use cases:**
        - Create standard tags for recurring topics (e.g., "Billing", "Support", "Onboarding")
        - Organize tasks by process or workflow stage
        - Enable cross-project filtering and analysis
        - Set up cascading tags in project components for automatic inheritance

        **Best practices:**
        - Use consistent naming to avoid duplicates (e.g., do not use both "Billing" and "Invoice")
        - Create standard tags for recurring topics to keep your team organized
        - Use colors strategically for visual organization (set via save endpoint)

        **Permission requirements:**
        - Task tags: Requires Tasks.createTags permission
        - Project tags: Must be an employee (not organization member)
        - Project component tags: Requires ProjectComponentsLibrary.edit OR Projects.manageComponents permission
      operationId: TagsController_create
      parameters:
        - name: type
          required: true
          in: path
          description: |-
            The type of tags to work with. Determines which tag category you are accessing:
            - "task": Tags for individual tasks/tickets across all projects
            - "project": Tags for categorizing entire projects
            - "project-component": Tags used in project component library (with cascading inheritance)
            - "organization": Tags for categorizing organizations
            - "object": Tags for workspace object instances
            - "form-template": Tags for workspace form templates (administration)
          schema:
            example: task
            type: string
            enum:
              - task
              - project
              - project-component
              - organization
              - object
              - form-template
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTagDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagResponseDto'
        '400':
          description: Invalid tag type or duplicate name
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new tag
      tags:
        - tags
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - color
          - isDeleted
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /tags/{type}/save:
    post:
      description: |-
        Updates an existing tag name and color. This endpoint allows you to rename tags or change their visual appearance.

        **What can be updated:**
        - **Tag name**: Change the tag label (must be unique within the tag type, case-insensitive)
        - **Tag color**: Set or change the predefined color code for visual organization

        **How it works:**
        - Provide the tag ID and new name (required)
        - Optionally provide a predefined color code (e.g., "red", "blue", "green")
        - The system validates that the new name does not conflict with existing tags
        - The system validates that the color is one of the predefined values
        - All entities using this tag will automatically reflect the changes

        **Tag Colors:**
        Tags use predefined color codes, not hex values. Available colors:
        - `default` - Light gray (default color)
        - `red` - Red
        - `yellow` - Yellow
        - `green` - Green
        - `blue` - Blue
        - `purple` - Purple
        - `fuchsia` - Fuchsia
        - `pink` - Pink
        - `magenta` - Magenta

        **Use cases:**
        - Rename tags for better consistency
        - Add or change colors for visual organization
        - Standardize tag naming across the workspace

        **Color strategy tips:**
        - Use green for active processes
        - Use blue for customer-related items
        - Use red for issues or urgent items

        **Permission requirements:**
        - Task tags: Requires Tasks.manageTags permission
        - Project tags: Must be an employee (not organization member)
        - Project component tags: Requires ProjectComponentsLibrary.edit OR Projects.manageComponents permission
      operationId: TagsController_save
      parameters:
        - name: type
          required: true
          in: path
          description: |-
            The type of tags to work with. Determines which tag category you are accessing:
            - "task": Tags for individual tasks/tickets across all projects
            - "project": Tags for categorizing entire projects
            - "project-component": Tags used in project component library (with cascading inheritance)
            - "organization": Tags for categorizing organizations
            - "object": Tags for workspace object instances
            - "form-template": Tags for workspace form templates (administration)
          schema:
            example: task
            type: string
            enum:
              - task
              - project
              - project-component
              - organization
              - object
              - form-template
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveTagDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagResponseDto'
        '400':
          description: Invalid tag type, duplicate name, or invalid color value
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Tag not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an existing tag
      tags:
        - tags
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - color
          - isDeleted
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /tags/{type}/{id}:
    delete:
      description: |-
        Soft deletes a tag and automatically removes it from all associated entities (tasks, projects, or components). The tag is marked as deleted but not permanently removed from the database.

        **How soft deletion works:**
        - The tag is marked as deleted (isDeleted flag set to true)
        - The tag is automatically removed from all entities that were using it
        - Tasks, projects, or components will no longer show this tag
        - The tag will not appear in list endpoints
        - Historical data is preserved (the tag still exists in the database)

        **Use cases:**
        - Remove tags that are no longer needed
        - Clean up duplicate or incorrectly named tags
        - Archive tags that are no longer in use

        **Note:** Soft deletion means the tag can potentially be restored if needed, but this endpoint does not provide restoration functionality.

        **Permission requirements:**
        - Task tags: Requires Tasks.manageTags permission
        - Project tags: Must be an employee (not organization member)
        - Project component tags: Requires ProjectComponentsLibrary.edit OR Projects.manageComponents permission
      operationId: TagsController__delete
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagResponseDto'
        '400':
          description: Invalid tag type
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Tag not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Soft delete a tag
      tags:
        - tags
        - common-lists
      x-required-scopes:
        - api:write
  /tasks/{identifier}/form-instances:
    get:
      description: Returns every form instance on the task, including `templateId`, current `values`, and a `fields` array for each instance. Each field includes `valueKey` — use that string as the property name in the `values` object when calling PATCH `/tasks/{identifier}/form-instances/{formInstanceId}`. To choose which template to attach next, list templates with GET `/workspace/form-templates` (template `id` is the `templateId` for POST). For the full static schema of a template (before attach), use GET `/workspace/form-templates/{id}`.
      operationId: TaskFormInstancesController_list
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or shortNumber
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List form instances on a task
      tags:
        - tasks
        - form-instances
      x-required-scopes:
        - api:read
    post:
      description: Creates a new form instance from a workspace template. Pass `templateId` from GET `/workspace/form-templates` (each item includes `id`) or the legacy GET `/administration/form-templates-available`. To review fields, labels, and validation constraints before attaching, call GET `/workspace/form-templates/{id}` with that template id. After attach, fill values with PATCH `/tasks/{identifier}/form-instances/{formInstanceId}` (get `formInstanceId` from GET this resource list).
      operationId: TaskFormInstancesController_attach
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or shortNumber
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,taskId,templateId,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachTaskFormInstanceDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Attach a form template to a task
      tags:
        - tasks
        - form-instances
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - taskId
          - templateId
          - title
        allowedFields:
          - id
          - taskId
          - templateId
          - title
          - name
          - fields
          - values
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.attachOrRemoveFormInstances
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/form-instances/{formInstanceId}:
    patch:
      description: Merges the provided `values` into the instance (see request body schema). Keys in `values` must match `fields[].valueKey` from GET `/tasks/{identifier}/form-instances` for this instance. For allowed types and select options, either read `fields` on that GET response or load the template definition with GET `/workspace/form-templates/{templateId}` using the instance `templateId`.
      operationId: TaskFormInstancesController_patchValues
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or shortNumber
          schema:
            type: string
        - name: formInstanceId
          required: true
          in: path
          description: Form instance UUID from GET `/tasks/{identifier}/form-instances` (`id` on each item).
          schema:
            format: uuid
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,taskId,templateId,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchTaskFormInstanceDto'
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update stored values for a form instance
      tags:
        - tasks
        - form-instances
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - taskId
          - templateId
          - title
        allowedFields:
          - id
          - taskId
          - templateId
          - title
          - name
          - fields
          - values
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.fillFormInstances
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: Deletes the instance from the task. Use `formInstanceId` from GET `/tasks/{identifier}/form-instances`. To attach again later, pick a template id from GET `/workspace/form-templates`.
      operationId: TaskFormInstancesController_remove
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or shortNumber
          schema:
            type: string
        - name: formInstanceId
          required: true
          in: path
          description: Form instance UUID from GET `/tasks/{identifier}/form-instances`.
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Remove a form instance from a task
      tags:
        - tasks
        - form-instances
      x-required-permissions:
        - tasks.attachOrRemoveFormInstances
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/agent-sessions:
    get:
      operationId: TaskAgentSessionsController_listTaskSessions
      parameters:
        - name: identifier
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - bearer: []
        - OAuth2:
            - api:read
        - bearer: []
      summary: List agent sessions for a task
      tags:
        - agent-sessions
        - tasks
      x-required-scopes:
        - api:read
  /tasks/{identifier}/products:
    post:
      description: |-
        **What are Task Products?**
        Task products allow you to add standardized services or goods from your product catalog directly to tasks (tickets). This enables quick pricing for support requests, service tickets, or express quotes without creating a full project. Products can include variants (e.g., Standard, Pro, Enterprise) and options (e.g., additional services or upgrades).

        **How this endpoint works:**
        This endpoint imports a product from your workspace product catalog into a specific task. The product is copied into the task context, allowing you to customize quantity, select variants, and configure options without affecting the original catalog product.

        **What you need to provide:**
        - **productId**: The UUID of the product from your catalog (must exist in catalog, not already assigned to a task or project)
        - **quantity**: How many units of this product (minimum 1)
        - **activeVariantId**: (Optional) Which variant to activate if the product has multiple variants
        - **options**: Array of option selections, where each option specifies which values are selected

        **Important notes:**
        - Only products from the catalog (not already assigned to tasks or projects) can be imported
        - The product is copied into the task - changes here do not affect the catalog version
        - Products support three pricing models: fixed price, subscription price, and per-unit pricing
        - Variants allow different configurations (e.g., different package sizes or service levels)
        - Options allow customers to add extra services or upgrades (e.g., "Add workshop" or "Enable premium support")

        **What is returned:**
        Returns the ID of the newly created task product, which can be used for subsequent updates or deletion.

        **Permission requirements:**
        Requires "manageProducts" permission on tasks and "api:write" scope.
      operationId: TaskProductsController_importProductToTask
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task identifier. Can be either a UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") or a numeric shortNumber (e.g., "123"). The shortNumber is a human-readable task number assigned by the system.
          schema:
            example: '123'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImportProductToTaskDto'
      responses:
        '201':
          description: Product imported successfully
          schema:
            example:
              success: true
              id: 550e8400-e29b-41d4-a716-446655440000
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Bad Request - Invalid input or validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no task access
        '404':
          description: Not Found - Catalog product or task does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Import product from catalog to task
      tags:
        - tasks
        - products
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/products/{productId}:
    patch:
      description: |-
        **What this endpoint does:**
        Updates an existing product that has been imported into a task. This endpoint supports partial updates - you only need to provide the fields you want to change. All other fields remain unchanged.

        **What can be updated:**
        - **Basic information**: Name, description (HTML or Markdown), category, logo
        - **Pricing**: Fixed price, subscription price, per-unit price, unit name, billing frequency
        - **Variants**: Add, update, or modify product variants (e.g., Standard, Pro, Enterprise versions)
        - **Options**: Modify product options and their values (e.g., additional services, upgrades)

        **Important notes:**
        - This is a PATCH operation - only provided fields are updated
        - Description can be provided as HTML or Markdown - it will be automatically converted to the internal document format
        - Variant descriptions also support HTML or Markdown
        - When updating variants, you must provide the complete variant structure (existing variants are replaced)
        - When updating options, you must provide the complete options structure (existing options are replaced)
        - Changes only affect the product within this task - the catalog product remains unchanged
        - Product must belong to the specified task

        **Pricing models:**
        - **Fixed price**: One-time payment amount
        - **Subscription price**: Recurring payment (e.g., monthly, yearly)
        - **Price per unit**: Calculated per quantity (e.g., per user, per hour)
        - **Price frequency**: Billing interval for subscriptions (in months)

        **What is returned:**
        Returns success status and the product ID.

        **Permission requirements:**
        Requires "manageProducts" permission on tasks and "api:write" scope.
      operationId: TaskProductsController_patchProduct
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task identifier. Can be either a UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") or a numeric shortNumber (e.g., "123"). The shortNumber is a human-readable task number assigned by the system.
          schema:
            example: '123'
            type: string
        - name: productId
          required: true
          in: path
          description: UUID of the product that was imported into the task. This is the ID returned when importing a product via the POST endpoint.
          schema:
            format: uuid
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchTaskProductDto'
      responses:
        '200':
          description: Product updated successfully
          schema:
            example:
              success: true
              id: 550e8400-e29b-41d4-a716-446655440000
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Bad Request - Invalid input or validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no task access
        '404':
          description: Not Found - Product or task does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update task product
      tags:
        - tasks
        - products
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What this endpoint does:**
        Removes a product from a task. This performs a soft delete - the product is marked as deleted but not permanently removed from the database. This preserves historical data for auditing and reporting purposes.

        **Important notes:**
        - Product must belong to the specified task
        - This is a soft delete - the product record remains in the database with deletedAt timestamp set
        - Deleted products are excluded from normal queries but can be accessed for historical purposes
        - The original catalog product is not affected by this deletion
        - Once deleted, the product cannot be restored through this API (requires database-level operation)

        **What is returned:**
        Returns success status.

        **Permission requirements:**
        Requires "manageProducts" permission on tasks and "api:write" scope.
      operationId: TaskProductsController_deleteProduct
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task identifier. Can be either a UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") or a numeric shortNumber (e.g., "123"). The shortNumber is a human-readable task number assigned by the system.
          schema:
            example: '123'
            type: string
        - name: productId
          required: true
          in: path
          description: UUID of the product that was imported into the task. This is the ID returned when importing a product via the POST endpoint.
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Product deleted successfully
          schema:
            example:
              success: true
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no task access
        '404':
          description: Not Found - Product or task does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete task product
      tags:
        - tasks
        - products
      x-required-permissions:
        - tasks.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/products/{productId}/settings:
    post:
      description: |-
        **What this endpoint does:**
        Updates the configuration settings for a product that has been imported into a task. This includes quantity, which variant is active, and which option values are selected. This is different from updating the product details (name, description, prices) - use PATCH for those changes.

        **What can be configured:**
        - **quantity**: How many units of this product (minimum 1)
        - **activeVariantId**: Which variant is currently active (if product has variants)
        - **options**: Which option values are selected for each product option

        **Use cases:**
        - Adjust quantity after initial import
        - Switch between variants (e.g., from Standard to Pro)
        - Change option selections (e.g., add or remove premium support)
        - Configure product for specific customer requirements

        **Important notes:**
        - This endpoint only updates configuration, not product details (name, description, prices)
        - To update product details, use the PATCH endpoint instead
        - Product must belong to the specified task
        - All option selections must be provided (partial updates not supported for options)

        **What is returned:**
        Returns success status.

        **Permission requirements:**
        Requires "manageProducts" permission on tasks and "api:write" scope.
      operationId: TaskProductsController_updateProductSettings
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task identifier. Can be either a UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") or a numeric shortNumber (e.g., "123"). The shortNumber is a human-readable task number assigned by the system.
          schema:
            example: '123'
            type: string
        - name: productId
          required: true
          in: path
          description: UUID of the product that was imported into the task. This is the ID returned when importing a product via the POST endpoint.
          schema:
            format: uuid
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskProductSettingsDto'
      responses:
        '200':
          description: Product settings updated successfully
          schema:
            example:
              success: true
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Bad Request - Invalid input or validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no task access
        '404':
          description: Not Found - Product or task does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update task product settings
      tags:
        - tasks
        - products
      x-required-permissions:
        - tasks.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/express-quotations:
    post:
      description: |-
        Creates a new express quotation for the specified task. Express quotations are pricing estimates that can be sent to contacts and, when accepted, replace time-based billing with the quotation amount.

        **What are Express Quotations?**
        Express quotations provide a way to offer fixed-price estimates for tasks instead of billing by time. They:
        - Link pricing estimates directly to tasks
        - Can be sent to contacts via email
        - When accepted, override time-based billing for the task
        - Include line items with quantities, unit prices, and options
        - Support tax calculations and multi-language content

        **How items are generated:**
        - Items are automatically created from task products (if any)
        - If `includeEstimate` is true, estimated work time is added as the first item
        - Each item can have options (variants) with their own pricing
        - Prices, quantities, and totals are calculated automatically

        **What is returned:**
        - Complete quotation with all items and options
        - Calculated subtotal, tax amount, and total
        - HTML-formatted comment and item descriptions
        - Quotation status (initially Pending)
        - Contact user and language information

        **Note:** The task identifier can be either a UUID or numeric shortNumber. The quotation is automatically assigned a sequential shortNumber per task.
      operationId: ExpressQuotationsController_create
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,taskId,shortNumber,status,total
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateExpressQuotationDto'
      responses:
        '201':
          description: Express quotation created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpressQuotationResponseDto'
        '400':
          description: Invalid input data or task identifier
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: No access to task project
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create express quotation
      tags:
        - tasks
        - express-quotations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - taskId
          - shortNumber
          - status
          - total
        allowedFields:
          - id
          - taskId
          - shortNumber
          - status
          - contactUserId
          - language
          - comment
          - items
          - subTotal
          - total
          - tax
          - taxAmount
          - createdAt
          - updatedAt
          - sentAt
          - createdBy
          - statusChangedBy
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.manageExpressQuotations
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    get:
      description: |-
        Returns all express quotations for the specified task, ordered by creation date (newest first).

        **What is returned:**
        - All non-deleted quotations for the task
        - Each quotation includes:
          - Full item list with options
          - Pricing details (subtotal, tax, total)
          - Status (Pending, Accepted, or Rejected)
          - Contact user and language
          - HTML-formatted comments and descriptions
          - Creation, update, and sent timestamps

        **Quotation statuses:**
        - **Pending**: Quotation created but not yet accepted or rejected
        - **Accepted**: Quotation accepted, will be used for billing instead of time
        - **Rejected**: Quotation rejected, will not be used for billing

        **Note:** Only one quotation per task can be Accepted at a time. When a quotation is accepted, all other Pending or Accepted quotations for the same task are automatically rejected.
      operationId: ExpressQuotationsController_list
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
      responses:
        '200':
          description: Express quotations retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExpressQuotationResponseDto'
        '400':
          description: Invalid task identifier
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: No access to task project
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List express quotations
      tags:
        - tasks
        - express-quotations
      x-required-scopes:
        - api:read
  /tasks/{identifier}/express-quotations/{quotationId}:
    get:
      description: |-
        Returns complete details of a specific express quotation, including all items, options, and pricing calculations.

        **What is returned:**
        - Quotation metadata (ID, shortNumber, status, timestamps)
        - Contact user and language information
        - HTML-formatted comment
        - All items with:
          - Title and HTML-formatted description
          - Unit type (Hour or Piece)
          - Quantity, unit price, and total price
          - All options (variants) with their pricing
        - Financial summary:
          - Subtotal (before tax)
          - Tax rate and tax amount
          - Total (after tax)
        - User tracking (created by, status changed by)
        - Email tracking (sentAt timestamp if sent)

        **Use cases:**
        - View quotation details before sending
        - Review pricing breakdown
        - Check quotation status and history
      operationId: ExpressQuotationsController_getById
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: quotationId
          required: true
          in: path
          description: Quotation UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      responses:
        '200':
          description: Express quotation retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpressQuotationResponseDto'
        '400':
          description: Invalid task identifier or quotation ID
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: No access to task project
        '404':
          description: Task or quotation not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get single express quotation
      tags:
        - tasks
        - express-quotations
      x-required-scopes:
        - api:read
    delete:
      description: |-
        Soft deletes the quotation by setting the `deletedAt` timestamp. Deleted quotations are excluded from listings and cannot be used for billing.

        **What happens when a quotation is deleted:**
        - The quotation is marked as deleted (soft delete)
        - It will not appear in quotation listings
        - It cannot be accepted, rejected, or sent via email
        - If this was the accepted quotation, the task's `hasAcceptedQuotation` flag is cleared
        - Historical data is preserved for audit purposes

        **Note:** This is a soft delete operation. The quotation data remains in the database but is hidden from normal operations. Deleted quotations cannot be restored through the API.
      operationId: ExpressQuotationsController__delete
      parameters:
        - name: quotationId
          required: true
          in: path
          description: Quotation UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
      responses:
        '200':
          description: Quotation deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid task identifier or quotation ID
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: No access to task project
        '404':
          description: Task or quotation not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete quotation
      tags:
        - tasks
        - express-quotations
      x-required-permissions:
        - tasks.manageExpressQuotations
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/express-quotations/{quotationId}/accept:
    post:
      description: |-
        Accepts the quotation, marking it as the active quotation for billing purposes. This action automatically rejects all other Pending or Accepted quotations for the same task.

        **What happens when a quotation is accepted:**
        - The quotation status changes to Accepted
        - All other Pending or Accepted quotations for the same task are automatically rejected
        - The task is marked with `hasAcceptedQuotation = true`
        - The task's `acceptedQuotationSubTotal` is set to this quotation's subtotal
        - When the task is billed, the quotation amount is used instead of time-based billing
        - Notifications are sent to task subscribers

        **Billing impact:**
        Once accepted, the task will use the quotation's subtotal for invoicing instead of calculating billing from `spentTime * hourRate`. The task must still be in Closed or Resolved status and not already billed to be included in invoices.

        **Note:** Only one quotation per task can be Accepted at a time. Accepting a new quotation will automatically reject the previously accepted one.
      operationId: ExpressQuotationsController_accept
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: quotationId
          required: true
          in: path
          description: Quotation UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      responses:
        '200':
          description: Quotation accepted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid task identifier or quotation ID
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: No access to task project
        '404':
          description: Task or quotation not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Accept quotation
      tags:
        - tasks
        - express-quotations
      x-required-permissions:
        - tasks.acceptOrRejectExpressQuotations
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/express-quotations/{quotationId}/reject:
    post:
      description: |-
        Rejects the quotation, marking it as not accepted. Rejected quotations will not be used for billing.

        **What happens when a quotation is rejected:**
        - The quotation status changes to Rejected
        - The quotation will not be used for task billing
        - If this was the accepted quotation, the task's `hasAcceptedQuotation` flag is cleared
        - Notifications are sent to task subscribers

        **Note:** Rejecting a quotation does not affect other quotations for the same task. You can reject multiple quotations, but only one can be Accepted at a time.
      operationId: ExpressQuotationsController_reject
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: quotationId
          required: true
          in: path
          description: Quotation UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      responses:
        '200':
          description: Quotation rejected successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid task identifier or quotation ID
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: No access to task project
        '404':
          description: Task or quotation not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reject quotation
      tags:
        - tasks
        - express-quotations
      x-required-permissions:
        - tasks.acceptOrRejectExpressQuotations
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/express-quotations/{quotationId}/send-email:
    post:
      description: |-
        Sends the quotation as a formatted document via email to the contact user specified in the quotation.

        **What is sent:**
        - A formatted quotation document (PDF or HTML email)
        - Includes all items, options, pricing, and tax calculations
        - Contains the quotation comment and task information
        - Formatted according to the quotation's language setting

        **Requirements:**
        - The contact user must have a valid email address
        - The quotation must not be deleted
        - Email sending must be configured in the workspace

        **What happens:**
        - The quotation's `sentAt` timestamp is updated
        - An email is sent to the contact user's email address
        - The contact user can view and potentially accept/reject the quotation

        **Note:** This endpoint only sends the email. To accept or reject a quotation, use the `/accept` or `/reject` endpoints. The contact user may need to log in or use a special link to interact with the quotation.
      operationId: ExpressQuotationsController_sendEmail
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: quotationId
          required: true
          in: path
          description: Quotation UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      responses:
        '200':
          description: Quotation email sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid task identifier, quotation ID, or contact user has no email
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: No access to task project
        '404':
          description: Task or quotation not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Send quotation email
      tags:
        - tasks
        - express-quotations
      x-required-permissions:
        - tasks.manageExpressQuotations
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/bigPicture:
    get:
      description: |-
        **What is Big Picture?**
        Big Picture is a visual overview of all tasks in a project, organized by task type (like Feature, Bug, or Management). It provides a quick way to assess project status, priorities, bottlenecks, and time efforts without diving into individual task details. This view is especially useful for project members who need a high-level understanding of project progress.

        **Visual structure:**
        The Big Picture displays tasks as cards arranged in horizontal rows. Each row represents a task type (e.g., Feature, Bug, Management). Within each row, tasks are sorted from left to right by priority - tasks further to the left have higher priority than those to the right.

        **What data is returned:**
        The response includes:
        - **Groups**: Array of task groups, where each group represents a task type (row). Each group contains:
          - **Task items (cards)**: Each card represents a single task and includes:
            - Task status (color-coded: New, In Progress, Feedback, Resolved, Backlog)
            - Task ID, title, and short description
            - Priority level and task type
            - Time information: planned hours vs spent hours (e.g., "11.8 h / 40 h")
            - Due date warnings if applicable
            - Comment count and pipeline status indicator
            - Tags and assigned people (responsible person and stakeholders)
          - **Group statistics**: Metrics for that specific task type:
            - Number of resolved tasks
            - Total estimated hours for all tasks in the group
            - Total spent hours across all tasks in the group
            - Resolved hours (time spent on completed tasks)
        - **Overall statistics**: Aggregated metrics across all tasks in the project:
          - Total number of tasks
          - Number of resolved tasks
          - Total estimated hours
          - Total spent hours
          - Resolved hours

        **Task grouping:**
        Tasks are grouped by their task type. Tasks without a recognized type are grouped into a special "other" category. Groups are sorted according to the configured task type order (which can be changed using the group-order endpoint).

        **Use cases:**
        - Quick project status assessment without opening individual tasks
        - Identifying bottlenecks and overload situations by comparing estimated vs spent hours
        - Prioritizing customer requests visually - arrange tasks left to right based on importance
        - Getting an overview when covering for absent team members - see priorities and status at a glance
        - Visual project planning and coordination - understand what needs attention most urgently
        - Communicating priorities to customers - show them which tasks matter most

        **Permissions:**
        Requires BigPicture.canView permission and access to the specified project.

        **Note:** If you need to retrieve all tasks with custom filtering, sorting, or pagination capabilities, consider using the GET /tasks/grid endpoint instead. The grid endpoint provides more flexible filtering options and is better suited for retrieving large datasets or when you need to filter by multiple criteria simultaneously.
      operationId: TasksBigPictureController_getBigPicture
      parameters:
        - name: projectId
          required: true
          in: query
          description: UUID of the project for which to retrieve Big Picture data. The project must exist, not be deleted, and the user must have access to it.
          schema:
            format: uuid
            example: 7225cca9-ac5f-461d-9a9d-ad8d7ee1b597
            type: string
      responses:
        '200':
          description: Big picture data successfully retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigPictureResponseDto'
        '400':
          description: Invalid project ID format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks BigPicture.canView permission or project access
        '404':
          description: Project not found or deleted
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get big picture data for a project
      tags:
        - tasks
        - big-picture
      x-required-permissions:
        - big_picture.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /tasks/bigPicture/order:
    post:
      description: |-
        **What this endpoint does:**
        Reorders tasks within a specific task type group (row) in the Big Picture view. Tasks are arranged from left to right, where tasks further to the left have higher priority than those to the right. This is equivalent to dragging and dropping tasks within a row in the visual interface.

        **How it works:**
        - Provide the project ID and the group ID (task type ID) you want to reorder
        - Provide an array of task IDs in the desired new order (left to right = highest to lowest priority)
        - The order array must include all tasks currently in that group - you cannot omit tasks
        - Tasks are reordered within their group only; this does not move tasks between groups or change task types

        **Visual behavior:**
        After reordering, tasks will appear in the Big Picture view in the new sequence. The leftmost task becomes the highest priority item for that task type, making it easy to see what should be worked on first.

        **Use cases:**
        - Prioritizing tasks within a category (e.g., which features are most important to customers)
        - Adjusting task order based on customer feedback during meetings
        - Reorganizing tasks after planning discussions or sprint planning
        - Setting up task sequence for team members to follow
        - Responding to urgent requests by moving critical tasks to the left

        **Validation:**
        - Project must exist and not be deleted
        - Group ID must be a valid task type ID for the project
        - Order array must contain valid task IDs that belong to the specified group
        - All tasks currently in the group must be included in the order array (no tasks can be omitted)

        **Permissions:**
        Requires BigPicture.canEdit permission and access to the specified project.
      operationId: TasksBigPictureController_updateBigPictureOrder
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateBigPictureOrderDto'
      responses:
        '200':
          description: Tasks successfully reordered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid project ID, group ID, or order array
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks BigPicture.canEdit permission or project access
        '404':
          description: Project not found or deleted
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder tasks within a group (task type)
      tags:
        - tasks
        - big-picture
      x-required-permissions:
        - big_picture.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/bigPicture/group-order:
    post:
      description: |-
        **What this endpoint does:**
        Reorders the task type groups (rows) themselves in the Big Picture view. This changes which task types appear first, second, third, etc., from top to bottom in the visual display. This is equivalent to dragging and dropping entire rows in the visual interface.

        **How it works:**
        - Provide the project ID
        - Provide an array of group IDs (task type IDs) in the desired new order (top to bottom)
        - The order array must include all groups currently in the project - you cannot omit groups
        - This reorders entire rows, not individual tasks within rows
        - The first group ID in the array will appear at the top, the last at the bottom

        **Visual behavior:**
        After reordering, task type rows will appear in the Big Picture view in the new sequence. This affects the visual hierarchy - task types at the top are more prominent and appear first when viewing the project.

        **Use cases:**
        - Prioritizing which task types are most important (e.g., Bugs before Features, urgent categories first)
        - Organizing the view to match team workflow and working habits
        - Customizing the Big Picture layout for different project contexts or customer preferences
        - Improving visual hierarchy for project stakeholders - show most relevant categories first
        - Grouping related task types together for better overview

        **Special groups:**
        - Tasks without a recognized type are grouped into a special "other" category
        - The "other" group can be included in the order array like any other group

        **Validation:**
        - Project must exist and not be deleted
        - Order array must contain valid group IDs (task type IDs) that exist in the project
        - All groups currently in the project must be included in the order array (no groups can be omitted)

        **Permissions:**
        Requires BigPicture.canEdit permission and access to the specified project.
      operationId: TasksBigPictureController_updateBigPictureGroupOrder
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateBigPictureGroupOrderDto'
      responses:
        '200':
          description: Groups successfully reordered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid project ID or order array
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks BigPicture.canEdit permission or project access
        '404':
          description: Project not found or deleted
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder task type groups (rows)
      tags:
        - tasks
        - big-picture
      x-required-permissions:
        - big_picture.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/pool:
    get:
      description: |-
        The pool view provides a cross-project overview of all tasks filtered by the Accountable field (not the Assigned field). Unlike the Big Picture view which focuses on a single project, the pool aggregates tasks across all projects where the specified user is listed in the Accountable field.

        **Important**: This endpoint filters tasks by the Accountable field, not the Assigned field. Only tasks where the accountableId user is set as the Accountable person will appear in the pool results.

        **How to call this endpoint:**
        - accountableId is required. Do not call GET /tasks/pool without it.
        - To show the current user's own pool, first call GET /account/info and use its userId as accountableId.
        - Example: GET /tasks/pool?accountableId=<current-user-id>.
        - An empty projects array is a valid successful result when the user has no accountable tasks.

        This view is commonly used for:
        - Weekly planning meetings between team leads and account managers
        - Prioritizing tasks across multiple client projects
        - Reviewing completed work and discussing open tickets
        - Managing workload and identifying blockers

        The response organizes tasks by project, with each project containing groups organized by task type (e.g., Feature, Bug, Task). Projects are sorted by custom order stored in PoolProjectOrder table, and groups within projects are sorted by custom order from PoolTypeProjectOrder table.

        The response includes:
        - Overall statistics: total tasks, resolved count, total estimated hours, total spent hours, resolved hours
        - Per-project structure: each project contains groups of tasks organized by type
        - Per-group statistics: resolved count, total estimate, total spent, resolved hours for each task type group

        Permission requirements:
        - Pool.canViewAll: Can view any user's pool
        - Pool.canViewOwnTeams: Can view pools of users in the same actual team membership. The role name alone is not enough; users must share a Team/UsersOnTeam relation.
        - Pool.canViewOwn: Can only view your own pool (accountableId must match authenticated user.id)

        Tasks are prioritized horizontally within each group - tasks on the left have higher priority.

        **Note:** If you need to retrieve all tasks with custom filtering, sorting, or pagination capabilities, consider using the GET /tasks/grid endpoint instead. The grid endpoint provides more flexible filtering options and is better suited for retrieving large datasets or when you need to filter by multiple criteria simultaneously.
      operationId: TasksPoolController_getPool
      parameters:
        - name: accountableId
          required: true
          in: query
          description: 'Required UUID of the user whose pool data should be retrieved. To retrieve the current user''s own pool, call GET /account/info first and use its userId here. **Important**: Tasks are filtered by the Accountable field (not the Assigned field). Only tasks where this user is set as the Accountable person will appear in the pool. The user must exist in the same workspace and not be deleted.'
          schema:
            example: aff55c87-49c6-4d5d-9b0b-bf7478e2d504
            type: string
      responses:
        '200':
          description: Successfully retrieved pool data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PoolResponseDto'
        '400':
          description: Invalid accountableId UUID format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks required Pool permissions or accountableId validation fails
        '404':
          description: User (accountableId) not found, deleted, or not in same workspace
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get pool data for an accountable user
      tags:
        - tasks
        - pool
      x-required-scopes:
        - api:read
  /tasks/pool/order:
    post:
      description: |-
        Updates the horizontal priority order of tasks within a pool for a specific accountable user. Tasks are ordered from left to right, with tasks on the left having higher priority.

        **Important**: The pool contains tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included in the pool.

        This endpoint updates the PoolOrder table, which stores the custom sort order for tasks within each accountable user's pool. The order is used when displaying tasks in the pool view, allowing users to prioritize which tasks should be handled first.

        The newOrder array should contain all task IDs that need to be reordered, in the desired priority sequence (first item = highest priority, leftmost position).

        Permission requirements:
        - Pool.canEditAll: Can edit any user's pool order
        - Pool.canEditOwnTeams: Can edit pools of users in the same team (requires team membership verification)
        - Pool.canEditOwn: Can only edit your own pool order (accountableId must match authenticated user.id)

        The accountableId must reference a valid user in the same workspace who is not deleted.
      operationId: TasksPoolController_updatePoolOrder
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePoolOrderDto'
      responses:
        '200':
          description: Successfully updated task order
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid accountableId UUID format or invalid newOrder array
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks required Pool edit permissions
        '404':
          description: User (accountableId) not found, deleted, or not in same workspace
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update task order within a pool
      tags:
        - tasks
        - pool
      x-required-scopes:
        - api:write
  /tasks/pool/project-order:
    post:
      description: |-
        Updates the vertical order of projects within a pool for a specific accountable user. Projects are displayed as rows in the pool view, and this endpoint allows you to customize which projects appear first.

        **Important**: The pool contains tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included in the pool.

        This endpoint updates the PoolProjectOrder table, which stores the custom sort order for projects within each accountable user's pool. The order determines how projects are displayed vertically in the pool view.

        The newOrder array should contain all project IDs that need to be reordered, in the desired sequence (first item = topmost position).

        Permission requirements:
        - Pool.canEditAll: Can edit any user's pool project order
        - Pool.canEditOwnTeams: Can edit pools of users in the same team (requires team membership verification)
        - Pool.canEditOwn: Can only edit your own pool project order (accountableId must match authenticated user.id)

        The accountableId must reference a valid user in the same workspace who is not deleted.
      operationId: TasksPoolController_updatePoolProjectOrder
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePoolProjectOrderDto'
      responses:
        '200':
          description: Successfully updated project order
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid accountableId UUID format or invalid newOrder array
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks required Pool edit permissions
        '404':
          description: User (accountableId) not found, deleted, or not in same workspace
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project order within a pool
      tags:
        - tasks
        - pool
      x-required-scopes:
        - api:write
  /tasks/pool/group-order:
    post:
      description: |-
        Updates the horizontal order of task type groups (e.g., Feature, Bug, Task) within a specific project in a pool for a specific accountable user. Groups are displayed as columns within each project row, and this endpoint allows you to customize which task types appear first.

        **Important**: The pool contains tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included in the pool.

        This endpoint updates the PoolTypeProjectOrder table, which stores the custom sort order for task type groups within each project for each accountable user. The order determines how task types are displayed horizontally within each project row in the pool view.

        The newOrder array should contain all task type IDs that need to be reordered, in the desired sequence (first item = leftmost position).

        Permission requirements:
        - Pool.canEditAll: Can edit any user's pool group order
        - Pool.canEditOwnTeams: Can edit pools of users in the same team (requires team membership verification)
        - Pool.canEditOwn: Can only edit your own pool group order (accountableId must match authenticated user.id)

        The accountableId must reference a valid user in the same workspace who is not deleted. The projectId must reference a valid project in the same workspace.
      operationId: TasksPoolController_updatePoolGroupOrder
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePoolGroupOrderDto'
      responses:
        '200':
          description: Successfully updated group order
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid accountableId or projectId UUID format or invalid newOrder array
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks required Pool edit permissions
        '404':
          description: User (accountableId) not found, deleted, or not in same workspace
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update group/type order within a project in a pool
      tags:
        - tasks
        - pool
      x-required-scopes:
        - api:write
  /tasks/stack:
    get:
      description: |-
        **What is the Stack?**
        The Stack view shows a person's personal weekly schedule and serves as their personal task overview. It acts like a personal Kanban board that helps specialists work through tasks in a focused, distraction-free way. The Stack automatically follows the order from pipeline planning, showing exactly which tasks are planned for the employee and in what priority order.

        **Task Filtering Logic:**
        The stack includes tasks from two sources:
        1. **Pipeline tasks**: Tasks that are in the user's pipeline for the current week (determined by the `today` parameter). These tasks appear regardless of whether they are assigned to the user or have the user as accountable. Pipeline tasks appear in the **toDo** and **done** columns.
        2. **Non-pipeline tasks**: Tasks filtered by the **Assigned field** (not the Accountable field). Only tasks where `assignedToId` equals the `userId` are included. These appear in **giveFeedback**, **getFeedback**, and **backlog** columns.

        **Important**: Unlike the Pool view which filters by Accountable field, the Stack filters non-pipeline tasks by the **Assigned field** (`assignedToId`). Pipeline tasks are included regardless of assignment or accountability.

        **How to call this endpoint:**
        - Both userId and today are required. Do not call GET /tasks/stack without them.
        - To show the current user's own stack, first call GET /account/info and use its userId.
        - today must be an ISO date string such as 2026-07-08; use the current date unless the user asks for another week.
        - Example: GET /tasks/stack?userId=<current-user-id>&today=2026-07-08.
        - Empty columns are a valid successful result when the user has no stack tasks.

        **What data is returned:**
        The response contains tasks organized into five columns that show how far tasks are in the workflow:
        - **toDo**: Tasks that need to be worked on this week. These are tasks from the pipeline (regardless of assignment) that are in "New" or "In Progress" status.
        - **giveFeedback**: Tasks where the expert should give feedback to others. These are tasks where the user is **assigned** (filtered by Assigned field, not Accountable) and are in "Feedback" status.
        - **getFeedback**: Tasks that are waiting for feedback from others. These are tasks where the user is **NOT assigned** (filtered by Assigned field) but are in "Feedback" status.
        - **done**: Finished tasks from the pipeline (regardless of assignment) that are in "Closed" or "Resolved" status.
        - **backlog**: Tasks that are **assigned to the user** (filtered by Assigned field) but are not part of this week's pipeline. These could be planned for coming weeks.

        **Task sorting and prioritization:**
        - Tasks are sorted by priority (High > Normal > Low) then by shortNumber (ascending)
        - Pipeline tasks are prioritized and appear first in their respective columns
        - The order matches the priority set during pipeline planning

        **Permission requirements:**
        Access is controlled by Stack permissions:
        - **Stack.canViewAll**: Can view any user's stack
        - **Stack.canViewOwnTeams**: Can view stacks of users in the same actual team membership. The role name alone is not enough; users must share a Team/UsersOnTeam relation.
        - **Stack.canViewOwn**: Can only view own stack

        **Task details included:**
        Each task includes comprehensive information: ID, title, icon, short number, organization color and name, project details, status, summary, priority, type, deadline, estimated/spent time, comments count, assigned users, and tags. The spentTime field is only visible if the user has Tasks.seeLoggedTime permission.

        **Note:** If you need to retrieve all tasks with custom filtering, sorting, or pagination capabilities, consider using the GET /tasks/grid endpoint instead. The grid endpoint provides more flexible filtering options and is better suited for retrieving large datasets or when you need to filter by multiple criteria simultaneously.
      operationId: TasksStackController_getStack
      parameters:
        - name: userId
          required: true
          in: query
          description: 'Required UUID of the user whose stack to retrieve. To retrieve the current user''s own stack, call GET /account/info first and use its userId here. **Important**: The stack includes (1) pipeline tasks regardless of assignment, and (2) non-pipeline tasks filtered by the Assigned field (where assignedToId equals this userId). The user must exist, not be deleted, and belong to the same workspace as the authenticated user.'
          schema:
            example: aff55c87-49c6-4d5d-9b0b-bf7478e2d504
            type: string
        - name: today
          required: true
          in: query
          description: Required date in ISO 8601 format (YYYY-MM-DD) used for pipeline calculations. Use the current date for the current week unless the user asks for another week. This date determines which tasks are included in the current week pipeline and affects task sorting and column assignment.
          schema:
            example: '2025-11-04'
            type: string
      responses:
        '200':
          description: 'Stack data successfully retrieved. Returns tasks organized into five columns (toDo, giveFeedback, getFeedback, done, backlog). Pipeline tasks are included regardless of assignment. Non-pipeline tasks are filtered by the Assigned field (assignedToId). Includes comprehensive task details: IDs, titles, priorities, assignments, time tracking, and more.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StackResponseDto'
        '400':
          description: Invalid request parameters. Occurs when userId is not a valid UUID format or today is not a valid ISO 8601 date format (YYYY-MM-DD).
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: 'Validation failed: userId must be a UUID'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Access denied. Occurs when the authenticated user does not have the required Stack permissions (Stack.canViewAll, Stack.canViewOwnTeams, or Stack.canViewOwn) to view the requested user's stack, or when permission validation fails (e.g., user with Stack.canViewOwnTeams trying to view stack of user from different team, or user with Stack.canViewOwn trying to view another user's stack).
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 403
                  message:
                    type: string
                    example: 'Forbidden: You do not have permission to view this user''s stack'
        '404':
          description: User not found. Occurs when the userId does not exist, has been deleted (deletedAt is not null), or does not belong to the same workspace as the authenticated user.
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 404
                  message:
                    type: string
                    example: User not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get stack data for a user
      tags:
        - tasks
      x-required-scopes:
        - api:read
  /tasks/pipeline:
    get:
      description: |-
        **What is the Pipeline?**
        The Pipeline is Leadtime's weekly planning tool that provides a visual overview of all tasks scheduled for team members during a selected period. It shows team members vertically and calendar weeks horizontally, with each colored card representing a planned task. The pipeline is used in weekly planning meetings to schedule tasks according to importance and capacity, optimizing the use of available working time.

        **What data is returned:**
        The response contains a complete pipeline view for the specified date range:
        - **users**: Object keyed by userId, each containing:
          - Employee information (employeeId, userId, name)
          - **days**: Array of day data including work day length, weekends, holidays, vacations, and sickness
          - **items**: Array of pipeline items (planned tasks) with task details, dates, and times
          - **reality**: Array of actual time logs (if enabled and user has permission)
        - **ready**: Boolean indicating if the week is marked as active/approved
        - **year** and **week**: ISO week number for the pipeline period

        **How it works:**
        - Pipeline items are filtered to include tasks that start, end, or span across the specified date range
        - The order of tasks in the pipeline automatically transfers to each team member's personal stack (Kanban board)
        - Dates are normalized to UTC and should be provided in ISO format (YYYY-MM-DD)
        - The pipeline supports two view modes: Overview (8-hour blocks) and Spacious (15-minute increments)

        **Permission-based Filtering:**
        - `usersFilter: "all"` - Requires Pipeline.canViewAll permission to view all users
        - `usersFilter: "me"` - Requires Pipeline.canViewOwnTeams or Pipeline.canViewAll permission to view own data
        - `usersFilter: "team:{teamId}"` - Requires Pipeline.canViewOwnTeams permission and user must be a member of the specified team

        **Reality Data (Time Logs):**
        - Set `withReality: true` to include actual booked working hours alongside planned tasks
        - Only includes time logs if user has Tasks.seeLoggedTime permission
        - If permission is missing, the reality array will be empty
        - This helps compare planned vs. actual work and improve future capacity estimates

        **Important - Understanding Time Format:**
        In the response, `items[].dateStart` and `items[].dateEnd` contain both date and time components:
        - The **date part** (YYYY-MM-DD) represents the calendar day
        - The **time part** (HH:mm) represents hours since the beginning of the work day, NOT since midnight
        - Times are stored in 15-minute increments (0, 15, 30, 45, 60, etc.) from work day start
        - **Example**: If work day starts at 09:00:
          - `"2025-10-20T00:30:00Z"` means 09:30 on Oct 20
          - `"2025-10-20T05:30:00Z"` means 14:30 on Oct 20
        - When reading these values, extract the time component relative to work day start, not midnight

        **Use cases:**
        - Display weekly planning view in custom dashboards or integrations
        - Export pipeline data for reporting and analysis
        - Compare planned vs. actual work (with reality data)
        - Build custom planning tools that integrate with Leadtime's pipeline system
      operationId: PipelineController_getPipeline
      parameters:
        - name: dateStart
          required: true
          in: query
          description: Start date of the pipeline period in ISO format (YYYY-MM-DD). Pipeline items that start, end, or span across this date range will be included.
          schema:
            example: '2025-10-20'
            type: string
        - name: dateEnd
          required: true
          in: query
          description: End date of the pipeline period in ISO format (YYYY-MM-DD). Pipeline items that start, end, or span across this date range will be included.
          schema:
            example: '2025-11-02'
            type: string
        - name: usersFilter
          required: true
          in: query
          description: 'Filter which users to include in the pipeline view. Options: "all" (requires Pipeline.canViewAll permission to view all users), "me" (requires Pipeline.canViewOwnTeams or Pipeline.canViewAll permission to view own data), or "team:{teamId}" (requires Pipeline.canViewOwnTeams permission and user must be a member of the specified team).'
          schema:
            example: me
            type: string
        - name: withReality
          required: false
          in: query
          description: Include reality data (actual time logs) alongside planned tasks. When enabled, shows what team members actually worked on during the period. Only includes time logs if user has Tasks.seeLoggedTime permission. If permission is missing, the reality array will be empty. Useful for comparing planned vs. actual work.
          schema:
            default: false
            example: false
            type: boolean
      responses:
        '200':
          description: Pipeline data successfully retrieved. Returns an object with users keyed by userId, ready status, year, and week.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PipelineResponseDto'
        '400':
          description: Invalid date format or filter value
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing required permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get pipeline data for date range
      tags:
        - tasks
        - pipeline
      x-required-permissions:
        - pipeline.canViewOwnTeams
        - pipeline.canView
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
  /tasks/pipeline/status:
    post:
      description: |-
        **What this does:**
        Toggles the active status of a pipeline week. When a week is marked as active, the planned tasks are automatically transferred to each team member's personal stack (Kanban board) in the correct order. This is typically done after the weekly pipeline planning meeting when all tasks have been agreed upon.

        **How it works:**
        - Creates a PipelineMeta record if it doesn't exist for the week (sets ready=true)
        - If PipelineMeta already exists, toggles the ready boolean value
        - The week is determined from the start date using ISO week numbering
        - Dates should be in ISO format (YYYY-MM-DD)

        **Status meanings:**
        - 🟢 **Active (ready=true)**: Current approved planning, tasks are visible in employee stacks
        - 🟠 **Inactive (ready=false)**: Draft for the upcoming week, still being planned

        **Required Permission**: Pipeline.canChangeSettings

        **Use cases:**
        - Mark a week as active after completing weekly planning meetings
        - Deactivate a week to make changes to the planning
        - Automate the activation process as part of a planning workflow
      operationId: PipelineController_activatePipeline
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActivatePipelineDto'
      responses:
        '200':
          description: Pipeline status successfully toggled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid date format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing required permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Activate/deactivate pipeline for a week
      tags:
        - tasks
        - pipeline
      x-required-permissions:
        - pipeline.canChangeSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/pipeline/items:
    post:
      description: |-
        **What this does:**
        Creates a new pipeline item, scheduling a task on the timeline for a specific team member. This is equivalent to adding a task to the pipeline during weekly planning meetings. The task will appear as a colored card in the pipeline view and, once the week is activated, will automatically appear in the assigned team member's personal stack in the correct order.

        **How it works:**
        - The pipeline item links a task to a user with specific start/end dates and times
        - The length of the pipeline card matches the planned working time
        - Tasks are placed in 15-minute increments for precise scheduling
        - The order of tasks in the pipeline determines their order in the team member's stack

        **Required Permission**: User must have either Pipeline.canPlanAll OR Pipeline.canPlanOwnTeams permission

        **Permission-based Assignment**:
        - Users with **Pipeline.canPlanAll** can assign tasks to any user in the workspace
        - Users with **Pipeline.canPlanOwnTeams** can only assign tasks to themselves or users in their teams

        **Validation Rules**:
        - **Date range**: Maximum 2 weeks between dateStart and dateEnd
        - **Time validation**: Start time must not exceed the work day length for the assigned user
        - **Task access**: Users can only plan tasks they have access to view
        - **Time increments**: Times must be in 15-minute increments (0, 15, 30, 45, 60, 75, 90, etc.)

        **Date/Time Format**: 
        - **dateStart/dateEnd**: ISO format (YYYY-MM-DDTHH:mm:ssZ). **CRITICAL**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Always use "T00:00:00Z" or similar. Example: "2025-11-04T00:00:00Z"
        - **timeStart/timeEnd**: MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time)
          - **Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)
          - **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.
          - **Calculation**: Actual time = workDayStartTime + (timeStart/timeEnd minutes)
        - **Examples** (assuming 8-hour work day starting at 09:00):
          - `timeStart=0` = 09:00 (start of work day)
          - `timeStart=60` = 10:00 (1 hour after work day start)
          - `timeStart=240` = 13:00 (4 hours = midday)
          - `timeStart=480` = 17:00 (8 hours = end of work day)
        - **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):
          To schedule multiple tasks sequentially throughout the day, chain them together:
          - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)
          - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)
          - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)
          - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)
          - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)
        - **Important**: timeStart/timeEnd are NOT hours since midnight. If work day starts at 09:00, timeStart=0 means 09:00, not 00:00.

        **Use cases:**
        - Programmatically add tasks to the pipeline during planning
        - Integrate with external planning tools
        - Automate task scheduling based on capacity calculations
        - Bulk import planned tasks from other systems
      operationId: PipelineController_createPipelineItem
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,taskId,userId
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePipelineItemDto'
      responses:
        '201':
          description: Pipeline item successfully created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PipelineItemResponseDto'
        '400':
          description: Invalid input data or validation failed
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing required permissions or not allowed to assign to target user
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Plan a task (create pipeline item)
      tags:
        - tasks
        - pipeline
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - taskId
          - userId
        allowedFields:
          - id
          - taskId
          - userId
          - dateStart
          - dateEnd
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - pipeline.canPlanAll
        - pipeline.canPlanOwnTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
  /tasks/pipeline/items/{id}:
    patch:
      description: |-
        **What this does:**
        Updates an existing pipeline item. This allows you to modify the scheduling of a task that has already been planned - for example, moving it to a different time slot, assigning it to a different team member, or changing the task itself. All fields are optional; only provided fields are updated.

        **How it works:**
        - Partial updates: Only send the fields you want to change
        - If a field is not provided, the existing value is preserved
        - The same validation rules apply as when creating a pipeline item
        - Changes are reflected immediately in the pipeline view

        **Required Permission**: User must have either Pipeline.canPlanAll OR Pipeline.canPlanOwnTeams permission

        **Permission-based Assignment**:
        - Users with **Pipeline.canPlanAll** can assign to any user
        - Users with **Pipeline.canPlanOwnTeams** can only assign to themselves or users in their teams

        **Validation Rules**:
        - **Date range**: Maximum 2 weeks between dateStart and dateEnd
        - **Time validation**: Start time must not exceed the work day length for the user
        - **Task access**: Users can only plan tasks they have access to view
        - **Time increments**: Times must be in 15-minute increments (0, 15, 30, 45, 60, 75, 90, etc.)

        **Date/Time Format**: 
        - **dateStart/dateEnd**: ISO format (YYYY-MM-DDTHH:mm:ssZ). **CRITICAL**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Always use "T00:00:00Z" or similar. Example: "2025-11-04T00:00:00Z"
        - **timeStart/timeEnd**: MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time)
          - **Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)
          - **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.
          - **Calculation**: Actual time = workDayStartTime + (timeStart/timeEnd minutes)
        - **Examples** (assuming 8-hour work day starting at 09:00):
          - `timeStart=0` = 09:00 (start of work day)
          - `timeStart=60` = 10:00 (1 hour after work day start)
          - `timeStart=240` = 13:00 (4 hours = midday)
          - `timeStart=480` = 17:00 (8 hours = end of work day)
        - **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):
          To schedule multiple tasks sequentially throughout the day, chain them together:
          - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)
          - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)
          - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)
          - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)
          - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)
        - **Important**: timeStart/timeEnd are NOT hours since midnight. If work day starts at 09:00, timeStart=0 means 09:00, not 00:00.
        - **Response**: The response dateStart/dateEnd values show calculated absolute times in UTC. To interpret them, extract the time component relative to work day start, not midnight.

        **Use cases:**
        - Adjust task timing during planning meetings
        - Reassign tasks to different team members
        - Extend or shorten task duration
        - Move tasks to different days or weeks
      operationId: PipelineController_updatePipelineItem
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,taskId,userId
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePipelineItemDto'
      responses:
        '200':
          description: Pipeline item successfully updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PipelineItemResponseDto'
        '400':
          description: Invalid input data or validation failed
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing required permissions or not allowed to assign to target user
        '404':
          description: Pipeline item not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Edit a planned task
      tags:
        - tasks
        - pipeline
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - taskId
          - userId
        allowedFields:
          - id
          - taskId
          - userId
          - dateStart
          - dateEnd
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - pipeline.canPlanAll
        - pipeline.canPlanOwnTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What this does:**
        Soft deletes a pipeline item, removing it from the pipeline view. The task itself is not deleted, only the planning/scheduling entry. This is useful when a task needs to be removed from the weekly plan but may be rescheduled later.

        **How it works:**
        - Performs a soft delete (marks as deleted, doesn't permanently remove)
        - The task remains in the system and can be re-planned later
        - The item disappears from the pipeline view immediately
        - If the week is already active, the task will also be removed from the team member's stack

        **Required Permission**: User must have either Pipeline.canPlanAll OR Pipeline.canPlanOwnTeams permission

        **Permission-based Deletion**:
        - Users with **Pipeline.canPlanAll** can delete any pipeline item in the workspace
        - Users with **Pipeline.canPlanOwnTeams** can only delete items assigned to themselves or users in their teams

        **Use cases:**
        - Remove tasks that are no longer needed in the weekly plan
        - Clear planning when tasks are cancelled or postponed
        - Adjust capacity by removing lower-priority items
      operationId: PipelineController_deletePipelineItem
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Pipeline item successfully deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing required permissions or not allowed to delete this item
        '404':
          description: Pipeline item not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Remove a planned task
      tags:
        - tasks
        - pipeline
      x-required-permissions:
        - pipeline.canPlanAll
        - pipeline.canPlanOwnTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
  /tasks/types:
    get:
      description: |-
        Returns a list of all task types configured in the workspace. Task types structure workflows, bundle fields and statuses, and define how tasks are created and handled. Each task type specifies what kind of ticket is created, what information must be collected, and how the processing workflow works. This endpoint is optimized for common list data retrieval and includes translations, associated statuses, custom fields, and required fields for each type.

        Use this endpoint as metadata before POST /tasks. The global create-task schema cannot know which fields are required for every workspace/task-type configuration. For the selected typeId, inspect requiredFields and include those fields in the POST /tasks body. Standard fields such as summary and estimatedTime can be listed here, as well as task custom fields.
      operationId: TasksController_listTaskTypes
      parameters:
        - name: includeDeleted
          required: false
          in: query
          description: 'Include deleted task types in the response (default: true)'
          schema:
            default: true
            type: boolean
      responses:
        '200':
          description: Task types retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskTypeDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all task types
      tags:
        - tasks
        - common-lists
      x-required-scopes:
        - api:read
  /tasks/statuses:
    get:
      description: Returns a list of all task statuses configured in the workspace. Task statuses show the progress of a task in the ticket system and represent different phases in the workflow (e.g., New, In Progress, Feedback, Resolved, Closed, Backlog). Each status has a type, icon, sort order, and multilingual translations. This endpoint is optimized for common list data retrieval.
      operationId: TasksController_listTaskStatuses
      parameters:
        - name: includeDeleted
          required: false
          in: query
          description: 'Include deleted task statuses in the response (default: true)'
          schema:
            default: true
            type: boolean
      responses:
        '200':
          description: Task statuses retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskStatusDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all task statuses
      tags:
        - tasks
        - common-lists
      x-required-scopes:
        - api:read
  /tasks/custom-fields:
    get:
      description: Returns a list of all custom fields configured for tasks in the workspace. Custom fields capture project-specific data and add structured, multilingual input fields to tasks beyond the standard fields. Available field types include Text, Text Area, Number, Date, Checkbox, and Select (dropdown). Each field includes translations, sort order, and select options for dropdown fields. This endpoint is optimized for common list data retrieval.
      operationId: TasksController_listTaskCustomFields
      parameters: []
      responses:
        '200':
          description: Task custom fields retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskCustomFieldDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all task custom fields
      tags:
        - tasks
        - common-lists
      x-required-scopes:
        - api:read
  /tasks:
    post:
      description: |-
        Creates a new task (ticket) in the specified project. Tasks are the core work units in Leadtime and represent actionable items that need to be completed. Each task belongs to a project, has a type (e.g., Feature, Bug), a status (e.g., New, In Progress), and can include assignments, deadlines, time estimates, custom fields, tags, and subtasks. The description field accepts HTML or Markdown format and will be automatically converted to the internal editor format. Project access is validated before task creation. Requires Tasks.create permission.

        Some fields are dynamically required by workspace and task-type configuration. The global OpenAPI schema shows the universal request shape, but it cannot mark every conditionally required field as required for every workspace. Before creating tasks, read task type metadata from GET /tasks/types or GET /administration/task-settings/types, find the selected typeId, and include every field listed in that type requiredFields. Standard fields such as summary and estimatedTime can be listed there, as well as task custom fields. The API still validates the final request and returns field-level 400 errors when configured requirements are missing.

        Assignment fields are project-scoped. assignedToId and accountableId must reference a Leadtime User.id that is assignable in the selected project: either a project user/member with access to that project, or the workspace Leadtime agent user. Read GET /projects/{id} and use its users array before assigning; if a workspace user is missing from the project, add them with PATCH /projects/{id} first.
      operationId: TasksController_createTask
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,shortNumber,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaskDto'
      responses:
        '200':
          description: Task created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskDetailsResponseDto'
        '400':
          description: Validation errors or invalid data
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      title:
                        - Title is required
                      projectId:
                        - Invalid project
                      typeId:
                        - Invalid type
                      statusId:
                        - Invalid status
                      priority:
                        - Priority is required
                      description:
                        - Description is required
                      summary:
                        - This field is required by the selected task type
                      estimatedTime:
                        - This field is required by the selected task type
                      assignedToId:
                        - 'Invalid user: user must be assignable in the selected project'
                      accountableId:
                        - 'Invalid user: user must be assignable in the selected project'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new task
      tags:
        - tasks
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - shortNumber
          - title
        allowedFields:
          - id
          - shortNumber
          - title
          - projectId
          - statusId
          - typeId
          - assignedToId
          - accountableId
          - priority
          - summary
          - createdAt
          - deadline
          - estimatedTime
          - guestAccess
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/grid:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of tasks across all projects in the workspace. This endpoint supports server-side filtering, sorting, and pagination for efficient data retrieval. You can filter by title, status, type, project, parent task, guest access, and more. Quick search is available on the title field. The default sort is by last updated timestamp in descending order.

        **What are "Open" Tasks?**
        A task is considered "open" when its status type is NOT `Closed`. This includes tasks with status types: `New`, `InProgress`, `Feedback`, `Backlog`, and `Resolved`. Only tasks with status type `Closed` are considered closed. You can filter for open tasks by using the `statusId` field with comparison `is_open` in your filters parameter.

        Retrieves a paginated grid of tasks with filtering and sorting capabilities. Quick search available on: title, summary. Filterable fields: title, shortNumber, statusId, typeId, lastUpdated, parentTaskId, guestAccess, projectId, assignedToId, userId, summary, priority, tags, isFavorite, inPipeline, hasObject, hasForms, billingStatus, billedAmount. Sortable fields: title, shortNumber, statusId, typeId, lastUpdated, parentTaskId, projectId, assignedToId, userId, summary, priority, billingStatus, billedAmount.
      operationId: TasksController_getGrid
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: title, summary'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **title** (string): Task title
            - **shortNumber** (number): Task short number
            - **statusId** (task_status): Task status ID. Special comparisons available: `is_open` (filters for tasks with status type NOT Closed - includes New, InProgress, Feedback, Backlog, Resolved), `is_closed` (filters for tasks with status type Closed).
            - **typeId** (task_type): Task type ID. Special comparisons available: `is_feature` (filters for tasks with type Feature), `is_bug` (filters for tasks with type Bug).
            - **lastUpdated** (date): Last updated timestamp
            - **parentTaskId** (set): Parent task ID (if subtask)
            - **guestAccess** (boolean): Whether task has guest access
            - **projectId** (set): Project ID
            - **assignedToId** (set): Assigned user ID. This must be a Leadtime User.id UUID, not an Employee.id UUID.
            - **userId** (set): Creator/accountable user ID. This must be a Leadtime User.id UUID, not an Employee.id UUID.
            - **summary** (string): Task summary
            - **priority** (set): Task priority (Low, Normal, High)
            - **tags** (array): Task tags
            - **isFavorite** (boolean): Whether task is marked as favorite by the current user
            - **inPipeline** (boolean): Whether the task appears on any team member pipeline in this workspace (non-deleted PipelineItem).
            - **hasObject** (boolean): Whether the task is linked to at least one non-deleted object instance in this workspace.
            - **hasForms** (boolean): Whether the task has at least one non-deleted form instance attached in this workspace.
            - **billingStatus** (set): Billing status (not_ready, waiting, charged, paid). Only available for external projects and users with Invoices.manage permission.
            - **billedAmount** (number): Total amount billed for this task. Only available for external projects and users with Invoices.manage permission.

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **title** (string): Task title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **shortNumber** (number): Task short number (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **statusId** (task_status): Task status ID. Special comparisons available: `is_open` (filters for tasks with status type NOT Closed - includes New, InProgress, Feedback, Backlog, Resolved), `is_closed` (filters for tasks with status type Closed). (task_status filter). Available comparisons: is_open, is_closed, is_empty, is_not_empty, in, not_in
            - **typeId** (task_type): Task type ID. Special comparisons available: `is_feature` (filters for tasks with type Feature), `is_bug` (filters for tasks with type Bug). (task_type filter). Available comparisons: is_feature, is_bug, is_empty, is_not_empty, in, not_in
            - **lastUpdated** (date): Last updated timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **parentTaskId** (set): Parent task ID (if subtask) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **guestAccess** (boolean): Whether task has guest access (boolean filter). Available comparisons: equal, not_equal
            - **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **assignedToId** (set): Assigned user ID. This must be a Leadtime User.id UUID, not an Employee.id UUID. (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **userId** (set): Creator/accountable user ID. This must be a Leadtime User.id UUID, not an Employee.id UUID. (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **summary** (string): Task summary (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **priority** (set): Task priority (Low, Normal, High) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **tags** (array): Task tags (array filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **isFavorite** (boolean): Whether task is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal
            - **inPipeline** (boolean): Whether the task appears on any team member pipeline in this workspace (non-deleted PipelineItem). (boolean filter). Available comparisons: equal, not_equal
            - **hasObject** (boolean): Whether the task is linked to at least one non-deleted object instance in this workspace. (boolean filter). Available comparisons: equal, not_equal
            - **hasForms** (boolean): Whether the task has at least one non-deleted form instance attached in this workspace. (boolean filter). Available comparisons: equal, not_equal
            - **billingStatus** (set): Billing status (not_ready, waiting, charged, paid). Only available for external projects and users with Invoices.manage permission. (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **billedAmount** (number): Total amount billed for this task. Only available for external projects and users with Invoices.manage permission. (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **title**: Task title
            - **shortNumber**: Task short number
            - **statusId**: Task status ID. Special comparisons available: `is_open` (filters for tasks with status type NOT Closed - includes New, InProgress, Feedback, Backlog, Resolved), `is_closed` (filters for tasks with status type Closed).
            - **typeId**: Task type ID. Special comparisons available: `is_feature` (filters for tasks with type Feature), `is_bug` (filters for tasks with type Bug).
            - **lastUpdated**: Last updated timestamp
            - **parentTaskId**: Parent task ID (if subtask)
            - **projectId**: Project ID
            - **assignedToId**: Assigned user ID. This must be a Leadtime User.id UUID, not an Employee.id UUID.
            - **userId**: Creator/accountable user ID. This must be a Leadtime User.id UUID, not an Employee.id UUID.
            - **summary**: Task summary
            - **priority**: Task priority (Low, Normal, High)
            - **billingStatus**: Billing status (not_ready, waiting, charged, paid). Only available for external projects and users with Invoices.manage permission.
            - **billedAmount**: Total amount billed for this task. Only available for external projects and users with Invoices.manage permission.

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** lastUpdated (desc)
          schema:
            example: '[{"field":"lastUpdated","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, title, icon, shortNumber, statusId, typeId, lastUpdated, parentTaskId, parentTitle, guestAccess, projectId, assignedToId, userId, summary, priority, tags, subTasksIds, isFavorite, hasActiveAgentSession, inPipeline, hasObject, hasForms, orgColor, billingStatus, billedAmount, spentTime, estimatedTime, deadline
          schema:
            example: id,title
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved tasks grid
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get tasks grid
      tags:
        - tasks
        - common-lists
      x-required-scopes:
        - api:read
  /tasks/{identifier}:
    get:
      description: |-
        Returns complete task details including all metadata, description, comments, history, time logs, participants, subtasks, and custom fields. The description and comment bodies are returned in HTML format (converted from the internal editor format). Accepts both UUID (id) and numeric shortNumber identifiers. Access is controlled by project permissions and guest access settings. Organization members can only access tasks with guestAccess enabled.

        **Embedded images and files in HTML:** Rich text may contain `<div data-type="appImage">`, `<div data-type="appFile">`, or `<div data-type="appVideo">` nodes with a `fileId` attribute (UUID) and `filename`. Images are **not** inlined as base64 in this API response.

        **Downloading embedded binaries for display or AI vision:** Use authenticated HTTP GET on the Public API endpoint `/workspace/files/{fileId}`, sending the same Bearer token. Integrations and agents should parse `fileId` from the HTML, then fetch it only when they need the binary content. Plain `/api/files/public/...` is reserved for explicitly public or signed-capability assets.
      operationId: TasksController_getTaskDetails
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
      responses:
        '200':
          description: Task details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskDetailsResponseDto'
        '400':
          description: Invalid identifier format (not UUID and not numeric)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get task details by UUID or shortNumber
      tags:
        - tasks
        - common-lists
      x-required-scopes:
        - api:read
    patch:
      description: 'Updates an existing task with partial data. Only provided fields will be updated, allowing you to modify specific properties without affecting others. The identifier can be either a UUID or a numeric short number (with or without # prefix). When updating the description, HTML or Markdown format is accepted and will be converted to the internal editor format. Changing the task type automatically updates the status if needed. Changing the project cascades to subtasks and time logs. Description mentions trigger notifications to mentioned users. Field-specific authorization matches the internal product API: most fields require project access only, description updates require authorship or Tasks.editAnyTitleAndDescription, project moves require Tasks.changeProject, and support contingent toggles require Tasks.toggleSupportContingent. Organization members can only update tasks with guestAccess enabled.'
      operationId: TasksController_patchTask
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or short number (e.g., "550e8400-e29b-41d4-a716-446655440000" or "123" or "#123")
          schema:
            example: '123'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,shortNumber,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchTaskDto'
      responses:
        '200':
          description: Task updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskDetailsResponseDto'
        '400':
          description: Invalid request data or validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: 'Access denied: No project access or permission denied'
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update a task
      tags:
        - tasks
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - shortNumber
          - title
        allowedFields:
          - id
          - shortNumber
          - title
          - projectId
          - statusId
          - typeId
          - assignedToId
          - accountableId
          - priority
          - summary
          - createdAt
          - deadline
          - estimatedTime
          - guestAccess
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: Soft deletes a task by its UUID or numeric shortNumber identifier. The task is marked as deleted but remains in the database for historical purposes. Access is controlled by Tasks.delete permission and project access. Billed tasks cannot be deleted to maintain billing integrity. Parent/subtask relationships are handled automatically - removing a parent task does not delete its subtasks, but removes the parent-child relationship. Organization members can only delete tasks with guestAccess enabled.
      operationId: TasksController_deleteTask
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
      responses:
        '200':
          description: Task deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid identifier format (not UUID and not numeric) or task is billed
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: 'Access denied: No Tasks.delete permission, no project access, or OrganizationMember accessing non-guest task'
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete task by UUID or shortNumber
      tags:
        - tasks
        - common-lists
      x-required-scopes:
        - api:write
  /tasks/{identifier}/duplicate:
    post:
      description: Creates a copy of an existing task in the same project. The duplicated task keeps the source task configuration such as type, status, description, priority, assignee, accountable person, tags, custom fields, guest access, and task products. The new task title gets a ` (Copy)` suffix. Accepts either a UUID or a numeric short number (with or without `#` prefix). Organization members can only duplicate tasks with guest access enabled.
      operationId: TasksController_duplicateTask
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or short number (e.g., "550e8400-e29b-41d4-a716-446655440000" or "123" or "#123")
          schema:
            example: '123'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,shortNumber,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '201':
          description: Task duplicated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskDetailsResponseDto'
        '400':
          description: Invalid identifier format (not UUID and not numeric)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: 'Access denied: Missing Tasks.create permission, no project access, or OrganizationMember accessing a non-guest task'
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Duplicate a task
      tags:
        - tasks
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - shortNumber
          - title
        allowedFields:
          - id
          - shortNumber
          - title
          - projectId
          - statusId
          - typeId
          - assignedToId
          - accountableId
          - priority
          - summary
          - createdAt
          - deadline
          - estimatedTime
          - guestAccess
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/comments:
    post:
      description: Creates a new comment on a task. Comments are used for ongoing communication between participants and can be marked as internal (visible only to team members) or public (visible to guest users). The comment body accepts HTML or Markdown format and will be converted to the internal editor format. You can optionally update the task assignee, change the status, or log time in the same request. Mentions in comments (using @username) trigger notifications to mentioned users. Access is controlled by Tasks.addComments permission, project permissions, and guest access settings. Organization members can only comment on tasks with guestAccess enabled.
      operationId: TasksController_addComment
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddTaskCommentDto'
      responses:
        '201':
          description: Comment created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCommentResponseDto'
        '400':
          description: Invalid identifier format or validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Add comment to task
      tags:
        - tasks
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - createdAt
        allowedFields:
          - id
          - userId
          - createdAt
          - lastUpdated
          - body
          - guestAccess
          - emailDetails
          - emailSendError
          - emailSendErrorCode
          - emailSendFailedAt
          - emailSendFailedTo
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.addComments
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/comments/{commentId}:
    patch:
      description: Updates the body of an existing task comment. Only the comment author can edit their own comment - this ensures comment integrity and prevents unauthorized modifications. The task identifier can be either a UUID or a numeric shortNumber. The comment body can be provided in HTML or Markdown format and will be converted to the internal editor format. The updated comment retains its original creation timestamp, author, and guest access settings.
      operationId: TasksController_updateTaskComment
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: commentId
          required: true
          in: path
          description: Comment UUID
          schema:
            example: aa0e8400-e29b-41d4-a716-446655440005
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,createdAt,lastUpdated
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTaskCommentDto'
      responses:
        '200':
          description: Comment updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCommentResponseDto'
        '400':
          description: Invalid identifier format, invalid comment data, or task not editable
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Not comment author, no project access, or OrganizationMember accessing non-guest task
        '404':
          description: Task or comment not found, or comment already deleted
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update task comment
      tags:
        - tasks
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - createdAt
          - lastUpdated
        allowedFields:
          - id
          - userId
          - createdAt
          - lastUpdated
          - body
          - guestAccess
          - emailDetails
          - emailSendError
          - emailSendErrorCode
          - emailSendFailedAt
          - emailSendFailedTo
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: Soft deletes a task comment. The comment is marked as deleted but remains in the database for historical purposes. Comment authors can always delete their own comments. Users with Tasks.deleteAnyComment permission can delete any comment, allowing moderators to remove inappropriate content. The task identifier can be either a UUID or a numeric shortNumber. Access is controlled by project permissions and guest access settings.
      operationId: TasksController_deleteTaskComment
      parameters:
        - name: identifier
          required: true
          in: path
          description: Task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: commentId
          required: true
          in: path
          description: Comment UUID
          schema:
            example: aa0e8400-e29b-41d4-a716-446655440005
            type: string
      responses:
        '200':
          description: Comment deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid identifier format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Not comment author and no Tasks.deleteAnyComment permission, no project access, or OrganizationMember accessing non-guest task
        '404':
          description: Task or comment not found, or comment already deleted
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete task comment
      tags:
        - tasks
        - common-lists
      x-required-scopes:
        - api:write
  /tasks/{identifier}/subtasks:
    post:
      description: Adds an existing task as a subtask to the specified parent task. Subtasks allow you to break down complex tasks into smaller, more manageable units with clear assignments and better tracking. Each subtask works like its own ticket with its own description, assignee, priority, and status. The parent task identifier can be either a UUID or a numeric shortNumber. The subtask must exist and be accessible to the user. Requires Tasks.editAnyTitleAndDescription permission.
      operationId: TasksController_addSubtask
      parameters:
        - name: identifier
          required: true
          in: path
          description: Parent task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,shortNumber,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddSubtaskDto'
      responses:
        '200':
          description: Subtask added successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskDetailsResponseDto'
        '400':
          description: Invalid identifier format or validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: 'Access denied: No access to project or task not editable'
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Add a subtask to a task
      tags:
        - tasks
        - common-lists
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - shortNumber
          - title
        allowedFields:
          - id
          - shortNumber
          - title
          - projectId
          - statusId
          - typeId
          - assignedToId
          - accountableId
          - priority
          - summary
          - createdAt
          - deadline
          - estimatedTime
          - guestAccess
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - tasks.editAnyTitleAndDescription
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /tasks/{identifier}/subtasks/{subtaskId}:
    delete:
      description: Removes the parent-child relationship between a task and its subtask by setting the subtask parentTaskId to null. The subtask itself is not deleted - it becomes a standalone task again. This is useful when reorganizing task hierarchies or when a subtask needs to be promoted to a main task. The parent task identifier can be either a UUID or a numeric shortNumber. The subtaskId must be a valid UUID. Requires Tasks.editAnyTitleAndDescription permission.
      operationId: TasksController_removeSubtask
      parameters:
        - name: identifier
          required: true
          in: path
          description: Parent task UUID or numeric shortNumber
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000 or 123
            type: string
        - name: subtaskId
          required: true
          in: path
          description: Subtask UUID to remove
          schema:
            example: 550e8400-e29b-41d4-a716-446655440001
            type: string
      responses:
        '200':
          description: Subtask removed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskDetailsResponseDto'
        '400':
          description: Invalid identifier format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: 'Access denied: No access to project or task not editable'
        '404':
          description: Task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Remove a subtask from a task
      tags:
        - tasks
        - common-lists
      x-required-permissions:
        - tasks.editAnyTitleAndDescription
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{id}/billing-settings:
    get:
      description: |-
        **What are Project Billing Settings?**
        Project billing settings define how invoices are created and structured for a specific project. These settings control invoice formatting, billing rules, default recipients, and support contingent (retainer) configurations.

        **What data is returned:**
        - **Invoice structure settings**: Whether to combine support and subscription invoices or split them separately
        - **Billing rules**: Whether bug tasks are billable
        - **Default invoice recipient**: The contact person from the customer organization who receives invoices
        - **Support contingent (retainer) configuration**: Monthly hour packages at fixed prices for recurring support/maintenance services
        - **Billing version snapshots**: Project versions used as the basis for invoicing (Single projects only)

        **Support Contingents (Retainers):**
        Support contingents allow you to allocate monthly hour packages at a fixed price. They are perfect for recurring support, maintenance, or service tasks and get suggested automatically in invoice review. Unused hours can optionally be carried over to subsequent months.

        **Billing Versions:**
        For Single projects, you can select which project version snapshot will be used as the basis for billing. This includes components, manual items, and products as agreed in that version. You can also optionally select a separate subscription billing version for products with recurring payments.

        **Permission requirements:**
        Requires Projects.seeAnyProject permission to view billing settings.
      operationId: ProjectBillingSettingsController_getBillingSettings
      parameters:
        - name: id
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Project billing settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetProjectBillingSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found or user does not have access
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project billing settings
      tags:
        - projects
        - project-settings
        - billing
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        **How to update billing settings:**
        This endpoint allows partial updates to project billing settings. Only the fields you provide will be updated; all other fields remain unchanged. The endpoint automatically merges your partial update with the current settings.

        **Invoice structure options:**
        - **allowMixedBilling (true)**: Combined invoices - bill support efforts (by booked hours) and subscriptions on a single invoice
        - **allowMixedBilling (false)**: Split invoices - support invoices are generated separately from subscription invoices

        **Billing rules:**
        - **bugsAreBillable**: Set whether time spent on tasks with "Bug" activity can be billed to the customer

        **Default invoice recipient:**
        - **invoiceContactId**: Select a contact person from the customer organization (OrganizationMember ID). This person will be auto-filled when creating invoices, but can be changed during invoice creation. Set to null to remove the default recipient.

        **Billing version snapshots (Single projects only):**
        - **billingProjectSnapshotId**: Select which project version will be used as the basis for billing. Includes components, manual items, and products as agreed in that version.
        - **productsBillingProjectSnapshotId**: Optionally select a separate version for billing subscriptions independently of the project scope. If empty, the main billing version is used automatically.

        **Permission requirements:**
        Requires both Projects.edit AND Invoices.manage permissions to update billing settings.
      operationId: ProjectBillingSettingsController_updateBillingSettings
      parameters:
        - name: id
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectBillingSettingsDto'
      responses:
        '200':
          description: Project billing settings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid input - validation errors or missing required conditional fields
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Projects.edit or Invoices.manage permission
        '404':
          description: Project not found or user does not have access
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project billing settings
      tags:
        - projects
        - project-settings
        - billing
      x-required-permissions:
        - projects.edit
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/component-details/{id}:
    get:
      description: |-
        Returns full details of a component from a project component tree including all nested children and metadata. 

        **What are Project Components?**
        Project components are reusable project templates that standardize recurring project types. They connect requirements management, cost calculation, and execution in one unified workflow. Each component contains items (Epics, Work Packages, Todo Lists, Test Suites) that define the project structure.

        **Project Components vs Library Components:**
        - **Project Components** (this endpoint): Belong to a specific project, contain live data (answers to questions, test case evaluations, todo completion status), and can be connected to tasks in the task management system.
        - **Library Components**: Workspace-wide reusable templates (projectId is null) that serve as blueprints. They can be imported into projects to create project component instances.

        **Response Format:**
        All ProseMirror editor content (description, internalNote) is automatically converted to HTML format for easy consumption. The response includes the complete hierarchical structure with all nested items and their metadata.

        **Access Control:**
        Only project components that belong to the specified project (component.projectId equals projectId) are accessible through this endpoint.
      operationId: ProjectComponentsController_getComponentDetails
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Component UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentDetailsResponseDto'
        '400':
          description: Invalid projectId or component id format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions or no access to project
        '404':
          description: Component not found or does not belong to the specified project
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project component details
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projectComponentsLibrary.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /projects/{projectId}/components/tree:
    get:
      description: |-
        Returns the complete hierarchical tree structure of components and their items for a specific project. Only non-deleted components and items are included in the response.

        **Component Tree Structure:**
        The project tree organizes work into a clear hierarchy:
        - **Components**: Top-level containers that group related work packages. Examples include "Website Development", "CRM Onboarding", or "E-commerce Setup". Each component represents a major deliverable or project phase.
        - **Items**: Building blocks within components. Each item can be one of four types:
          - **Epic**: Major sections or phases that group related work packages around a common theme (e.g., "Technical Implementation", "Design & Development")
          - **Work Package**: Self-contained, clearly defined tasks that deliver tangible results (e.g., "Set up hosting", "Design homepage layout"). Work packages can have timeframes, questions, todos, and test cases.
          - **Todo List**: Checklists of sub-steps or checkpoints perfect for recurring processes, quality control, or project preparation (e.g., "Kickoff Checklist", "Quality Control")
          - **Test Suite**: Formal acceptance tests for project results. Contains multiple test cases with steps and expected results (e.g., "Contact Form Tests", "SEO Review")

        **Nesting and Relationships:**
        Items can be nested to any depth, allowing for complex project structures. Each Work Package can contain Questions (to capture customer requirements), Todos (checklist items that can be checked off), and Test Cases (individual test steps within a Test Suite).

        **Use Cases:**
        This endpoint is ideal for displaying the complete project structure in a tree view, understanding project organization, and navigating the component hierarchy.
      operationId: ProjectComponentsController_getProjectTree
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project component tree retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentLibraryTreeResponseDto'
        '400':
          description: Invalid project ID format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get component tree for a specific project
      tags:
        - projects
        - components
        - questions
      x-required-scopes:
        - api:read
  /projects/{projectId}/components/item-details/{id}:
    get:
      description: |-
        Returns complete details of a component item from a project component tree including all test cases, todos, questions, conditions, and nested children.

        **What are Component Items?**
        Items are the building blocks within components that structure the project work. Each item can be one of four types:
        - **Epic**: Major sections or phases that group related work packages around a common theme (e.g., "Technical Implementation", "Design & Development"). Epics help track project progress at a strategic level.
        - **Work Package**: Self-contained, clearly defined tasks that deliver tangible results (e.g., "Set up hosting", "Design homepage"). Work packages can have timeframes, questions, todos, and test cases attached.
        - **Todo List**: Checklists of sub-steps or checkpoints perfect for recurring processes, quality control, or project preparation (e.g., "Kickoff Checklist", "Quality Control"). Each todo item can be checked off individually.
        - **Test Suite**: Formal acceptance tests for project results (e.g., "Contact Form Tests", "SEO Review"). Contains multiple test cases with steps and expected results. The project is considered finished when all test cases pass.

        **Attached Resources:**
        Each item can have various resources attached:
        - **Questions**: Capture customer-specific requirements or project details in a structured way (e.g., "Do you need e-commerce functionality?"). Answers can automatically affect effort calculation and pricing.
        - **Todos**: Checklist items within a Work Package or Todo List. Each todo can be checked off individually, supports comments, and tracks completion status with timestamps.
        - **Test Cases**: Individual test steps within a Test Suite. Each test case has steps, expected results, and can be evaluated as Passed, Failed, or PassedWithReservations.
        - **Conditions**: Rules that control when items are visible or active based on answers, todo completion, or test case status.

        **Response Format:**
        All ProseMirror editor content (description, internalNote, question descriptions, test case steps, etc.) is automatically converted to HTML format. The response includes the complete item structure with all nested children, making it easy to display the full item hierarchy.

        **Access Control:**
        Only project component items that belong to components in the specified project (component.projectId equals projectId) are accessible through this endpoint.
      operationId: ProjectComponentsController_getProjectComponentItemDetails
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
        - name: id
          required: true
          in: path
          description: Component item UUID
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentItemDetailsResponseDto'
        '400':
          description: Invalid projectId or id UUID format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to view component item details or access to project denied
        '404':
          description: Component item not found, is deleted, or does not belong to the specified project
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project component item details
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projectComponentsLibrary.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /projects/{projectId}/components:
    post:
      description: |-
        Creates a new project component (reusable project template) with name, description, icon, tags, and internal notes.

        **What are Project Components?**
        Project components are reusable project templates that standardize recurring project types. They serve as blueprints for planning, pricing, and executing similar projects consistently. Components connect requirements management, cost calculation, and execution in one unified workflow.

        **After Creating a Component:**
        Once created, you can add items (Epics, Work Packages, Todo Lists, Test Suites) to define the project structure. You can also add questions to capture customer requirements, todos for checklists, and test cases for acceptance testing.

        **Component Placement:**
        The component is automatically appended to the end of the project component list. You can reorder components later using the sort endpoint.

        **Content Format:**
        Description and internal note fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage. When retrieved, this content is converted back to HTML for easy consumption.

        **Side Effects:**
        Creating a component automatically triggers project configuration change tracking, marking the project as having been modified.
      operationId: ProjectComponentsController_createComponent
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectComponentDto'
      responses:
        '201':
          description: Component created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateProjectComponentResponseDto'
        '400':
          description: 'Invalid request: missing required fields or invalid UUID format for tags'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to create components or access to project denied
        '404':
          description: Project not found or not accessible
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new component in a project
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/import-from-library:
    post:
      description: |-
        Imports one or more reusable component templates from the workspace component library into the specified project.

        **Component Library vs Project Components:**
        The component system operates in two contexts:
        - **Library Components**: Workspace-wide reusable templates (projectId is null). These are blueprints with no live data - questions are defined but not answered, todos are not checked, test cases are not evaluated. Library components serve as templates that can be reused across multiple projects.
        - **Project Components**: Live instances in projects (projectId is set). These contain actual project data - questions can be answered, todos can be checked off, test cases can be evaluated, and items can be connected to tasks in the task management system.

        **What Gets Imported:**
        When you import a library component, the complete component structure is duplicated into the project, including:
        - All items (Epics, Work Packages, Todo Lists, Test Suites) with their complete hierarchy and nesting
        - All questions with their options and configurations (answers are initialized as empty/unanswered)
        - All todos with their descriptions (completion status is initialized as not done)
        - All test cases with steps and expected results (evaluations are initialized as not tested)
        - All conditions with their logic (automatically updated to reference the new item/question/todo/test case IDs in the project)

        **Automatic Processing:**
        After import, the system automatically:
        - Recalculates all conditions to ensure they reference the correct items in the project
        - Recalculates timeframes based on work packages and their configurations
        - Adds the imported components to the end of the project component list
        - Marks the project configuration as changed

        **Use Case:**
        This endpoint is perfect for standardizing project setup. Create reusable templates in the library once, then import them into new projects to get a consistent starting structure that can be customized per project.
      operationId: ProjectComponentsController_importFromLibrary
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: ids
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImportComponentsFromLibraryDto'
      responses:
        '201':
          description: Components imported successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportComponentsFromLibraryResponseDto'
        '400':
          description: 'Invalid request: empty componentIds array or invalid UUID format'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to import components or access to project denied
        '404':
          description: Project not found or component not found in library
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Import components from library into project
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - ids
        allowedFields:
          - ids
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/todos:
    post:
      description: |-
        Creates a new todo checklist item within a project component item. Todos are actionable checklist items that help track progress and ensure nothing is missed.

        **What are Todos?**
        Todos are checklist items used to track actionable tasks within component items. They are perfect for:
        - Recurring processes (tests, approvals, installation steps)
        - Quality control checkpoints
        - Project preparation steps
        - Final checks before go-live
        - Kickoff checklists
        - Review and approval workflows

        **Todo Features:**
        Each todo can be:
        - Checked off individually to track completion
        - Commented on to add notes or context
        - Tracked with completion status (isDone), who completed it (doneBy), and when (doneUpdatedAt)

        **Where Todos Can Be Attached:**
        Todos can be attached to Work Packages or Todo List items. When attached to a Todo List, they become sub-items in a checklist. When attached to a Work Package, they serve as actionable steps within that work package.

        **Content Format:**
        The description field accepts HTML or Markdown format and is automatically converted to IDoc format for storage. When retrieved, it is converted back to HTML for easy consumption.

        **Automatic Processing:**
        After creating a todo, the system automatically:
        - Calculates sort order as max(sort) + 1 for todos in the same item, positioning it at the end
        - Recalculates all conditions that depend on todo completion status
        - Updates timeframes if conditions changed
        - Marks the project configuration as changed

        **Use Case:**
        Create todos to break down work packages into actionable steps, create quality checklists, or define approval workflows. Todos can also be used in conditions to control when other items are visible or active.
      operationId: ProjectComponentsController_createTodo
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTodoDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOperationResponseDto'
        '400':
          description: 'Invalid request: invalid UUID format, missing required fields, or invalid content'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Component item not found or does not belong to the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new todo (checklist item) for a component item
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/items/{itemId}/todos/{todoId}:
    patch:
      description: Updates title and/or description of an existing todo item within a project component item. Only provided fields are updated (partial update). Description accepts HTML or Markdown format and is converted to IDoc for storage. Only todos belonging to component items in the specified project are accessible.
      operationId: ProjectComponentsController_updateTodo
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: todoId
          required: true
          in: path
          description: Todo UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440011
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTodoDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOperationResponseDto'
        '400':
          description: 'Invalid request: no fields provided for update or invalid UUID format'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components
        '404':
          description: Todo not found or does not belong to the specified item in the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an existing todo
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/todos/{id}:
    delete:
      description: Deletes a todo item from a project component item (soft delete - sets deletedAt timestamp). Todos are checklist items within component items (epics, features, tasks) that can be marked as complete and have comments. This endpoint validates that the todo exists and belongs to a component item in the specified project. The service automatically triggers condition cleanup (removes conditions targeting the deleted todo), recalculates conditions if needed, and marks the project configuration as changed.
      operationId: ProjectComponentsController_deleteTodo
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: id
          required: true
          in: path
          description: Todo UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440011
            type: string
      responses:
        '200':
          description: Todo deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid projectId, itemId, or todo id format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or no access to project
        '404':
          description: Todo not found, component item not found, or item does not belong to the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete (soft delete) a todo from a component item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/testcases/{id}:
    delete:
      description: Deletes a test case from a project component item (soft delete - sets deletedAt timestamp). Test cases are used to define test scenarios with steps and expected results for component items. This endpoint validates that the test case exists and belongs to a component item in the specified project. The service automatically triggers condition cleanup, recalculation, and timeframe updates.
      operationId: ProjectComponentsController_deleteTestCase
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: id
          required: true
          in: path
          description: Test case UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440010
            type: string
      responses:
        '200':
          description: Test case deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid projectId, itemId, or testCaseId format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to delete test cases or no access to project
        '404':
          description: Project not found, component item not found, or test case not found in the specified item
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete (soft delete) a test case
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: Partially updates an existing test case for a component item within a project. Only provided fields will be updated. Editor content (description, steps, expectedResult) accepts HTML or Markdown input when provided.
      operationId: ProjectComponentsController_updateTestCase
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440010
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: id
          required: true
          in: path
          description: Test case UUID
          schema:
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,testStatus
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTestCaseDto'
      responses:
        '200':
          description: Test case updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentTestCaseDto'
        '400':
          description: 'Invalid request: validation errors or test case does not belong to project'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project, item, or test case not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an existing test case
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - testStatus
        allowedFields:
          - id
          - title
          - description
          - steps
          - expectedResult
          - testComment
          - testStatus
          - testedBy
          - testedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/questions:
    post:
      description: |-
        Creates a new question for a component item within a project component. Questions capture customer-specific requirements or project details in a structured way, making work packages flexible and reusable.

        **What are Questions?**
        Questions are form fields attached to component items (typically Work Packages) that help capture customer requirements or project details. They make work packages flexible and reusable by allowing later individualization in projects. When customers answer questions, the answers can automatically affect effort calculation and pricing.

        **Question Types:**
        Each question can have one of eight types, each suited for different data collection needs:
        - **ShortText**: Single-line text input for names, titles, keywords, or short answers
        - **Editor**: Formatted multi-line text for descriptions, detailed explanations, or free text answers
        - **Checkbox**: Yes/No selection or multiple checkbox options. Answers can impact effort calculation if options have extraHours configured
        - **Radio**: Single choice from multiple predefined options. Answers can impact effort calculation if options have extraHours configured
        - **Files**: Upload files such as logos, documents, graphics, or reference materials
        - **Datepicker**: Select a date for deadlines, delivery dates, or scheduling
        - **Multiplier**: Dynamic calculation based on quantity. For example, "How many pages?" with a duration per page. The system calculates: Answer (quantity) × Duration per element = Total additional effort
        - **Person**: Select one or more people from the project team or clients

        **Key Features:**
        - **Automatic Effort Calculation**: Answers to Checkbox, Radio, and Multiplier questions can automatically affect the project effort and pricing
        - **Conditional Visibility**: Questions can have conditions that control when they are shown (e.g., only show if another question was answered a certain way)
        - **Flexible Options**: Checkbox and Radio questions support multiple options, each with optional extraHours that affect effort calculation

        **Validation Rules:**
        For Checkbox and Radio question types, at least one option must be provided in the options array. The description field accepts HTML or Markdown input and is automatically converted to IDoc format for storage.

        **Automatic Processing:**
        After creating a question, the system automatically:
        - Assigns a sort order (max existing sort + 1) to position the question in the list
        - Recalculates all conditions to ensure dependent items are shown/hidden correctly
        - Updates timeframes if the question affects effort calculation
        - Triggers summary generation if needed

        **Use Case:**
        Use questions to create dynamic, customer-specific project configurations. For example, ask "Do you need e-commerce functionality?" and automatically add extra work packages or adjust pricing based on the answer.
      operationId: ProjectComponentsController_createQuestion
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectQuestionDto'
      responses:
        '201':
          description: Question created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    example: e57dc37b-7693-4d06-b49c-17084b773aff
                    description: ID of the created question
        '400':
          description: 'Invalid request: options array must have at least 1 item for Checkbox/Radio types, or invalid UUID/description format'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project not found, item not found, or item does not belong to a component in the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new question for a component item
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/questions/{id}:
    patch:
      description: 'Updates only the provided fields of an existing question for a component item within a project. Questions support multiple types (ShortText, Editor, Checkbox, Radio, Files, Datepicker, Multiplier, Person). For Checkbox/Radio types, options must be provided if type is being updated to one of these types. The description field accepts HTML or Markdown input and is stored in IDoc format. Options management: options with id are updated, options without id are created, options removed from array are deleted. Conditions management: conditions with id are updated, conditions without id are created, conditions removed from array are deleted. This endpoint automatically recalculates conditions, updates timeframes, and triggers summary generation.'
      operationId: ProjectComponentsController_updateQuestion
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Question UUID
          schema:
            example: f47dc37b-7693-4d06-b49c-17084b773baa
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectQuestionDto'
      responses:
        '200':
          description: Question updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    example: f47dc37b-7693-4d06-b49c-17084b773baa
                    description: ID of the updated question
        '400':
          description: 'Invalid request: options array must have at least 1 item for Checkbox/Radio types when type is updated, or invalid UUID/description format'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project not found, item not found, question not found, or question does not belong to an item in the project
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update an existing question
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/questions/sort:
    post:
      description: Reorders questions within a component item by providing a new sort order array of question IDs. The sort order determines the display order of questions in the UI and affects condition evaluation order. All questions for the item must be included in the newSortOrder array.
      operationId: ProjectComponentsController_sortProjectQuestions
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortProjectQuestionsDto'
      responses:
        '200':
          description: Questions reordered successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid UUID format or failed to sort questions
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Item not found or does not belong to a component in the project, or question IDs not found in the item
        '422':
          description: 'Incomplete sort order: not all questions for the item are included in newSortOrder'
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder questions within a component item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/questions/{id}/answer:
    post:
      description: |-
        Submits an answer to a question within a project component item. This endpoint allows users to provide answers to questions that capture customer requirements or project details.

        **Question Types and Answer Formats:**
        Questions can have different types, and each type requires a specific answer format. Only provide the answer field(s) relevant to the question type:
        - **ShortText**: Provide shortTextAnswer (string)
        - **Editor**: Provide editorAnswer (HTML or Markdown string)
        - **Checkbox**: Provide selectedOptions (array of option IDs for multiple selections)
        - **Radio**: Provide selectedOption (single option ID string)
        - **Files**: Provide fileUrls (array of file URL strings)
        - **Datepicker**: Provide dateAnswer (ISO 8601 date string)
        - **Multiplier**: Provide multiplierAnswer (number)
        - **Person**: Provide selectedPersonId (array of person IDs)

        **Automatic Processing:**
        When you submit an answer, the system automatically:
        - Tracks who answered (answeredBy is set to the current user)
        - Records when the answer was provided (answeredAt timestamp)
        - Calculates the isAnswered flag based on whether a valid answer was provided
        - Updates option selections for Checkbox/Radio types (marks options as selected)
        - Recalculates project conditions to show/hide dependent items based on the answer
        - Updates effort calculations if the question affects pricing (for Checkbox/Radio/Multiplier types)
        - Updates timeframes if effort changed

        **Permissions:**
        This endpoint uses the processComponentsQuestions permission, which is more permissive than manageComponents. This allows users to answer questions and participate in project configuration without requiring full component edit access.
      operationId: ProjectComponentsController_answerQuestion
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component Item UUID
          schema:
            example: d47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Question UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnswerProjectQuestionDto'
      responses:
        '200':
          description: Question answered successfully
        '400':
          description: 'Invalid request: invalid UUID format, invalid date format, or invalid option IDs'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions (processComponentsQuestions) or access to project denied
        '404':
          description: Question not found or question does not belong to the component item in the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Answer a question in a project component item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.processComponentsQuestions
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/questions/{questionId}:
    delete:
      description: Soft deletes a question from a component item within a project. The question must exist and belong to the specified component item in the project. This operation automatically cleans up invalid condition targets, recalculates conditions, updates component item timeframes, and marks the project configuration as changed.
      operationId: ProjectComponentsController_deleteQuestion
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: 123e4567-e89b-12d3-a456-426614174000
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: questionId
          required: true
          in: path
          description: Question UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: Question deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid project ID, item ID, or question ID format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Question not found, item not found, or item does not belong to the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a question from a project component item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/items/{itemId}/testcases:
    post:
      description: |-
        Creates a new test case for a component item within a project. Test cases document formal review and approval criteria for project results.

        **What are Test Cases?**
        Test cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results, ensuring that all delivered work meets the agreed requirements. Test cases are part of the acceptance testing process.

        **Test Case Structure:**
        Each test case consists of four main parts:
        - **Title**: Short, clear title that describes what is being tested (e.g., "Check confirmation email destination", "Validate form input fields")
        - **Description**: Explains what is being tested and why it matters. Provides context for the test.
        - **Steps**: Step-by-step instructions for running the test. Should be clear and reproducible (e.g., "1. Navigate to login page 2. Enter credentials 3. Click login button")
        - **Expected Result**: Describes the expected or correct system behavior when the test is run successfully (e.g., "User is successfully logged in and redirected to dashboard")

        **Test Case Evaluation:**
        After a test case is created, it can be evaluated later using the evaluate endpoint. Test cases can be marked as:
        - **Passed**: Test completed successfully and met all expected results
        - **Failed**: Test did not meet the expected result or revealed issues
        - **PassedWithReservations**: Test passed but with notes or concerns that should be documented

        **Project Completion:**
        The project is considered finished when all test cases within all Test Suites have been passed successfully. This ensures quality and formal acceptance before project completion.

        **Content Format:**
        All editor content fields (description, steps, expectedResult) accept HTML or Markdown input and are automatically converted to IDoc format for storage. When retrieved, they are converted back to HTML for easy consumption.

        **Use Case:**
        Create test cases to define formal acceptance criteria for project deliverables. This ensures consistent quality standards and provides clear documentation of what was tested and approved.
      operationId: ProjectComponentsController_createTestCase
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440010
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID. Items are the building blocks within components (Epics, Work Packages, Todo Lists, Test Suites).
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTestCaseDto'
      responses:
        '201':
          description: Test case created successfully. Returns the ID of the created test case.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    example: 550e8400-e29b-41d4-a716-446655440010
        '400':
          description: 'Invalid request: validation errors or item does not belong to project'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project or item not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new test case (acceptance test step) for a component item
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/testcases/{id}/evaluate:
    post:
      description: Mark a test case as passed or failed with optional comment. Uses Projects.processComponentsTestCases permission, allowing test evaluation without full component management access. Automatically sets testedBy and testedAt fields. Updates project configuration flag and recalculates conditions.
      operationId: ProjectComponentsController_evaluateTestCase
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item ID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440001
            type: string
        - name: id
          required: true
          in: path
          description: Test case ID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440002
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,testStatus
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvaluateTestCaseDto'
      responses:
        '200':
          description: Test case evaluation saved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentTestCaseDto'
        '400':
          description: Invalid request data or test case does not belong to component in project
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User does not have permission to evaluate test cases
        '404':
          description: Test case not found or does not belong to the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Evaluate a test case
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - testStatus
        allowedFields:
          - id
          - title
          - description
          - steps
          - expectedResult
          - testComment
          - testStatus
          - testedBy
          - testedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.processComponentsTestCases
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/tree/sort:
    post:
      description: Allows reordering components and items at the same level, reparenting items to different components or parent items, and moving items between components (automatically updates componentId for all descendants). Automatically handles side-effects including conditions recalculation, configurationChanged flag updates, summary generation checks, and timeframe updates.
      operationId: ProjectComponentsController_saveComponentsSort
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveComponentsSortDto'
      responses:
        '200':
          description: Tree structure updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Products sort updated successfully
        '400':
          description: Invalid request format, invalid UUIDs, or invalid type values
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project not found or component/item not found in project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Move/reorder items in project component tree
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/tasks/generate:
    post:
      description: Generate tasks from a component item and its children. The endpoint recursively processes items (two levels deep), skips Epic items, and only creates tasks for items that don't already have associated tasks. Can be used with either a Component ID or a ComponentItem ID.
      operationId: ProjectComponentsController_generateTasksFromItems
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: itemId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: createdTasksCount,tasksNumbers
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GenerateTasksFromItemsDto'
      responses:
        '200':
          description: Tasks generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenerateTasksFromItemsResponseDto'
        '400':
          description: 'Invalid request: invalid projectId, itemId format, or itemType value. Items may already be generating.'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project not found, item not found, or item does not belong to the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Generate tasks from a component item
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - createdTasksCount
          - tasksNumbers
        allowedFields:
          - createdTasksCount
          - componentItemsCount
          - tasksNumbers
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/tasks/disconnect:
    post:
      description: Disconnect a component item from its associated task(s). Optionally, the associated task(s) can be deleted after disconnection by setting deleteTask to true. All tasks connected to the item via entityUniqueId will be disconnected. If no tasks are connected to the item, a 404 error will be returned.
      operationId: ProjectComponentsController_disconnectItemFromTask
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: itemId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DisconnectItemFromTaskDto'
      responses:
        '200':
          description: Item successfully disconnected from task(s)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: 'Invalid request: invalid projectId or itemId format, or validation error in request body'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project not found, item not found, item does not belong to the project, or no tasks are connected to the item
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Disconnect a component item from its associated task(s)
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/{id}:
    patch:
      description: Updates only the provided fields of an existing component in a project. The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. Updating a component triggers project configuration change tracking.
      operationId: ProjectComponentsController_patchComponent
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectComponentDto'
      responses:
        '200':
          description: Component updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateProjectComponentResponseDto'
        '400':
          description: 'Invalid request: invalid UUID format for component ID, project ID, or tag IDs'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Component not found, component does not belong to project, or project not accessible
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update a project component
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: Soft deletes a component by setting the deletedAt timestamp. The component is marked as deleted but not permanently removed from the database. Automatically cleans up invalid condition targets, recalculates project conditions, updates project configurationChanged flag, and updates timeframes.
      operationId: ProjectComponentsController_deleteComponent
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Component UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: Component deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid component ID or project ID format (not valid UUID)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Component not found, component does not belong to project, component already deleted, or project not accessible
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete (soft delete) a component in a project
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/todos/{id}/toggle:
    post:
      description: Toggles the completion status of a todo item within a project component item. Updates the isDone status, sets doneBy to the current user, and updates doneUpdatedAt timestamp. This endpoint uses the processComponentsTodos permission, allowing users to toggle todo completion without requiring full component management permissions. After toggling, the system recalculates conditions and marks the project configuration as changed.
      operationId: ProjectComponentsController_toggleTodo
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID (the item that contains the todo)
          schema:
            example: b58dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Todo UUID
          schema:
            example: c69dc37b-7693-4d06-b49c-17084b773aff
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ToggleTodoDto'
      responses:
        '200':
          description: Todo completion status toggled successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid UUID format or missing isDone field
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions or no access to project
        '404':
          description: Todo not found or does not belong to a component item in the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Toggle todo completion status
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.processComponentsTodos
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/todos/sort:
    post:
      description: Reorders todos within a component item by providing a new sort order array. The sort order determines the display order of todos in the UI and affects condition evaluation order. All todos in the newSortOrder array must belong to the specified item. This operation automatically recalculates conditions if needed and marks the project configuration as changed.
      operationId: ProjectComponentsController_sortTodos
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortTodosDto'
      responses:
        '200':
          description: Todos successfully reordered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid projectId, itemId, or todo IDs in sort order
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions or no access to project
        '404':
          description: Item not found or does not belong to the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Sort todos within an item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/testcases/sort:
    post:
      description: Reorders test cases within a component item by providing a new sort order array. The sort order determines the display order of test cases in the UI and affects condition evaluation order. All test case IDs in the array must belong to the specified item.
      operationId: ProjectComponentsController_sortTestCases
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortTestCasesDto'
      responses:
        '200':
          description: Test cases successfully sorted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid projectId, itemId, or test case IDs format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions or no access to project
        '404':
          description: Project or item not found, or item does not belong to the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Sort test cases within a component item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/{id}/export:
    post:
      description: Exports a project component to the workspace library, making it available for reuse across all projects. The exported component includes all nested data (items, questions, todos, test cases, conditions) with ID references automatically updated. The component is placed at the end of the library with an optional custom name. Requires both Projects.manageComponents and ProjectComponentsLibrary.edit permissions.
      operationId: ProjectComponentsController_exportComponentToLibrary
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Component UUID to export
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExportComponentToLibraryDto'
      responses:
        '201':
          description: Component exported to library successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExportComponentToLibraryResponseDto'
        '400':
          description: Invalid projectId or component id format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or edit library, or no access to project
        '404':
          description: Component not found, deleted, or does not belong to the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Export a project component to the library
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
        - projectComponentsLibrary.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{id}:
    patch:
      description: Updates only the provided fields of an existing component item (epic, feature, task) in a project. The description and internalNote fields accept HTML or Markdown and will be converted to the internal format. When the conditions array is provided, it completely replaces existing conditions. Conditions with id are updated, conditions without id are created, and conditions not in the array are deleted. Updating an item triggers automatic side effects including condition recalculation, project configuration change tracking, and timeframe updates.
      operationId: ProjectComponentsController_patchItem
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Component item UUID
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchComponentItemDto'
      responses:
        '200':
          description: Item updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    example: 3047983b-d798-4aff-8dd4-628cf1900703
        '400':
          description: 'Invalid request: invalid UUID format for item ID, project ID, component ID, tag IDs, or invalid type/timeFrame combination'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Item not found, item does not belong to project component, component not found, or parent item not found
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update a component item
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: Soft deletes a component item (epic, feature, task) in a project component. The item will be marked as deleted but not permanently removed. Automatically cleans up invalid condition targets and recalculates project conditions.
      operationId: ProjectComponentsController_deleteItem
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
        - name: id
          required: true
          in: path
          description: Component item UUID
          schema:
            example: 3047983b-d798-4aff-8dd4-628cf1900703
            type: string
      responses:
        '200':
          description: Item deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid projectId or item id format
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Item not found or does not belong to a component in the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete component item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/tasks/connect:
    post:
      description: Connects a component item (epic, feature, or task) to an existing task in the task management system. If the item already has a task connection, it will be disconnected first before establishing the new connection. Component tags are automatically merged into the task's tags. To disconnect an item from a task without connecting to a new one, pass null or omit the taskId in the request body.
      operationId: ProjectComponentsController_connectItemToTask
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component item UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectItemToTaskDto'
      responses:
        '200':
          description: Item connected/disconnected successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: 'Invalid request: invalid UUID format for projectId, itemId, or taskId'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Project not found, item not found, item does not belong to project, or task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Connect component item to task
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items/{itemId}/todos/{id}/comment:
    post:
      description: Adds or updates a comment on a todo item within a project component item. Todos are checklist items that can be marked as complete and have comments. The comment content is provided in HTML or Markdown format and converted to internal IDoc format. Uses the processComponentsTodos permission, allowing users to comment on todos without full component edit access. The system automatically recalculates project conditions and sets the project configurationChanged flag after adding a comment.
      operationId: ProjectComponentsController_addTodoComment
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: itemId
          required: true
          in: path
          description: Component Item UUID
          schema:
            example: d47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Todo UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddTodoCommentDto'
      responses:
        '200':
          description: Comment added successfully
        '400':
          description: 'Invalid request: invalid UUID format or missing todoComment field'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions (processComponentsTodos) or access to project denied
        '404':
          description: Todo not found or todo does not belong to the component item in the project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Add a comment to a todo in a project component item
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.processComponentsTodos
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/{id}/duplicate:
    post:
      description: Creates a copy of an existing component including all nested data (items, questions, todos, test cases, and conditions). The duplicated component is automatically named with "(Copy)" suffix and placed immediately after the original component in the sort order. Condition references are automatically updated to point to the new duplicated items. Triggers project configuration change tracking and recalculates conditions and timeframes.
      operationId: ProjectComponentsController_duplicateComponent
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Component UUID to duplicate
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '201':
          description: Component duplicated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DuplicateComponentResponseDto'
        '400':
          description: Invalid component ID format (not a valid UUID)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or no access to project
        '404':
          description: Component not found or does not belong to the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Duplicate a component within the same project
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/components/{id}/recalculate-estimates:
    post:
      description: |-
        Recalculates time estimates for all items (epics and non-epics) within a project component. This includes:
        - Recalculating individual item estimates based on timeFrame and question multipliers/extra hours
        - Recalculating epic estimates by summing their children's estimates
        - Updating the component's totalTimeFrame
        - Respecting visibility conditions (items with invalid conditions are excluded)

        Items are processed bottom-up (children first, then parents) to ensure accurate calculations. Only components that belong to the specified project can be recalculated through this endpoint.
      operationId: ProjectComponentsController_recalculateComponentEstimates
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: a47dc37b-7693-4d06-b49c-17084b773aff
            type: string
        - name: id
          required: true
          in: path
          description: Component UUID
          schema:
            example: e57dc37b-7693-4d06-b49c-17084b773aff
            type: string
      responses:
        '200':
          description: Estimates recalculated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid projectId or component ID format (not a valid UUID)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or no access to project
        '404':
          description: Component not found or does not belong to the specified project
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Recalculate all estimates for a project component
      tags:
        - projects
        - components
        - questions
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/items:
    post:
      description: |-
        Creates a new component item within a project component. Items are the building blocks that structure project work.

        **What are Component Items?**
        Items are the building blocks within components that organize and structure project work. Each item can be one of four types:

        1. **Epic**: Major sections or phases that group related work packages around a common theme or goal
           - Example: "Technical Implementation", "Design & Development", "Project Kickoff & Preparation"
           - Can contain Work Packages, Todo Lists, or Test Suites
           - Can nest other Epics to create complex hierarchies (arbitrary nesting depth)
           - Helps track project progress at a strategic level
           - TimeFrame is not required for Epics

        2. **Work Package**: Self-contained, clearly defined tasks that deliver tangible results
           - Example: "Set up domain and hosting", "Design homepage layout", "Configure CMS"
           - Can have timeframes/estimates (required field)
           - Can contain Questions (to capture customer requirements), Todos (checklist items), and Test Cases (acceptance test steps)
           - Can nest other Work Packages for complex tasks
           - Acts as a draft before creating tickets in the task management system

        3. **Todo List**: Checklists of sub-steps or checkpoints
           - Example: "Kickoff Checklist", "Quality Control Checklist", "Go-live Approval"
           - Perfect for recurring processes (tests, approvals, installation steps)
           - Each todo item within the list can be checked off individually
           - TimeFrame is required (estimated time to complete the checklist)

        4. **Test Suite**: Formal acceptance tests for project results
           - Example: "Contact Form Tests", "Responsive Design Check", "SEO Review"
           - Contains multiple test cases with steps and expected results
           - Project is considered finished when all test cases within all Test Suites pass
           - TimeFrame is required (estimated time to run all tests)

        **Item Placement:**
        Items can be created at the root level of a component or nested under parent items (Epics or other Work Packages). This allows for flexible project structures that match your workflow.

        **Content Format:**
        The description and internalNote fields accept HTML or Markdown input and are automatically converted to the internal IDoc format for storage. When retrieved, they are converted back to HTML for easy consumption.

        **Validation Rules:**
        TimeFrame is required for all item types except Epic. The timeframe represents the estimated effort in hours for completing the item.
      operationId: ProjectComponentsController_createItem
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project UUID
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateComponentItemDto'
      responses:
        '201':
          description: Component item created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateComponentItemResponseDto'
        '400':
          description: 'Invalid request: invalid UUID format, missing required fields, or validation errors'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions to manage components or access to project denied
        '404':
          description: Component not found or does not belong to the specified project
        '422':
          description: Editor content conversion errors
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new component item (Epic, Work Package, Todo List, or Test Suite)
      tags:
        - projects
        - components
        - questions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/documentation:
    get:
      description: |-
        Project documentation allows you to capture, structure, and share project-related knowledge directly in the project context. This helps keep important information like technical notes, repeated questions, and project-specific details organized and accessible.

        Retrieves a paginated grid of project documentation with filtering and sorting capabilities. Quick search available on: title. Filterable fields: id, projectId, projectName, title, userId, createdAt, updatedAt, sort. Sortable fields: id, projectId, projectName, title, userId, createdAt, updatedAt, sort.

        **Features:**
        - View all documentation articles across the workspace or filter by specific project
        - Each article includes title, author, and last update timestamp
        - Articles can be sorted and filtered using grid parameters

        **Use cases:**
        - Document project setup procedures and access credentials
        - Capture technical quirks and troubleshooting steps
        - Share project-specific notes and guidelines
        - Keep track of important decisions and context
      operationId: ProjectDocumentationController_listProjectDocumentation
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: title'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Documentation ID
            - **projectId** (set): Project ID
            - **projectName** (string): Project name
            - **title** (string): Documentation title
            - **userId** (string): Author user ID
            - **createdAt** (date): Creation timestamp
            - **updatedAt** (date): Last update timestamp
            - **sort** (number): Sort order

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Documentation ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **projectName** (string): Project name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **title** (string): Documentation title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **userId** (string): Author user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **sort** (number): Sort order (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Documentation ID
            - **projectId**: Project ID
            - **projectName**: Project name
            - **title**: Documentation title
            - **userId**: Author user ID
            - **createdAt**: Creation timestamp
            - **updatedAt**: Last update timestamp
            - **sort**: Sort order

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** sort (asc)
          schema:
            example: '[{"field":"sort","direction":"asc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, projectId, projectName, title, userId, createdAt, updatedAt, sort
          schema:
            example: id,projectId
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of project documentation articles
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project documentation articles
      tags:
        - projects
        - project-documentation
        - projects
      x-required-permissions:
        - projects.viewDocumentation
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new project documentation article to capture and share project-related knowledge.

        **What to include:**
        - Clear, descriptive title that helps team members find the article quickly
        - Rich text content with formatting, headings, lists, and links
        - Project-specific information like access credentials, technical notes, or procedures

        **Content format:**
        - Accepts HTML or Markdown input
        - Content is automatically converted to internal ProseMirror format for storage
        - Supports rich text formatting including headings, lists, tables, and code blocks
        - When retrieved via GET endpoint, content is returned as HTML

        **Permissions:**
        - Requires "createDocumentation" permission for the project
        - Article is automatically associated with the authenticated user as the author
      operationId: ProjectDocumentationController_createProjectDocumentation
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,projectId,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectDocumentationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectDocumentationResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      projectId:
                        - Project ID is required
                      title:
                        - Title is required
                      content:
                        - Content is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new project documentation article
      tags:
        - projects
        - project-documentation
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - projectId
          - title
        allowedFields:
          - id
          - projectId
          - title
          - content
          - userId
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.createDocumentation
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/documentation/{id}:
    get:
      description: |-
        Retrieves complete details of a specific project documentation article, including the full formatted content.

        **Returns:**
        - Article ID, project ID, and title
        - Full article content in HTML format (converted from internal ProseMirror format)
        - Author information (user ID who created the article)
        - Creation and last update timestamps

        The content field contains HTML that can be directly rendered in a browser or HTML viewer. The content supports basic text formatting including headings, paragraphs, bold, italic, links, and lists.
      operationId: ProjectDocumentationController_getProjectDocumentationDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectDocumentationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project documentation article details
      tags:
        - projects
        - project-documentation
        - projects
      x-required-permissions:
        - projects.viewDocumentation
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates specific fields of an existing project documentation article. Only provided fields will be updated; all other fields remain unchanged.

        **Update behavior:**
        - All fields are optional - only include fields you want to update
        - If content is provided, it replaces the entire article content
        - If projectId is provided, the article is moved to the specified project
        - If title is provided, only the title is updated

        **Content format:**
        - Accepts HTML or Markdown input (same as create endpoint)
        - Content is converted to internal format and stored
        - Supports rich text formatting including headings, lists, tables, and code blocks

        **Permissions:**
        - Requires "manageDocumentation" permission for the project
        - Original author information is preserved
      operationId: ProjectDocumentationController_patchProjectDocumentation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,projectId,title
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectDocumentationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectDocumentationResponseDto'
        '400':
          description: Validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update a project documentation article
      tags:
        - projects
        - project-documentation
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - projectId
          - title
        allowedFields:
          - id
          - projectId
          - title
          - content
          - userId
          - createdAt
          - updatedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageDocumentation
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a project documentation article. The article is marked as deleted and will no longer appear in list queries, but it is not permanently removed from the database.

        **Deletion behavior:**
        - Article is marked with a deletedAt timestamp
        - Article will not appear in GET list queries
        - Article details can still be retrieved directly by ID if needed
        - Data is preserved for potential recovery or audit purposes

        **Permissions:**
        - Requires "manageDocumentation" permission for the project
      operationId: ProjectDocumentationController_deleteProjectDocumentation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a project documentation article
      tags:
        - projects
        - project-documentation
        - projects
      x-required-permissions:
        - projects.manageDocumentation
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/documents/contact-persons:
    get:
      description: |-
        **What are contact persons?**
        Contact persons are users who appear as the addressed recipient in project documents. They represent the client or stakeholder who will receive and review the document.

        **How contact persons are determined:**
        - For external projects (projects linked to an organization): Returns all members of the linked organization
        - For internal projects: Returns all employees in the workspace

        **What is returned:**
        A list of users with their ID, full name, position/title, and avatar. This list is used when creating or updating documents to select who should be listed as the contact person in the document header and recipient fields.

        **Use cases:**
        - Populate dropdown/select fields when creating documents
        - Display available contacts when editing document properties
        - Ensure only valid contacts are assigned to documents
      operationId: ProjectDocumentsController_getContactPersons
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
      responses:
        '200':
          description: Contact persons retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectDocumentUserResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get available contact persons for document creation
      tags:
        - projects
        - documents
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /projects/{projectId}/documents:
    get:
      description: |-
        **What are project documents?**
        Project documents are structured documents (estimates, specifications, contracts) that are automatically generated from project data. They are based on saved project versions (snapshots) and can include dynamic content, variables, and conditional sections.

        **Document types:**
        - **Estimate**: Commercial proposals summarizing project services, quantities, discounts, and costs
        - **Specification**: Detailed requirement documents describing what must be delivered
        - **Custom Editor**: Free-form documents like contracts, NDAs, or custom agreements

        **What is returned:**
        A paginated grid/list of all documents for the specified project, including:
        - Document title, type, and status
        - Associated project version (snapshot)
        - Contact person assigned to the document
        - Creation date
        - All fields can be filtered, sorted, and paginated

        **Document statuses:**
        - Draft: Initial version, not yet finalized
        - Waiting for Approval: Sent to client for review
        - Final: Ready for signing
        - Accepted: Approved by client
        - Rejected: Declined by client
        - Parked: Temporarily set aside

        **Important notes:**
        - Documents are always linked to a specific project version for traceability
        - Only documents from the current workspace are returned
        - Requires permission to view projects in the workspace

        Retrieves a paginated grid of documents with filtering and sorting capabilities. Quick search available on: title. Filterable fields: id, title, type, status, contactUserId, snapshotId, createdAt. Sortable fields: id, title, type, status, contactUserId, snapshotId, createdAt.
      operationId: ProjectDocumentsController_listDocuments
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: title'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Document ID
            - **title** (string): Document title
            - **type** (set): Document type (Estimate, Specification, CustomEditor)
            - **status** (set): Document status (Draft, Final, WaitingForApproval, etc.)
            - **contactUserId** (string): Contact person user ID
            - **snapshotId** (string): Project snapshot ID
            - **createdAt** (date): Document creation timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Document ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **title** (string): Document title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Document type (Estimate, Specification, CustomEditor) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **status** (set): Document status (Draft, Final, WaitingForApproval, etc.) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **contactUserId** (string): Contact person user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **snapshotId** (string): Project snapshot ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Document creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Document ID
            - **title**: Document title
            - **type**: Document type (Estimate, Specification, CustomEditor)
            - **status**: Document status (Draft, Final, WaitingForApproval, etc.)
            - **contactUserId**: Contact person user ID
            - **snapshotId**: Project snapshot ID
            - **createdAt**: Document creation timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, title, type, status, contactUserId, snapshotId, createdAt
          schema:
            example: id,title
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of project documents
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List documents for a project
      tags:
        - projects
        - documents
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        **What are project documents?**
        Project documents are structured documents automatically generated from project data. They can be estimates, specifications, or custom documents (contracts, NDAs, etc.) that pull in project, organization, and company data through variables.

        **Document creation workflow:**
        1. **Select project version**: Documents must be linked to a saved project version (snapshot) for traceability. Use GET /projects/:id/versions to get available versions.
        2. **Choose document type**: Estimate (commercial proposal), Specification (requirements), or CustomEditor (free-form document)
        3. **Set basic properties**: Title, description, status (usually starts as Draft)
        4. **Assign contact person**: Select from available contacts (use GET /projects/:id/documents/contact-persons)
        5. **Configure layout**: Enable table of contents, title page, indication mode (marks as non-binding estimate)
        6. **Set heading style**: Controls automatic numbering of headings (important for legal documents)
        7. **Add custom variables**: If using a template with custom variables, provide values for each variable

        **Content format:**
        - Description and customEditorContent accept HTML or Markdown
        - Content is automatically converted to internal IDoc format for storage
        - When retrieved, content is converted back to HTML for display
        - Supports rich formatting: headings, lists, tables, links, etc.

        **Custom variables:**
        If your document template includes custom variables, provide them in the customVariables array:
        - Each variable needs: name, type, and value (matching the type)
        - Types: Text, LongText, Number, Boolean, Date, Select, MultiSelect
        - For Select/MultiSelect: provide options array and select value(s)
        - Required variables must have values provided

        **Validation:**
        - Project version must exist and belong to the project
        - Contact person must be available for this project type
        - All required custom variables must have values
        - Document type and status must be valid enum values

        **What is returned:**
        The created document with all fields, including converted HTML content and custom variable values.

        **Use cases:**
        - Create new estimate for client approval
        - Generate specification document from project requirements
        - Create custom contract or agreement document
        - Automate document generation from project data
      operationId: ProjectDocumentsController_createDocument
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,projectId,type,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectDocumentDto'
      responses:
        '201':
          description: Document created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectDocumentResponseDto'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Permission denied
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create new document
      tags:
        - projects
        - documents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - projectId
          - type
          - status
        allowedFields:
          - id
          - title
          - description
          - projectId
          - snapshotId
          - contactUserId
          - type
          - status
          - includeToc
          - indicationMode
          - titlePage
          - headingStyle
          - customEditorContent
          - customVariables
          - offerNumber
          - formattedOfferNumber
          - validUntil
          - language
          - documentStyle
          - breakdownLevel
          - introText
          - outroText
          - createdAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageDocuments
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/documents/{id}:
    get:
      description: |-
        **What is returned:**
        Complete details for a specific project document, including all metadata, content, and configuration.

        **Content format:**
        - Description and custom editor content are returned as HTML (converted from internal IDoc format)
        - This allows direct display in web browsers or further HTML processing
        - Original formatting, headings, lists, and tables are preserved

        **Custom variables:**
        If the document uses custom variables (defined in templates), all variable definitions and their current values are included:
        - Variable name, type, and description
        - Current value (formatted according to variable type)
        - Available options (for Select/MultiSelect types)
        - Required flag indicating if value must be provided

        **Document configuration:**
        - Layout options: table of contents, title page, indication mode
        - Heading style: controls how headings are numbered (normal, sequential, sequential from level 2, sequential with paragraph signs)
        - Type and status: document category and workflow state
        - Project version binding: which project snapshot this document is based on

        **Use cases:**
        - Display document in a viewer/editor interface
        - Show document metadata and properties
        - Edit document content and variables
        - Export document to DOCX format
        - Track document status and history
      operationId: ProjectDocumentsController_getDocument
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
        - name: id
          required: true
          in: path
          description: Document ID
          schema:
            type: string
      responses:
        '200':
          description: Document retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectDocumentResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Document not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get document details
      tags:
        - projects
        - documents
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        **How partial updates work:**
        Only fields provided in the request body will be updated. All other fields remain unchanged. This allows flexible updates without needing to send the entire document.

        **Content updates:**
        - If description or customEditorContent is provided, it replaces the existing content
        - If set to null or empty string, the content is cleared
        - If omitted, existing content is preserved
        - Content accepts HTML or Markdown and is converted to internal format

        **Custom variables:**
        - If customVariables array is provided, it completely replaces all existing custom variables
        - If omitted, existing custom variables are preserved
        - To update individual variables, include all variables (updated and unchanged) in the array

        **Validation:**
        - Project version (if provided) must exist and belong to the project
        - Contact person (if provided) must be available for this project type
        - All provided custom variables must have valid values matching their types

        **Common update scenarios:**
        - Change document status (e.g., Draft → Waiting for Approval)
        - Update document title or description
        - Modify custom variable values
        - Change contact person assignment
        - Update layout options (table of contents, title page, etc.)
        - Switch to a different project version

        **What is returned:**
        The updated document with all fields, including converted HTML content and custom variable values.
      operationId: ProjectDocumentsController_updateDocument
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
        - name: id
          required: true
          in: path
          description: Document ID
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,projectId,type,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectDocumentDto'
      responses:
        '200':
          description: Document updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectDocumentResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Document not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update document
      tags:
        - projects
        - documents
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - projectId
          - type
          - status
        allowedFields:
          - id
          - title
          - description
          - projectId
          - snapshotId
          - contactUserId
          - type
          - status
          - includeToc
          - indicationMode
          - titlePage
          - headingStyle
          - customEditorContent
          - customVariables
          - offerNumber
          - formattedOfferNumber
          - validUntil
          - language
          - documentStyle
          - breakdownLevel
          - introText
          - outroText
          - createdAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageDocuments
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **How deletion works:**
        This endpoint performs a soft delete - the document is marked as deleted but not permanently removed from the database. This preserves data integrity and allows for audit trails.

        **What happens:**
        - Document is marked with deletedAt timestamp
        - Document no longer appears in list queries (GET /projects/:id/documents)
        - Document details cannot be retrieved (returns 404)
        - Historical data is preserved for compliance and auditing

        **Validation:**
        - Document must exist and belong to the specified project
        - Document must belong to the current workspace
        - Requires permission to manage documents

        **Use cases:**
        - Remove documents that are no longer needed
        - Clean up draft documents
        - Archive outdated versions
        - Maintain data retention policies

        **Note:** This is a permanent operation. There is no undo functionality. If you need to restore a deleted document, contact support.
      operationId: ProjectDocumentsController_deleteDocument
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
        - name: id
          required: true
          in: path
          description: Document ID
          schema:
            type: string
      responses:
        '204':
          description: Document deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Document not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete document
      tags:
        - projects
        - documents
      x-required-permissions:
        - projects.manageDocuments
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/documents/preview-offer-number/{snapshotId}:
    get:
      description: Generates a preview formatted offer number that will be assigned when the document is created. This is used for UI display purposes only. Requires snapshotId to format with correct version.
      operationId: ProjectDocumentsController_getPreviewOfferNumber
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
        - name: snapshotId
          required: true
          in: path
          description: Snapshot ID for version information
          schema:
            type: string
      responses:
        '200':
          description: Preview formatted offer number
          content:
            application/json:
              schema:
                type: object
                properties:
                  formattedOfferNumber:
                    type: string
                    example: WKM-1-v2-27012026-1
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get preview formatted offer number for new document
      tags:
        - projects
        - documents
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /projects/{projectId}/interim-payments:
    get:
      description: |-
        Returns all interim payments (partial payments, advance payments, prepayments) for the specified project.

        **What are Interim Payments?**
        Interim payments are payments made before the final project invoice is completed. They help maintain cash flow during long-running projects, split large invoices into manageable parts, and track incoming payments according to agreed payment schedules.

        **Typical use cases:**
        - Contractually agreed down payment at project start
        - Installment after milestone approval
        - Security payment before development start

        **What is returned:**
        - All interim payments for the project, sorted by their sort order
        - Each payment includes: title, description (as HTML), amount, billing status, creation date, and sort order
        - The description field is automatically converted from internal IDoc format to HTML for API responses

        **Important:**
        - Only non-deleted payments are returned
        - Payments marked as billed (isBilled: true) are still included in the list
        - Requires Projects.seeAnyProject permission
      operationId: ProjectInterimPaymentsController_listInterimPayments
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Interim payments retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectInterimPaymentResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project interim payments
      tags:
        - projects
        - project-settings
        - billing
        - interim-payments
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new interim payment (partial payment, advance payment, prepayment) in the specified project.

        **How it works:**
        - The new payment is added to the project with the provided title, description, and amount
        - Sort order is automatically set to the maximum existing sort order plus 1 (appears at the end of the list)
        - The payment is initially marked as not billed (isBilled: false)
        - Description can be provided as HTML or Markdown and is automatically converted to internal IDoc format

        **Required fields:**
        - **title**: Short label for the payment (e.g., "Prepayment", "50% Deposit", "Milestone 1")
        - **amount**: Payment amount (must be positive, minimum 0.01)

        **Optional fields:**
        - **description**: Detailed notes about the payment (e.g., "Client paid €1,000 upfront"). Can be provided as HTML or Markdown format

        **What happens after creation:**
        - The payment becomes available for billing in the invoicing system
        - When used in an invoice, it is automatically deducted from the final invoice amount
        - The payment can be reordered using the sort endpoint

        **Validation:**
        - Amount must be a positive number (minimum 0.01)
        - Title cannot be empty
        - Project must exist and be accessible

        **Permissions:** Requires Projects.edit permission
      operationId: ProjectInterimPaymentsController_createInterimPayment
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectInterimPaymentDto'
      responses:
        '201':
          description: Interim payment created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create interim payment
      tags:
        - projects
        - project-settings
        - billing
        - interim-payments
      x-required-permissions:
        - projects.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/interim-payments/{id}:
    patch:
      description: |-
        Updates only the provided fields of an existing interim payment. Fields not included in the request remain unchanged.

        **How partial updates work:**
        - Only fields included in the request body are updated
        - Omitted fields retain their current values
        - Description can be updated with new HTML or Markdown content
        - Sort order is not changed (use the sort endpoint to reorder payments)

        **Updatable fields:**
        - **title**: Update the payment label
        - **description**: Update payment notes (HTML or Markdown format)
        - **amount**: Change the payment amount (must be positive, minimum 0.01)

        **Important restrictions:**
        - Cannot update the isBilled status (this is managed automatically by the invoicing system)
        - Cannot change the project ID (payment is tied to the project)
        - Cannot update creation date or user ID

        **Validation:**
        - If amount is provided, it must be a positive number (minimum 0.01)
        - If title is provided, it cannot be empty
        - Payment must exist and belong to the specified project

        **Permissions:** Requires Projects.edit permission
      operationId: ProjectInterimPaymentsController_patchInterimPayment
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectInterimPaymentDto'
      responses:
        '200':
          description: Interim payment updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Interim payment not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update interim payment
      tags:
        - projects
        - project-settings
        - billing
        - interim-payments
      x-required-permissions:
        - projects.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft-deletes an interim payment by marking it as deleted. The payment is not permanently removed from the database.

        **How deletion works:**
        - The payment is marked with a deletedAt timestamp
        - Deleted payments no longer appear in list queries
        - The payment is not permanently removed (soft delete)
        - Historical data and billing records remain intact

        **Important considerations:**
        - If the payment has already been billed (isBilled: true), deletion may affect invoice calculations
        - Consider the impact on invoicing before deleting billed payments
        - Deleted payments can be filtered out from API responses

        **Permissions:** Requires Projects.edit permission
      operationId: ProjectInterimPaymentsController_deleteInterimPayment
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Interim payment deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Interim payment not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete interim payment
      tags:
        - projects
        - project-settings
        - billing
        - interim-payments
      x-required-permissions:
        - projects.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/interim-payments/sort:
    post:
      description: |-
        Updates the sort order of interim payments by providing a new ordered list of payment IDs.

        **How reordering works:**
        - Provide an array of payment IDs in the desired display order
        - The first ID in the array gets sort order 0, the second gets 1, and so on
        - Payments not included in the array retain their current sort order
        - This affects the order in which payments appear in list responses

        **Example:**
        To display payments in order: Payment A, Payment C, Payment B
        Send: { "paymentIds": ["id-of-A", "id-of-C", "id-of-B"] }

        **Validation:**
        - All payment IDs must be valid UUIDs
        - All payment IDs must belong to the specified project
        - Array cannot be empty

        **Use cases:**
        - Organize payments chronologically (e.g., first payment, second payment, final payment)
        - Group related payments together
        - Prioritize certain payments in the display

        **Permissions:** Requires Projects.edit permission
      operationId: ProjectInterimPaymentsController_sortInterimPayments
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortInterimPaymentsDto'
      responses:
        '200':
          description: Interim payments reordered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder interim payments
      tags:
        - projects
        - project-settings
        - billing
        - interim-payments
      x-required-permissions:
        - projects.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/manual-positions:
    get:
      description: |-
        **What are Manual Positions?**
        Manual positions are extra costs that do not come from the product catalog. They are used to bill for external costs, third-party services, flat fees, and other expenses that fall outside the automatic system logic. Typical examples include printing costs, translation services, license fees, travel expenses, or one-time flat rates.

        Manual positions are part of the project planning process and will appear in project quotes and invoices. They can be categorized, discounted, and reordered as needed.

        **What this endpoint returns:**
        - All active manual positions for the specified project
        - Positions are sorted by their sort order (ascending)
        - Only current positions are returned (snapshot positions are excluded)
        - Descriptions are returned as HTML (converted from internal IDoc format)

        **Permission requirements:**
        - Requires read access to the project
        - Requires api:read scope

        **Use cases:**
        - Display manual positions in project planning interface
        - Show billable items that will appear in quotes and invoices
        - Review project cost breakdown before creating offers
      operationId: ProjectManualPositionsController_listManualPositions
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ManualPositionResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all manual positions for a project
      tags:
        - projects
      x-required-scopes:
        - api:read
    post:
      description: |-
        **What this endpoint does:**
        Creates a new manual position (billable item) for a project. Manual positions represent extra costs that are not automatically tracked by the system, such as external services, third-party expenses, or one-time fees.

        **How it works:**
        - The position is added to the project's current planning
        - Sort order is automatically assigned (new positions are added at the end)
        - Description can be provided as HTML or Markdown (automatically converted to internal format)
        - The position can optionally be assigned to a category for better organization
        - Once created, the position will appear in project quotes and invoices

        **Description format:**
        - Accepts HTML or Markdown format
        - The system automatically detects the format and converts it to internal IDoc format
        - Empty descriptions are allowed (will be stored as empty IDoc)
        - In responses, descriptions are always returned as HTML

        **Validation rules:**
        - Name is required and must not be empty
        - Price must be a number greater than or equal to 0
        - Category ID must be a valid UUID if provided
        - Project must exist and user must have access

        **Permission requirements:**
        - Requires Projects.manageManualPositions permission
        - Requires api:write scope
        - Requires read access to the project

        **Use cases:**
        - Add external costs like printing or translation services
        - Record one-time fees that are not in the product catalog
        - Include retroactive charges or corrections to existing offers
        - Add services that happened outside the operational project structure
      operationId: ProjectManualPositionsController_createManualPosition
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,price
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateManualPositionDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManualPositionResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new manual position
      tags:
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - price
        allowedFields:
          - id
          - name
          - description
          - price
          - categoryId
          - sort
          - createdAt
          - discountAmount
          - discountType
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageManualPositions
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/manual-positions/{id}:
    patch:
      description: |-
        **What this endpoint does:**
        Updates an existing manual position. Supports partial updates - you only need to provide the fields you want to change.

        **How partial updates work:**
        - Only provided fields are updated
        - Fields not included in the request remain unchanged
        - To remove a category, set categoryId to null
        - Description can be updated with new HTML or Markdown content

        **Description updates:**
        - If description is provided, it will be converted from HTML/Markdown to internal IDoc format
        - If description is not provided, the existing description remains unchanged
        - Empty descriptions are allowed

        **Validation rules:**
        - Position must exist and belong to the specified project
        - Name must not be empty if provided
        - Price must be greater than or equal to 0 if provided
        - Category ID must be a valid UUID if provided (or null to remove)
        - Project must exist and user must have access

        **Permission requirements:**
        - Requires Projects.manageManualPositions permission
        - Requires api:write scope
        - Requires read access to the project

        **Use cases:**
        - Correct pricing or descriptions
        - Update position details before creating quotes
        - Reassign positions to different categories
        - Modify positions based on client feedback
      operationId: ProjectManualPositionsController_updateManualPosition
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,price
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchManualPositionDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManualPositionResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an existing manual position
      tags:
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - price
        allowedFields:
          - id
          - name
          - description
          - price
          - categoryId
          - sort
          - createdAt
          - discountAmount
          - discountType
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageManualPositions
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What this endpoint does:**
        Soft deletes a manual position by setting the deletedAt timestamp. The position is not permanently removed from the database but is hidden from normal queries and will not appear in project quotes or invoices.

        **How soft deletion works:**
        - The position is marked as deleted with a timestamp
        - It remains in the database for historical reference
        - Deleted positions are excluded from list queries
        - The position can be referenced in historical project snapshots

        **Validation rules:**
        - Position must exist and belong to the specified project
        - Project must exist and user must have access

        **Permission requirements:**
        - Requires Projects.manageManualPositions permission
        - Requires api:write scope
        - Requires read access to the project

        **Use cases:**
        - Remove positions that are no longer needed
        - Clean up project planning before finalizing quotes
        - Remove incorrectly added positions
      operationId: ProjectManualPositionsController_deleteManualPosition
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a manual position
      tags:
        - projects
      x-required-permissions:
        - projects.manageManualPositions
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/manual-positions/sort:
    post:
      description: |-
        **What this endpoint does:**
        Updates the sort order of manual positions within a project. The order determines how positions appear in project planning views, quotes, and invoices.

        **How reordering works:**
        - Provide an array of position IDs in the desired display order
        - The first ID in the array will have sort order 0, the second will have sort order 1, and so on
        - All positions must belong to the specified project
        - Positions not included in the array will maintain their current sort order (but may appear after the reordered positions)

        **Validation rules:**
        - All position IDs must be valid UUIDs
        - All positions must belong to the specified project
        - Project must exist and user must have access
        - Duplicate IDs are not allowed

        **Permission requirements:**
        - Requires Projects.manageManualPositions permission
        - Requires api:write scope
        - Requires read access to the project

        **Use cases:**
        - Organize positions by importance or category
        - Group related positions together
        - Control the order in which positions appear in quotes and invoices
        - Improve readability of project planning overview
      operationId: ProjectManualPositionsController_sortManualPositions
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortManualPositionsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder manual positions
      tags:
        - projects
      x-required-permissions:
        - projects.manageManualPositions
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/journal:
    get:
      description: |-
        Retrieves a paginated grid of project journal entries with filtering and sorting capabilities. Filterable fields: id, projectId, createdAt, createdBy, mood, reminder. Sortable fields: id, projectId, createdAt, createdBy, mood, reminder.

        **What are Project Journals?**
        Project journals are chronological records of important project events, observations, feedback, risks, and internal notes. They act as a timeline-based memory for projects, helping stakeholders track developments, assess communication, and prevent knowledge loss.

        **What is returned:**
        - Journal entry identification (ID, projectId)
        - Content (body as HTML, mood indicator)
        - Metadata (createdAt, createdBy, lastUpdated)
        - Optional reminder date for follow-ups

        **Journal Entry Features:**
        - **Mood indicators**: Sad (negative reactions, problems), Neutral (factual observations), Happy (positive feedback, wins)
        - **Rich text content**: Supports formatted text with paragraphs, highlighting, and structuring
        - **Reminder function**: Optional date-based reminders for follow-ups or review points

        **Filtering and search:**
        - Filter by projectId to view entries for a specific project
        - Filter by mood, creation date, creator, or reminder date
        - Sort by any sortable field (default: createdAt descending)
        - Server-side pagination for large journal lists

        **Permissions:**
        Automatically filters journal entries based on user permissions. Users without Projects.seeAnyProject permission will only see journal entries for projects they have access to.
      operationId: ProjectsJournalController_listProjectJournals
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Journal entry ID
            - **projectId** (string): Project ID
            - **createdAt** (date): Journal entry creation timestamp
            - **createdBy** (string): Creator user ID
            - **mood** (set): Journal entry mood
            - **reminder** (date): Reminder date

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Journal entry ID
            - **projectId**: Project ID
            - **createdAt**: Journal entry creation timestamp
            - **createdBy**: Creator user ID
            - **mood**: Journal entry mood
            - **reminder**: Reminder date

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, projectId, createdAt, createdBy, mood, reminder, body
          schema:
            example: id,projectId
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of project journal entries
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project journal entries
      tags:
        - projects
        - journal
        - projects
      x-required-permissions:
        - projects.viewProjectJournals
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new project journal entry to document important project events, observations, feedback, or notes.

        **What to include:**
        - **projectId** (required): The UUID of the project this entry belongs to
        - **body** (required): The journal entry content in HTML or Markdown format. Supports rich text formatting including paragraphs, headings, highlighting, and lists
        - **mood** (optional): Mood indicator - Sad (problems/escalations), Neutral (factual), or Happy (wins/progress). Defaults to Neutral if not provided
        - **reminder** (optional): ISO 8601 date string for setting a reminder about this entry

        **Common use cases:**
        - Documenting customer feedback or complaints
        - Recording internal findings and observations
        - Capturing important conversations or decisions
        - Noting risks or opportunities
        - Tracking project mood and sentiment over time

        **Validation:**
        - The projectId must exist and be accessible to the authenticated user
        - Body content cannot be empty
        - Reminder date must be a valid ISO 8601 date string

        **What is returned:**
        The complete created journal entry with all fields populated, including the body converted to HTML format.
      operationId: ProjectsJournalController_createProjectJournal
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,projectId,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectJournalResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      projectId:
                        - Project ID is required
                      body:
                        - Body content is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project journal entry
      tags:
        - projects
        - journal
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - projectId
          - createdAt
        allowedFields:
          - id
          - projectId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.updateProjectJournals
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/journal/{id}:
    get:
      description: |-
        Returns complete details for a specific project journal entry, including the full formatted content.

        **What is returned:**
        - Journal entry identification (ID, projectId)
        - Complete body content converted to HTML format with all formatting preserved
        - Mood indicator (Sad, Neutral, or Happy)
        - Creation metadata (createdAt, createdBy)
        - Last update timestamp (lastUpdated)
        - Optional reminder date (null if not set)

        **Use cases:**
        - Displaying a full journal entry in detail view
        - Editing an existing entry (use this to get current values before updating)
        - Retrieving formatted content for display in external systems

        **Note:** Returns 404 if the journal entry does not exist, has been deleted, or is not a project journal entry.
      operationId: ProjectsJournalController_getProjectJournalDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectJournalResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project journal entry details
      tags:
        - projects
        - journal
        - projects
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates one or more fields of an existing project journal entry. Only provided fields will be updated; all other fields remain unchanged.

        **What can be updated:**
        - **projectId**: Move the entry to a different project (must be accessible to user)
        - **body**: Update the journal entry content (HTML or Markdown format)
        - **mood**: Change the mood indicator (Sad, Neutral, or Happy)
        - **reminder**: Update or set a reminder date (ISO 8601 format). Set to null to clear an existing reminder

        **Update behavior:**
        - All fields are optional - only include fields you want to change
        - If a field is not provided, its current value is preserved
        - For reminder: provide a date string to set/update, or null to clear
        - The lastUpdated timestamp is automatically updated when any field changes

        **Validation:**
        - If projectId is provided, it must exist and be accessible to the authenticated user
        - Body content cannot be empty if provided
        - Reminder date must be a valid ISO 8601 date string if provided

        **What is returned:**
        The complete updated journal entry with all fields, including the body converted to HTML format.
      operationId: ProjectsJournalController_patchProjectJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,projectId,lastUpdated
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectJournalResponseDto'
        '400':
          description: Validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update project journal entry
      tags:
        - projects
        - journal
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - projectId
          - lastUpdated
        allowedFields:
          - id
          - projectId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a project journal entry. The entry is marked as deleted and will no longer appear in list queries, but it is not permanently removed from the database.

        **Deletion behavior:**
        - The entry is marked with a deletedAt timestamp
        - Deleted entries do not appear in the list endpoint results
        - The entry data remains in the database for audit purposes
        - This operation cannot be undone through the API

        **Note:** Returns 404 if the journal entry does not exist, has already been deleted, or is not a project journal entry.
      operationId: ProjectsJournalController_deleteProjectJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project journal entry
      tags:
        - projects
        - journal
        - projects
      x-required-scopes:
        - api:write
  /projects/uploads:
    get:
      description: |-
        Retrieves a paginated grid of project uploads with filtering and sorting capabilities. Quick search available on: fileName, description. Filterable fields: id, projectId, fileId, fileName, description, categoryId, mimeType, fileSize, userId, updatedAt. Sortable fields: id, projectId, fileId, fileName, description, categoryId, mimeType, fileSize, userId, updatedAt.

        **What are Project Uploads?**
        Project uploads allow you to centrally manage project-related files within Leadtime. This feature provides organized storage for documents like contracts, offers, technical specifications, or internal notes directly within project context, eliminating the need to search through external folders.

        **What is returned:**
        - Upload identification (ID, project ID, file ID)
        - File information (filename, MIME type, file size in bytes)
        - Organization details (description, category ID)
        - User information (user ID who created or last updated the upload)
        - Timestamps (creation and last update dates)

        **Filtering and search:**
        - Quick search across file name and description fields
        - Advanced filtering by project ID, category, file type, user, and more
        - Sorting by any sortable field (default: updatedAt descending)
        - Server-side pagination for large upload lists

        **Access control:**
        This endpoint automatically filters uploads based on project access permissions:
        - Users with Projects.seeAnyProject permission see all uploads across all projects
        - Other users see only uploads from projects they have access to
        - Optional filtering by projectId via grid filters allows focusing on specific projects

        **Note:** This endpoint requires the Projects.manageUploads permission.
      operationId: ProjectUploadsController_listProjectUploads
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: fileName, description'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Upload ID
            - **projectId** (set): Project ID
            - **fileId** (string): File ID
            - **fileName** (string): File name
            - **description** (string): Upload description
            - **categoryId** (set): Category ID
            - **mimeType** (string): MIME type
            - **fileSize** (number): File size in bytes
            - **userId** (set): User ID who created the upload
            - **updatedAt** (date): Last update timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Upload ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **fileId** (string): File ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **fileName** (string): File name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **description** (string): Upload description (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **categoryId** (set): Category ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **mimeType** (string): MIME type (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **fileSize** (number): File size in bytes (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **userId** (set): User ID who created the upload (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **updatedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Upload ID
            - **projectId**: Project ID
            - **fileId**: File ID
            - **fileName**: File name
            - **description**: Upload description
            - **categoryId**: Category ID
            - **mimeType**: MIME type
            - **fileSize**: File size in bytes
            - **userId**: User ID who created the upload
            - **updatedAt**: Last update timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** updatedAt (desc)
          schema:
            example: '[{"field":"updatedAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, projectId, fileId, fileName, description, categoryId, mimeType, fileSize, userId, updatedAt
          schema:
            example: id,projectId
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of project uploads
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project uploads
      tags:
        - projects
        - uploads
        - projects
      x-required-permissions:
        - projects.manageUploads
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new project upload record, linking an uploaded file to a project.

        **How to upload files:**
        1. First, upload the file using POST /workspace/upload to obtain a fileId
        2. Then use that fileId in this endpoint to create the project upload record

        **Required fields:**
        - projectId: The project to attach the upload to (must exist and user must have access)
        - fileId: The file ID from the workspace upload endpoint (must exist in workspace)

        **Optional fields:**
        - description: Free text field for classifying the content (e.g., "Signed contract 10/28/2025", "Kickoff protocol")
        - categoryId: Thematic classification for organizing uploads (e.g., Contract, Offer, Documentation)

        **Validation:**
        - Project must exist and belong to the workspace
        - User must have access to the project
        - File must exist and belong to the workspace
        - Category must exist and belong to the workspace (if provided)

        **What is returned:**
        Complete upload details including file metadata, timestamps, and user information.

        **Note:** This endpoint requires the Projects.manageUploads permission.
      operationId: ProjectUploadsController_createProjectUpload
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,projectId,fileId,fileName
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectUploadDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectUploadResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      projectId:
                        - Project ID is required
                      fileId:
                        - File ID is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project upload
      tags:
        - projects
        - uploads
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - projectId
          - fileId
          - fileName
        allowedFields:
          - id
          - projectId
          - fileId
          - description
          - categoryId
          - fileName
          - mimeType
          - size
          - createdAt
          - createdBy
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageUploads
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/uploads/{id}:
    get:
      description: |-
        Retrieves complete details for a specific project upload, including all file metadata and organization information.

        **What is returned:**
        - Upload identification (ID, project ID, file ID)
        - File metadata (filename, MIME type, file size in bytes)
        - Organization details (description, category ID)
        - User information (user ID who created the upload)
        - Timestamps (creation and last update dates in ISO 8601 format)

        **Note:** This endpoint requires the Projects.manageUploads permission. Only uploads from projects the user has access to can be retrieved.
      operationId: ProjectUploadsController_getProjectUploadDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectUploadResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project upload details
      tags:
        - projects
        - uploads
        - projects
      x-required-permissions:
        - projects.manageUploads
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates specific fields of an existing project upload. Only provided fields are updated; all fields are optional.

        **What can be updated:**
        - projectId: Change which project the upload belongs to (must exist and user must have access)
        - description: Update the description text (set to null to clear)
        - categoryId: Change or remove the category (set to null to remove category assignment)

        **Note:** The file itself cannot be changed. To replace a file, delete the upload and create a new one.

        **Validation:**
        - Upload must exist and belong to the workspace
        - If projectId is provided, project must exist and user must have access
        - If categoryId is provided, category must exist and belong to the workspace

        **What is returned:**
        Complete updated upload details including all current field values.

        **Note:** This endpoint requires the Projects.manageUploads permission.
      operationId: ProjectUploadsController_patchProjectUpload
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,projectId,fileId,fileName
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectUploadDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectUploadResponseDto'
        '400':
          description: Validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update project upload
      tags:
        - projects
        - uploads
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - projectId
          - fileId
          - fileName
        allowedFields:
          - id
          - projectId
          - fileId
          - description
          - categoryId
          - fileName
          - mimeType
          - size
          - createdAt
          - createdBy
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageUploads
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Removes a project upload from the project. This performs a soft delete, meaning the upload is marked as deleted but not permanently removed from the database. The file itself remains in the workspace file storage.

        **What happens:**
        - The upload record is marked as deleted (deletedAt timestamp is set)
        - The upload no longer appears in project upload lists
        - The underlying file remains in workspace storage

        **Validation:**
        - Upload must exist and belong to the workspace
        - User must have access to the project containing the upload

        **Note:** This endpoint requires the Projects.manageUploads permission.
      operationId: ProjectUploadsController_deleteProjectUpload
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project upload
      tags:
        - projects
        - uploads
        - projects
      x-required-permissions:
        - projects.manageUploads
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{id}/document-settings:
    get:
      description: |-
        Returns document settings for a specific project including current values and inherited defaults from organization/workspace hierarchy.

        **What are Project Document Settings?**
        Project document settings control formatting options and contact information for automatically generated documents such as proposals, contracts, requirement specifications, and other project documents. These settings can override organization/workspace defaults on a per-project basis, allowing you to customize document appearance and structure for specific projects.

        **Settings Overview:**
        - **Title Page**: Controls whether generated documents include a title page
        - **Table of Contents**: Determines if an automatically generated table of contents is embedded (especially helpful for long documents)
        - **Heading Style**: Controls the numbering and formatting of headings (Normal, Sequential, SequentialFromSecondLevel, SequentialWithParagraphs)
        - **Default Contact Person**: Sets the contact person automatically when creating new documents (useful for recurring offer processes and automated preambles)

        **Inheritance System:**
        Settings support a three-level inheritance hierarchy:
        1. **Project-level settings**: Project-specific overrides (null = inherit from organization)
        2. **Organization-level defaults**: Organization-specific defaults (null = inherit from workspace)
        3. **Workspace-level defaults**: Global workspace defaults

        The response includes both current project values and the inherited defaults from the organization/workspace hierarchy. Null values in current settings indicate that the project inherits from organization/workspace defaults. The "*Default" fields show what values are inherited when project settings are null.
      operationId: ProjectDocumentSettingsController_getDocumentSettings
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetProjectDocumentSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project document settings
      tags:
        - projects
        - project-settings
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Partially updates document settings for a specific project. All fields are optional - only provided fields will be updated.

        **What are Project Document Settings?**
        Project document settings control formatting options and contact information for automatically generated documents such as proposals, contracts, requirement specifications, and other project documents. These settings can override organization/workspace defaults on a per-project basis.

        **Settings You Can Update:**
        - **projectDocumentTitlePage**: Enable/disable title page for generated documents (null = use organization standard)
        - **projectDocumentEnableToc**: Enable/disable table of contents in generated documents (null = use organization standard)
        - **projectDocumentHeadingStyle**: Set heading numbering style (null = use organization standard)
          - Normal: No numbering
          - Sequential: Numbering from level 1
          - SequentialFromSecondLevel: Numbering from level 2
          - SequentialWithParagraphs: Numbering with symbol prefix (e.g., §)
        - **projectDocumentDefaultContactUserId**: Set default contact person UUID for new documents (null = use organization standard or no default)

        **Inheritance Behavior:**
        Setting any field to null enables inheritance from organization/workspace defaults. This allows you to override organization settings for specific projects or revert to using organization standards.

        **Use Cases:**
        - Customize document formatting for specific project types
        - Set project-specific contact persons for recurring offer processes
        - Override organization defaults when needed
        - Revert to organization standards by setting fields to null
      operationId: ProjectDocumentSettingsController_updateDocumentSettings
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectDocumentSettingsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project document settings
      tags:
        - projects
        - project-settings
      x-required-permissions:
        - projects.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/subscription-billings:
    get:
      description: |-
        **What are Project Subscription Billings?**
        Project Subscription Billings allow you to manage recurring fees and charges for projects. This feature is ideal for SaaS subscriptions, maintenance contracts, support packages, or any recurring billing arrangement. The system automatically generates billing rows based on your date range and frequency settings, which can then be included in invoices.

        **What data is returned:**
        - All active subscription billing items for the specified project
        - Items are sorted by their sort order (display order)
        - Each item includes date range, payment type, price, frequency, and configuration details

        **Payment types supported:**
        - **fixed**: Fixed monthly fee (e.g., $500/month support package)
        - **perUnit**: Variable pricing based on units (e.g., per user, per license)
        - **oneTime**: One-time charge within the subscription period

        **Use cases:**
        - Retrieve all recurring billing items for a project
        - Display subscription information in project dashboards
        - Generate reports on recurring revenue
        - Audit subscription configurations
      operationId: ProjectSubscriptionBillingsController_listSubscriptionBillings
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Subscription billings retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectSubscriptionBillingResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found or access denied
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List project subscription billings
      tags:
        - projects
        - project-settings
        - billing
        - subscriptions
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        **What this endpoint does:**
        Creates a new recurring subscription billing item for a project. The system automatically generates billing rows for each billing period based on your date range and frequency settings. These rows can later be included in invoices.

        **How it works:**
        1. Specify the date range (fromDate to toDate) for when the subscription is active
        2. Set the frequency (1-12 months) to determine billing intervals:
           - 1 = monthly billing
           - 3 = quarterly billing
           - 6 = semi-annual billing
           - 12 = yearly billing
        3. Choose the payment type:
           - **fixed**: Fixed amount per period (e.g., $500/month)
           - **perUnit**: Variable pricing (e.g., $50 per user per month)
           - **oneTime**: One-time charge (requires quantity field)
        4. The system automatically:
           - Calculates sort order (display position)
           - Generates billing rows for each period within the date range
           - Creates date keys for invoice matching

        **Validation rules:**
        - fromDate must be before or equal to toDate
        - Price must be positive
        - Frequency must be between 1 and 12 months
        - Quantity is required for oneTime payment type

        **Permissions required:**
        - Projects.edit: Ability to edit project settings
        - Invoices.manage: Ability to manage invoice configurations

        **Example use cases:**
        - Set up monthly support package subscription
        - Configure quarterly maintenance contract
        - Add yearly license renewal
        - Create variable pricing based on usage
      operationId: ProjectSubscriptionBillingsController_createSubscriptionBilling
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectSubscriptionBillingDto'
      responses:
        '201':
          description: Subscription billing created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid date range, frequency, or other validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions (requires Projects.edit and Invoices.manage)
        '404':
          description: Project not found or access denied
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project subscription billing
      tags:
        - projects
        - project-settings
        - billing
        - subscriptions
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/subscription-billings/{id}:
    patch:
      description: |-
        **What this endpoint does:**
        Partially updates an existing subscription billing item. You can update any combination of fields without providing all values. The system automatically regenerates billing rows if the date range or frequency changes.

        **How updates work:**
        - Only provide the fields you want to change (all fields are optional)
        - If you update fromDate, toDate, or frequency, the system will:
          - Delete unbilled billing rows
          - Regenerate billing rows based on new date range and frequency
          - Preserve already billed rows (they are not affected)
        - Other fields (price, title, payment type) update immediately without regenerating rows

        **Important notes:**
        - If updating both fromDate and toDate, ensure fromDate is before or equal to toDate
        - Changing frequency will affect all future billing periods
        - Already billed rows remain unchanged
        - Sort order is preserved unless explicitly changed

        **Permissions required:**
        - Projects.edit: Ability to edit project settings
        - Invoices.manage: Ability to manage invoice configurations

        **Example use cases:**
        - Extend subscription end date
        - Change billing frequency (e.g., from monthly to quarterly)
        - Update subscription price
        - Modify subscription title or description
        - Change payment type
      operationId: ProjectSubscriptionBillingsController_updateSubscriptionBilling
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectSubscriptionBillingDto'
      responses:
        '200':
          description: Subscription billing updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid date range, frequency, or other validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions (requires Projects.edit and Invoices.manage)
        '404':
          description: Project or subscription billing not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update project subscription billing
      tags:
        - projects
        - project-settings
        - billing
        - subscriptions
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What this endpoint does:**
        Soft deletes a subscription billing item. The item is marked as deleted and will no longer appear in listings or be included in future invoice generation. However, the data is retained in the database for historical records and audit purposes.

        **What happens when deleted:**
        - The subscription billing item is marked with a deletedAt timestamp
        - It disappears from all active listings and queries
        - Already billed rows remain unchanged (historical invoices are not affected)
        - Unbilled billing rows are effectively cancelled
        - The item can be restored if needed (though not through this API)

        **Permissions required:**
        - Projects.edit: Ability to edit project settings
        - Invoices.manage: Ability to manage invoice configurations

        **Use cases:**
        - Cancel an active subscription
        - Remove a subscription that is no longer needed
        - Clean up test or incorrect subscription entries
      operationId: ProjectSubscriptionBillingsController_deleteSubscriptionBilling
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Subscription billing deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions (requires Projects.edit and Invoices.manage)
        '404':
          description: Project or subscription billing not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project subscription billing
      tags:
        - projects
        - project-settings
        - billing
        - subscriptions
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/subscription-billings/products-from-snapshot:
    get:
      description: |-
        **What this endpoint does:**
        Returns products from the current billing snapshot for Single type projects. These products represent what will be used for invoice generation and show how invoice rows will be formed.

        **What data is returned:**
        - Products from the billing snapshot (productsBillingProjectSnapshotId or billingProjectSnapshotId)
        - Only for Single type projects
        - Products are sorted by their sort order
        - Includes product details: name, prices, quantities, active dates, etc.

        **Use cases:**
        - Display products that will be used for billing
        - Preview how invoice rows will be formed
        - Show read-only product information for subscription billing
      operationId: ProjectSubscriptionBillingsController_getProductsFromBillingSnapshot
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Products retrieved successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found or access denied
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get products from billing snapshot
      tags:
        - projects
        - project-settings
        - billing
        - subscriptions
      x-required-permissions:
        - projects.seeAnyProject
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /projects/{projectId}/products:
    get:
      description: |-
        Returns an array of product summary objects for a project. Products are sorted by sort field (ascending) and only non-deleted products are returned.

        Products in Leadtime are standardized services or items from the product catalog that have been imported into a project. They can represent one-time purchases, subscriptions, or quantity-based services. Each product includes pricing information, quantity, active date ranges, and calculated final prices based on selected variants and options.

        The response includes:
        - Product identification (id, name, finalName)
        - Category and logo information
        - Quantity and pricing (fixed, subscription, per-unit)
        - Final calculated prices (includes variant and option adjustments)
        - Active date range (activeFrom, activeTo, isActive flag)
      operationId: ProjectProductsController_listProducts
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Products retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectProductResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no project access
        '404':
          description: Not Found - Project does not exist
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all products in a project
      tags:
        - projects
        - products
      x-required-scopes:
        - api:read
    post:
      description: |-
        Imports a standardized product from the product catalog into a project. The product is copied into the project, allowing project-specific configuration without affecting the original catalog entry.

        **How it works:**
        1. Select a product from the catalog by providing its productId
        2. Set the quantity for this product in the project
        3. Optionally select an active variant (e.g., Standard, Pro, Enterprise)
        4. Select any required options and optional add-ons

        **Product Isolation:**
        When imported, the product becomes a copy within the project. Any subsequent changes to the product in this project do not affect the original catalog entry. This ensures historical accuracy for quotes and invoices.

        **Pricing:**
        The final price is calculated based on:
        - Base product pricing (fixed, subscription, or per-unit)
        - Selected variant pricing adjustments
        - Selected option value extra pricing
        - Quantity multiplier

        **Required fields:**
        - productId: UUID of the catalog product to import
        - quantity: Minimum 1
        - options: Array of option selections (can be empty array)

        **Optional fields:**
        - activeVariantId: UUID of the variant to activate

        Returns the ID of the newly created product in the project.
      operationId: ProjectProductsController_importProductToProject
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImportProductToProjectDto'
      responses:
        '201':
          description: Product imported successfully
          schema:
            example:
              success: true
              id: 550e8400-e29b-41d4-a716-446655440000
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Bad Request - Invalid input or validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no project access
        '404':
          description: Not Found - Catalog product or project does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Import product from catalog
      tags:
        - projects
        - products
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/products/{productId}:
    get:
      description: |-
        Returns full product details including variants, options, and HTML-formatted description.

        This endpoint provides complete information about a product in a project, including:

        **Product Information:**
        - Basic details (name, description as HTML, category, logo)
        - Pricing structure (fixed price, subscription price, per-unit price)
        - Price calculation details (unit name, frequency, final calculated prices)
        - Quantity and active date ranges

        **Variants:**
        - All available product variants (e.g., Standard, Pro, Enterprise)
        - Each variant includes its own pricing and HTML-formatted description
        - Active variant status and sort order

        **Options:**
        - All available product options (add-ons or configuration choices)
        - Option values with extra pricing adjustments
        - Required/multiple selection settings

        The description and variant descriptions are returned as HTML, converted from the internal document format.
      operationId: ProjectProductsController_getProductDetails
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
        - name: productId
          required: true
          in: path
          description: Product ID
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Product details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectProductDetailsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no project access
        '404':
          description: Not Found - Product or project does not exist
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get product details
      tags:
        - projects
        - products
      x-required-scopes:
        - api:read
    delete:
      description: |-
        Soft deletes a product from a project by setting the deletedAt timestamp. The product is not permanently removed, but is hidden from product lists and excluded from calculations.

        **Soft Delete Behavior:**
        - Product is marked as deleted with a timestamp
        - Product no longer appears in list endpoints
        - Product is excluded from price calculations
        - Historical data is preserved for audit purposes

        **Note:** This only removes the product from the project. The original catalog product remains unchanged.
      operationId: ProjectProductsController_deleteProduct
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
        - name: productId
          required: true
          in: path
          description: Product ID
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Product deleted successfully
          schema:
            example:
              success: true
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no project access
        '404':
          description: Not Found - Product or project does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete product from project
      tags:
        - projects
        - products
      x-required-permissions:
        - projects.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/products/{productId}/settings:
    post:
      description: |-
        Updates only product configuration settings without modifying product details. This endpoint is designed for quick updates to how a product is used in a project, without changing the product itself.

        **What gets updated:**
        - quantity: How many units of this product are in the project
        - activeVariantId: Which variant is currently active (e.g., Standard vs Pro)
        - options: Which option values are selected (e.g., color, add-ons)

        **What does not get updated:**
        - Product name, description, category, or logo
        - Base pricing (priceFixed, priceSubscription, pricePerUnit)
        - Variant definitions or option definitions
        - Active date ranges

        **Use cases:**
        - Customer changes their mind about variant selection
        - Adjusting quantity after initial import
        - Adding or removing optional add-ons
        - Quick configuration changes without full product edit

        **Pricing Impact:**
        Changing quantity, variant, or options will affect the final calculated prices, but the base product pricing structure remains unchanged.
      operationId: ProjectProductsController_updateProductSettings
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
        - name: productId
          required: true
          in: path
          description: Product ID
          schema:
            format: uuid
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductSettingsDto'
      responses:
        '200':
          description: Product settings updated successfully
          schema:
            example:
              success: true
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Bad Request - Invalid input or validation error
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no project access
        '404':
          description: Not Found - Product or project does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update product settings
      tags:
        - projects
        - products
      x-required-permissions:
        - projects.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/products/{productId}/duplicate:
    post:
      description: |-
        Creates a copy of an existing product within the same project. Useful when you need the same product multiple times with different configurations or for different time periods.

        **What gets duplicated:**
        - All product details (name, description, category, logo)
        - All pricing information
        - All variants and their configurations
        - All options and option values
        - Current quantity and settings

        **Use cases:**
        - Same product with different active date ranges
        - Same product with different variant/option selections
        - Multiple instances of a product in one project

        Returns the ID of the newly created duplicate product.
      operationId: ProjectProductsController_duplicateProduct
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
        - name: productId
          required: true
          in: path
          description: Product ID to duplicate
          schema:
            format: uuid
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: success,id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '201':
          description: Product duplicated successfully
          schema:
            example:
              success: true
              id: 550e8400-e29b-41d4-a716-446655440001
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no project access
        '404':
          description: Not Found - Product or project does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Duplicate product
      tags:
        - projects
        - products
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - success
          - id
        allowedFields:
          - success
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{projectId}/products/sort:
    post:
      description: |-
        Updates the sort order for multiple products in a project. Products are displayed in the order specified by the provided array of product IDs.

        **How it works:**
        1. Provide an array of product IDs in the desired display order
        2. The first ID in the array will have the lowest sort value
        3. Subsequent IDs receive incrementally higher sort values
        4. Products not included in the array retain their current sort order

        **Requirements:**
        - All product IDs must belong to the specified project
        - All product IDs must be valid UUIDs
        - Array must contain at least one product ID

        **Use cases:**
        - Reorganizing product list for better presentation
        - Grouping related products together
        - Prioritizing important products at the top
      operationId: ProjectProductsController_reorderProducts
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            format: uuid
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductSortDto'
      responses:
        '200':
          description: Products reordered successfully
          schema:
            example:
              success: true
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Bad Request - Invalid input
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient permissions or no project access
        '404':
          description: Not Found - Project does not exist
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder products
      tags:
        - projects
        - products
      x-required-permissions:
        - projects.manageProducts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{id}/versions:
    get:
      description: |-
        Returns a list of all saved project versions (snapshots) with their details.

        **What are Project Versions?**
        Project versions freeze the current state of a project configuration at a specific point in time. They capture the complete project state including:
        - Project tree structure with dynamically activated work packages
        - Products including variants, prices, and discounts
        - Answered form questions and their effects
        - Manually added items
        - All configuration changes

        Versions ensure that offered, negotiated, and contracted content remains traceable. They serve as the basis for quotes, specifications, and billing documents.

        **What is returned:**
        - **id**: Unique version identifier (UUID)
        - **title**: Version title/name
        - **semverVersion**: Semantic version string (e.g., "1.2.3")
        - **branchId**: Branch identifier for version tracking
        - **parentId**: Parent version ID (null for first version)
        - **createdAt**: ISO 8601 timestamp when version was created
        - **createdBy**: User ID who created the version
        - **description**: HTML-formatted version description
        - **isCurrent**: Whether this is the current active version for the project

        **Use cases:**
        - View version history to see how project evolved
        - Select a version when creating quotes or specifications
        - Compare different project states
        - Restore to a previous version if needed
      operationId: ProjectVersionsController_listVersions
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Array of project versions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectVersionDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all versions for a project
      tags:
        - projects
        - versions
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new version (snapshot) of the current project configuration state.

        **What gets saved:**
        The version captures the complete project state at the time of creation:
        - Project tree structure with all components and work packages
        - Products including variants, prices, and discounts
        - Answered form questions and their effects
        - Manually added items
        - All configuration changes

        **How to use:**
        1. Make changes to your project configuration
        2. Call this endpoint with a descriptive title
        3. Optionally provide a description in HTML or Markdown format
        4. The version is created and becomes the current active version

        **Best practices:**
        - Create versions after filling out questionnaires or making structural changes
        - Create a version before sending out offers
        - Use clear, descriptive version titles
        - Create variants when customers want to compare alternatives

        **Required permissions:** Projects.manageSnapshots permission is required to create versions.
      operationId: ProjectVersionsController_createVersion
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,title,semverVersion
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectVersionDto'
      responses:
        '200':
          description: Created version with description as HTML
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectVersionDto'
        '400':
          description: Invalid input
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new project version
      tags:
        - projects
        - versions
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - title
          - semverVersion
        allowedFields:
          - id
          - title
          - semverVersion
          - branchId
          - parentId
          - createdAt
          - createdBy
          - description
          - isCurrent
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.manageSnapshots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{id}/versions/{versionId}:
    get:
      description: |-
        Returns complete details for a specific project version, including estimate data.

        **What is returned:**
        - All standard version fields (id, title, semverVersion, branchId, parentId, createdAt, createdBy, description, isCurrent)
        - **estimateData**: Complete estimate data including:
          - Products with variants, prices, and discounts
          - Components with nested structure
          - Manual positions
          - Pricing breakdowns (fixed, subscriptions, per-unit)

        **Use cases:**
        - View detailed version information
        - Access estimate data for a specific version
        - Compare estimate data between versions
        - Use version data for generating documents or reports
      operationId: ProjectVersionsController_getVersionDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: versionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Version details with estimate data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectVersionDetailsDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Version or project not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get version details
      tags:
        - projects
        - versions
      x-required-scopes:
        - api:read
  /projects/{id}/versions/{versionId}/restore:
    post:
      description: |-
        Restores the project configuration to the state of a previously saved version. This is a destructive operation that replaces the current project state.

        **What happens:**
        - The current project configuration is replaced with the selected version
        - All current components, products, and manual positions are removed
        - The project state is restored to exactly match the saved version
        - The restored version becomes the current active version

        **Warning:**
        This operation cannot be undone. All unsaved changes will be lost. Make sure to create a new version of the current state before restoring if you want to preserve it.

        **Use cases:**
        - Rollback to a previous version after unwanted changes
        - Restore a project variant for comparison
        - Revert to a stable configuration

        **Required permissions:** Projects.manageSnapshots permission is required to restore versions.
      operationId: ProjectVersionsController_restoreVersion
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: versionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Version restored successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Insufficient permissions
        '404':
          description: Version or project not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Restore project to a previous version
      tags:
        - projects
        - versions
      x-required-permissions:
        - projects.manageSnapshots
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{id}/configuration-state:
    get:
      description: |-
        Returns the current state of the project configuration, including version information and change tracking.

        **What is returned:**
        - **lastConfigurationSnapshot**: Information about the last saved version (id, version, createdAt, title)
        - **configurationChanged**: Whether the project configuration has changed since the last version was saved
        - **configurationChangedAt**: ISO 8601 timestamp when configuration last changed (null if unchanged)
        - **productCount**: Number of products in the project
        - **componentCount**: Number of components in the project
        - **manualPositionCount**: Number of manual positions in the project
        - **discountCount**: Number of discounts applied
        - **snapshotCount**: Total number of versions saved for the project
        - **documentCount**: Number of documents associated with the project

        **Use cases:**
        - Check if there are unsaved changes (configurationChanged flag)
        - Get overview of project contents
        - Determine if a new version should be created
        - View version history count
      operationId: ProjectConfigurationStateController_getConfigurationState
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Project configuration state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectConfigurationStateDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get current project configuration state
      tags:
        - projects
        - versions
      x-required-scopes:
        - api:read
  /projects:
    get:
      description: |-
        Returns a paginated, filterable list of projects visible to the authenticated user. Users with full project visibility see all projects; restricted users receive only projects they can access, or an empty list.

        **What are Projects?**
        Projects are the central organizational unit in Leadtime. Every task, time booking, and activity is associated with a project. Projects can be either internal (company activities) or external (client work), and can be structured as single projects (one-off initiatives) or ongoing projects (continuous collaboration).

        **Project Types:**
        - **Single Projects**: Clear-cut initiatives with defined start and finish, usually planned and billed as quoted
        - **Ongoing Projects**: Continuous activities where tasks are handled and billed based on effort

        **Project Classification:**
        - **External Projects**: Billable customer projects that generate direct revenue
        - **Internal Projects**: Non-billable activities essential for running the company (e.g., administration, product development)

        **Value Groups:**
        Projects are classified by value creation:
        - **A - Direct value creation**: External customer projects
        - **B - Indirect value creation**: Internal projects that increase product/service value
        - **C - Not value creating**: Administrative tasks
        - **D - Waste**: Unproductive time (downtime, failed projects)

        **What is returned:**
        The response includes project details such as name, short name (unique identifier), organization, category, status, phase, open tasks count, value group, type, tags, access permissions, and timestamps.

        Retrieves a paginated grid of projects with filtering and sorting capabilities. Quick search available on: name, shortName, orgName. Filterable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, deadline, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt. Sortable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, deadline, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt.
      operationId: ProjectsController_listProjects
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: name, shortName, orgName'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Project ID
            - **name** (string): Project name
            - **shortName** (string): Project short name (organization prefix + number)
            - **type** (set): Project type
            - **valueGroup** (set): Value group classification
            - **categoryId** (string): Project category ID
            - **statusId** (string): Project status ID
            - **phaseId** (string): Project phase ID
            - **deadline** (date): Project deadline date
            - **organizationId** (string): Organization ID
            - **orgName** (string): Organization name
            - **guestAccess** (boolean): Whether guest access is enabled
            - **isFavorite** (boolean): Whether the project is marked as favorite by the current user
            - **isArchived** (boolean): Whether the project is archived
            - **openTasksCount** (number): Number of open tasks
            - **createdAt** (date): Creation timestamp
            - **editedAt** (date): Last update timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **name** (string): Project name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **shortName** (string): Project short name (organization prefix + number) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Project type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **valueGroup** (set): Value group classification (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **categoryId** (string): Project category ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **statusId** (string): Project status ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **phaseId** (string): Project phase ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **deadline** (date): Project deadline date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **orgName** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **guestAccess** (boolean): Whether guest access is enabled (boolean filter). Available comparisons: equal, not_equal
            - **isFavorite** (boolean): Whether the project is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal
            - **isArchived** (boolean): Whether the project is archived (boolean filter). Available comparisons: equal, not_equal
            - **openTasksCount** (number): Number of open tasks (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Project ID
            - **name**: Project name
            - **shortName**: Project short name (organization prefix + number)
            - **type**: Project type
            - **valueGroup**: Value group classification
            - **categoryId**: Project category ID
            - **statusId**: Project status ID
            - **phaseId**: Project phase ID
            - **deadline**: Project deadline date
            - **organizationId**: Organization ID
            - **orgName**: Organization name
            - **guestAccess**: Whether guest access is enabled
            - **isFavorite**: Whether the project is marked as favorite by the current user
            - **isArchived**: Whether the project is archived
            - **openTasksCount**: Number of open tasks
            - **createdAt**: Creation timestamp
            - **editedAt**: Last update timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, name, shortName, icon, type, valueGroup, categoryId, statusId, phaseId, deadline, organizationId, orgName, orgColor, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt
          schema:
            example: id,name
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of projects
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List projects
      tags:
        - projects
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new project in the workspace.

        **Project Types:**
        - **Single Projects**: One-off initiatives with defined start and finish
        - **Ongoing Projects**: Continuous activities with recurring tasks

        **Internal vs External:**
        - **External Projects**: Set organizationId to link to a client organization. These are billable customer projects.
        - **Internal Projects**: Set organizationId to null. These are non-billable company activities.

        **Required Fields:**
        - name: Project name
        - type: Project type (Single or Ongoing)
        - valueGroup: Value classification (A-D)
        - categoryId: Project category UUID
        - statusId: Project status UUID
        - users: Array of user IDs (at least one)
        - taskTypes: Array of task type IDs (at least one)
        - activities: Array of activity IDs (at least one)

        **Reference Data:**
        - categoryId: use GET /projects/categories
        - statusId: use GET /projects/statuses
        - phaseId: use GET /projects/phases
        - organizationId: use GET /organizations
        - users/defaultAccountableId/responsibleId: use User.id from GET /workspace/users
        - teams: use GET /teams
        - taskTypes: use GET /tasks/types or GET /administration/task-settings/types
        - activities: use GET /administration/task-settings/activities
        - customFields: use configured project custom-field IDs from GET /administration/project-settings/custom-fields; values must match their definition types and do not create native scheduling, relationship, or billing behavior

        **Optional Fields:**
        - phaseId: Required for external single projects, optional otherwise
        - organizationId: Required for external projects, must be null for internal projects
        - description: HTML or Markdown (will be converted to internal format)
        - icon: Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:shopping_cart:`, `:mobile_phone:`). Use standard emoji short names or custom icon names.
        - deadline: ISO 8601 date string
        - defaultAccountableId: Default accountable user UUID
        - responsibleId: Responsible user UUID
        - teams: Array of team IDs
        - customFields: Key-value pairs keyed by configured project custom-field ID; values must match each definition type
        - tags: Array of tag strings
        - guestAccess: Whether organization members can access (only for external projects)

        **Description Format:**
        The description field accepts HTML or Markdown and will be automatically converted to the internal IDoc format. When retrieved later, it will be returned as HTML.
      operationId: ProjectsController_createProject
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,shortName,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      name:
                        - Name is required
                      type:
                        - Type is required
                      organizationId:
                        - organizationId is required for client projects
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create project
      tags:
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - shortName
          - name
        allowedFields:
          - id
          - shortName
          - name
          - type
          - projectType
          - organizationId
          - statusId
          - phaseId
          - categoryId
          - valueGroup
          - deadline
          - responsibleId
          - defaultAccountableId
          - guestAccess
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/categories:
    get:
      description: |-
        Returns a list of all project categories configured in the workspace.

        **What are Project Categories?**
        Categories classify projects by functional area or project type (e.g., Onboarding, Support, Implementation, Website Development). They help organize and filter projects in the workspace. Categories can be configured in workspace settings and support multi-language translations.

        **What is returned:**
        Each category includes:
        - Unique identifier (ID)
        - Name and description
        - Icon identifier
        - Multi-language translations (if configured)

        This is a read-only endpoint optimized for common list data retrieval.
      operationId: ProjectsController_listCategories
      parameters: []
      responses:
        '200':
          description: Project categories retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectCategoryDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all project categories
      tags:
        - projects
        - common-lists
      x-required-scopes:
        - api:read
  /projects/statuses:
    get:
      description: |-
        Returns a list of all project statuses configured in the workspace.

        **What are Project Statuses?**
        Statuses represent the current progress stage of a project (e.g., Sales, Implementation, Billing, Done). They help track project lifecycle and workflow. Statuses can be configured in workspace settings and support multi-language translations.

        **Status Types:**
        Each status has a type that determines its behavior in the project workflow.

        **What is returned:**
        Each status includes:
        - Unique identifier (ID)
        - Name and description
        - Icon identifier
        - Status type
        - Multi-language translations (if configured)

        This is a read-only endpoint optimized for common list data retrieval.
      operationId: ProjectsController_listStatuses
      parameters: []
      responses:
        '200':
          description: Project statuses retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectStatusDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all project statuses
      tags:
        - projects
        - common-lists
      x-required-scopes:
        - api:read
  /projects/phases:
    get:
      description: |-
        Returns a list of all project phases configured in the workspace.

        **What are Project Phases?**
        Phases represent distinct stages or steps within a project lifecycle (e.g., Planning, Development, Testing, Deployment). They provide additional granularity beyond project statuses and are particularly useful for external single projects. Phases can be configured in workspace settings and support multi-language translations.

        **Phase Types:**
        Each phase has a type that determines its behavior in the project workflow.

        **What is returned:**
        Each phase includes:
        - Unique identifier (ID)
        - Name and description
        - Icon identifier
        - Phase type
        - Multi-language translations (if configured)

        This is a read-only endpoint optimized for common list data retrieval.
      operationId: ProjectsController_listPhases
      parameters: []
      responses:
        '200':
          description: Project phases retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectPhaseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all project phases
      tags:
        - projects
        - common-lists
      x-required-scopes:
        - api:read
  /projects/{id}/component-overview:
    get:
      description: |-
        Returns the hierarchical structure of project components, items, and associated tasks.

        **What are Components?**
        Components are reusable project templates that standardize recurring project types. They serve as blueprints for planning, pricing, and executing similar projects consistently. When imported into a project, components create a structured tree of work items.

        **Component Structure:**
        Components contain a hierarchical tree of items:
        - **Epics**: Groups related work packages around a common theme
        - **Work Packages**: Self-contained, clearly defined tasks that deliver tangible results
        - **Todo Lists**: Checklists for quality control and process steps
        - **Test Suites**: Acceptance tests for formal approval

        **What is returned:**
        The overview shows:
        - Complete hierarchical structure of all components in the project
        - All component items (Epics, Work Packages, Todo Lists, Test Suites)
        - Tasks that have been connected to component items
        - Implementation progress and status information

        This endpoint helps track how project components are being implemented and which tasks are associated with each component item.
      operationId: ProjectsController_getComponentOverview
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Component overview retrieved successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - missing required permission
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project component overview
      tags:
        - projects
      x-required-permissions:
        - projects.manageComponents
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /projects/{id}/objects:
    get:
      description: Returns objects whose current projectId matches this project. Object visibility rules still apply.
      operationId: ProjectsController_listProjectObjects
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: page
          required: false
          in: query
          schema:
            minimum: 1
            default: 1
            type: number
        - name: pageSize
          required: false
          in: query
          schema:
            minimum: 1
            maximum: 200
            default: 50
            type: number
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List objects assigned to a project
      tags:
        - projects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /projects/{id}:
    get:
      description: |-
        Returns complete project details including all settings, relationships, and metadata.

        **What is returned:**
        The response includes:
        - Basic information: name, short name, type, value group, description, icon
        - Classification: category, status, phase, organization
        - Access settings: guest access, assigned users and teams
        - Task configuration: enabled task types and activities
        - Custom data: custom fields, tags
        - Responsibilities: default accountable user, responsible user
        - Timestamps: creation date, last edit date, archive date
        - Project metadata: color, favorite status, express quotations setting

        The description field is returned as HTML (converted from the internal IDoc format).
      operationId: ProjectsController_getProjectDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get project details
      tags:
        - projects
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates only the fields provided in the request body. All fields are optional - only included fields will be updated.

        **How it works:**
        - Send only the fields you want to update
        - Fields not included in the request remain unchanged
        - Arrays (users, teams, taskTypes, activities, tags) replace the entire array
        - Set a field to null to clear it (where applicable)

        **Description Format:**
        If you provide a description field, it accepts HTML or Markdown and will be converted to the internal IDoc format.

        **Important Notes:**
        - organizationId: Can be changed. The numeric project identifier is preserved when available; if it already exists in the target organization, the next available number is assigned automatically. The displayed short name uses the target organization prefix. Set null for internal projects.
        - phaseId: Can be updated, but is required for external single projects
        - enableExpressQuotations: Only applicable for external projects
        - guestAccess: Only applicable for external projects
      operationId: ProjectsController_patchProject
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,shortName,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchProjectDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update project
      tags:
        - projects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - shortName
          - name
        allowedFields:
          - id
          - shortName
          - name
          - type
          - projectType
          - organizationId
          - statusId
          - phaseId
          - categoryId
          - valueGroup
          - deadline
          - responsibleId
          - defaultAccountableId
          - guestAccess
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - projects.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a project. The project will be marked as deleted (archived) but not permanently removed from the database.

        **What happens:**
        - The project is marked as archived
        - It will no longer appear in active project lists
        - Historical data (tasks, time bookings, etc.) is preserved
        - The project can be viewed in archived projects if needed

        **Note:** This is a soft delete operation. The project data remains in the database for historical reporting and audit purposes.
      operationId: ProjectsController_deleteProject
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete project
      tags:
        - projects
      x-required-permissions:
        - projects.delete
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /projects/{id}/active-subscriptions:
    get:
      description: |-
        Returns all active subscriptions (manual and product-based) for a project as of the current date.

        **What are Active Subscriptions?**
        Active subscriptions combine:
        - **Manual subscriptions**: Fixed price and variable price subscriptions configured directly for the project
        - **Product subscriptions**: Products with subscription pricing from the billing version/snapshot (for Single projects) or current products (for Support projects)

        **Project Type Behavior:**
        - **Single Projects**: Uses products from the billing version/snapshot (productsBillingProjectSnapshotId or billingProjectSnapshotId)
        - **Support Projects**: Uses current products (no snapshot)

        **What is returned:**
        - Only subscriptions that are currently active (activeFrom <= now <= activeTo)
        - Each subscription includes: name, activeFrom date, activeTo date, priceSubscription, pricePerUnit
        - Manual subscriptions: name comes from manualTitle, dates from fromDate/toDate
        - Product subscriptions: name comes from finalName, dates from activeFrom/activeTo

        **Note:** Requires project access permission.
      operationId: ProjectsController_getActiveSubscriptions
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Active subscriptions retrieved successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found or access denied
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get active subscriptions for a project
      tags:
        - projects
      x-required-scopes:
        - api:read
  /projects/{id}/support-contingents:
    get:
      description: |-
        Retrieves all support contingents (retainer plans) for a specific project with detailed information including available and consumed hours.

        **What are Support Contingents?**
        Support contingents are retainer plans that define allocated hours and pricing for support periods. They help manage support contracts with predefined hours, rates, and transferability of unused hours.

        **What is returned:**
        - All support contingents for the project
        - Each contingent includes: title, date range, frequency, hours, transferability, active status
        - For active contingents: available hours and consumed hours for the current period
        - Available hours calculation considers transferable logic and consumed hours from invoices
        - Results are sorted by active status (active first) and then by fromDate

        **Note:** Requires project access permission.
      operationId: ProjectsController_getSupportContingentsWithDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Support contingents retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SupportContingentWithDetailsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List support contingents with details for a project
      tags:
        - projects
        - projects
        - support-contingents
      x-required-scopes:
        - api:read
  /projects/{id}/support-contingents-details:
    get:
      description: |-
        Retrieves a detailed period-by-period breakdown of all support contingents for a specific project.

        **What are Support Contingents?**
        Support contingents are retainer plans that define allocated hours and pricing for support periods. They help manage support contracts with predefined hours, rates, and transferability of unused hours.

        **What is returned:**
        - All support contingents for the project
        - For each contingent: period-by-period breakdown from start date until present day
        - Each period includes: start/end dates, allocated hours, hours from previous period (if transferable), consumed hours, and list of tasks
        - Tasks include: title, short number, hours billed, invoice number (if billed), and unbilled flag (if in progress)
        - Current period includes unbilled tasks with their spent hours
        - Periods are sorted newest first, active contingents appear before inactive ones
        - Tasks within each period are sorted with unbilled tasks first, then by short number

        **Note:** Requires project access and `Projects.canSeeSupportContingentDetails` permission.
      operationId: ProjectsController_getSupportContingentsBreakdown
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Support contingents breakdown retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SupportContingentWithBreakdownResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get detailed period-by-period breakdown of support contingents
      tags:
        - projects
        - projects
        - support-contingents
      x-required-permissions:
        - projects.canSeeSupportContingentDetails
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /object-types:
    get:
      operationId: ObjectTypesController_list
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ObjectTypeListItemDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List object types (admin)
      tags:
        - object-types
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: ObjectTypesController_create
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateObjectTypeDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create object type (admin)
      tags:
        - object-types
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /object-types/sort:
    post:
      operationId: ObjectTypesController_saveObjectTypeSort
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveSortDTO'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder object types (admin)
      tags:
        - object-types
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /object-types/{typeId}/statuses:
    get:
      operationId: ObjectTypesController_listStatuses
      parameters:
        - name: typeId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ObjectStatusDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List statuses for an object type (admin)
      tags:
        - object-types
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: ObjectTypesController_createStatus
      parameters:
        - name: typeId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateObjectStatusDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectStatusDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create object status (admin)
      tags:
        - object-types
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - color
          - sort
          - createdAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /object-types/{typeId}/statuses/sort:
    post:
      operationId: ObjectTypesController_saveStatusSort
      parameters:
        - name: typeId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveSortDTO'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder statuses for an object type (admin)
      tags:
        - object-types
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /object-types/{typeId}/statuses/{statusId}:
    put:
      operationId: ObjectTypesController_updateStatus
      parameters:
        - name: typeId
          required: true
          in: path
          schema:
            type: string
        - name: statusId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateObjectStatusDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectStatusDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update object status (admin)
      tags:
        - object-types
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - color
          - sort
          - createdAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: ObjectTypesController_deleteStatus
      parameters:
        - name: typeId
          required: true
          in: path
          schema:
            type: string
        - name: statusId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete object status (admin, soft delete)
      tags:
        - object-types
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /object-types/{id}:
    get:
      operationId: ObjectTypesController_getOne
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectTypeDetailResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get object type with statuses and custom fields (admin)
      tags:
        - object-types
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      operationId: ObjectTypesController_update
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateObjectTypeDto'
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update object type (admin)
      tags:
        - object-types
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: ObjectTypesController_remove
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete object type (admin, soft delete). Allowed even when object instances exist; instances keep objectTypeId pointing at the soft-deleted type.
      tags:
        - object-types
      x-required-permissions:
        - objects.canManageTypes
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects:
    get:
      operationId: ObjectsController_list
      parameters:
        - name: objectTypeId
          required: false
          in: query
          description: Filter by object type UUID
          schema:
            type: string
        - name: organizationId
          required: false
          in: query
          description: Filter by organization UUID
          schema:
            type: string
        - name: projectId
          required: false
          in: query
          description: Filter by assigned project UUID
          schema:
            type: string
        - name: statusId
          required: false
          in: query
          description: Filter by object status UUID
          schema:
            type: string
        - name: search
          required: false
          in: query
          description: Case-insensitive substring match on name
          schema:
            type: string
        - name: page
          required: false
          in: query
          schema:
            minimum: 1
            default: 1
            type: number
        - name: pageSize
          required: false
          in: query
          schema:
            minimum: 1
            maximum: 200
            default: 50
            type: number
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedObjectsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List objects (visibility rules apply)
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: ObjectsController_create
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,objectType,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateObjectDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectInstanceResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create object
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - objectType
          - status
        allowedFields:
          - id
          - name
          - objectType
          - status
          - organizationId
          - organization
          - projectId
          - project
          - externalId
          - tags
          - createdAt
          - editedAt
          - customFields
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/{id}/files:
    get:
      operationId: ObjectsController_listFiles
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ObjectFileResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List files attached to an object
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: Upload via POST /workspace/upload first, then pass fileId here.
      operationId: ObjectsController_attachFile
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,file,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachObjectFileDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectFileResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Attach a file to an object
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - file
          - createdAt
        allowedFields:
          - id
          - category
          - description
          - createdAt
          - uploadedBy
          - file
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/{id}/files/{objectFileId}:
    delete:
      description: objectFileId is the ObjectFile join row id (not the raw File id).
      operationId: ObjectsController_deleteFile
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: objectFileId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Remove an object file attachment
      tags:
        - objects
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/{id}/files/{fileRowId}/download:
    get:
      description: fileRowId is the ObjectFile join row id (same as in list files). Streams the file as an attachment.
      operationId: ObjectsController_downloadObjectFile
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fileRowId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Download a file attached to an object
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /objects/file-categories:
    get:
      operationId: ObjectsController_listFileCategories
      parameters: []
      responses:
        '200':
          description: Category id and name pairs
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                    name:
                      type: string
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List object file categories (workspace-wide)
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /objects/file-categories/save:
    post:
      operationId: ObjectsController_saveFileCategory
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
                  format: uuid
                  description: Set to update
                name:
                  type: string
              required:
                - name
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update an object file category
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/file-categories/{categoryId}:
    delete:
      operationId: ObjectsController_deleteFileCategory
      parameters:
        - name: categoryId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Soft-delete an object file category
      tags:
        - objects
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/assignments/assign-to-organization:
    post:
      operationId: ObjectsController_assignToOrganization
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignObjectsToOrganizationDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Assign objects to an organization
      tags:
        - objects
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/assignments/unassign-from-organization:
    post:
      operationId: ObjectsController_unassignFromOrganization
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnassignObjectFromOrganizationDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Unassign an object from its organization
      tags:
        - objects
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/assignments/assign-to-project:
    post:
      operationId: ObjectsController_assignToProject
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,objectId,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignObjectsToProjectDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Assign objects to a project
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - objectId
          - createdAt
        allowedFields:
          - id
          - objectId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/assignments/unassign-from-project:
    post:
      operationId: ObjectsController_unassignFromProject
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,objectId,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnassignObjectFromProjectDto'
      responses:
        '201':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Unassign an object from its project
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - objectId
          - createdAt
        allowedFields:
          - id
          - objectId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/{id}/journal/{entryId}:
    get:
      operationId: ObjectsController_getJournalEntry
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: entryId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectJournalResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get one object journal entry
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: Requires canEditObject, or editing your own entry when you only have canViewObjects (same rules as app).
      operationId: ObjectsController_patchJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: entryId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,objectId,lastUpdated
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchObjectJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectJournalResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update object journal entry
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - objectId
          - lastUpdated
        allowedFields:
          - id
          - objectId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: ObjectsController_deleteJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: entryId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete object journal entry (soft delete)
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/{id}/journal:
    get:
      operationId: ObjectsController_listJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List journal entries for an object (paginated grid)
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: ObjectsController_createJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,objectId,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateObjectJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectJournalResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create object journal entry
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - objectId
          - createdAt
        allowedFields:
          - id
          - objectId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/{id}/history:
    get:
      operationId: ObjectsController_historyForObject
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedObjectHistoryResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Audit history for an object (paginated, newest first)
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /objects/{id}/tasks:
    get:
      operationId: ObjectsController_tasksForObject
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TasksByObjectResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List tasks linked to an object
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /objects/tasks/link:
    post:
      operationId: ObjectsController_linkObjectToTask
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: objectId,taskId
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkObjectToTaskPublicDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LinkObjectToTaskPublicResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Link an object to a task
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - objectId
          - taskId
        allowedFields:
          - objectId
          - taskId
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/tasks/link-batch:
    post:
      description: Creates any missing links; already-linked pairs are skipped. Task-side and object-side audit entries match the internal batch semantics.
      operationId: ObjectsController_linkObjectsBatchToTask
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: linked
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkObjectsBatchToTaskPublicDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LinkObjectsBatchPublicResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Link multiple objects to one task
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - linked
        allowedFields:
          - linked
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/tasks/unlink:
    post:
      operationId: ObjectsController_unlinkObjectFromTask
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkObjectToTaskPublicDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Remove the link between an object and a task
      tags:
        - objects
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /objects/{id}:
    get:
      operationId: ObjectsController_getOne
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectInstanceResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get object by id
      tags:
        - objects
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      operationId: ObjectsController_update
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,objectType,status
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateObjectDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectInstanceResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update object
      tags:
        - objects
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - objectType
          - status
        allowedFields:
          - id
          - name
          - objectType
          - status
          - organizationId
          - organization
          - projectId
          - project
          - externalId
          - tags
          - createdAt
          - editedAt
          - customFields
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: ObjectsController_remove
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Soft-delete object
      tags:
        - objects
      x-required-permissions:
        - objects.canEdit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/document-settings:
    get:
      description: |-
        Retrieves the current document settings for the workspace.

        **What are Document Settings?**
        Document settings control the appearance and formatting of all documents automatically generated by Leadtime, including letters, invoices, quotes, specifications, and project documents. These settings apply workspace-wide and can be overridden at the organization or project level.

        **What is returned:**
        - **Document font**: The selected font family for all documents (e.g., Inter, Roboto, Arimo)
        - **Document logo URL**: Public URL to the workspace logo image (null if not set, defaults to Leadtime logo)
        - **Project document options**:
          - Title page toggle (automatically adds a title page)
          - Table of contents toggle (creates TOC at document start)
          - Heading style (controls how headings are numbered and formatted)
        - **Background image URLs**: Public URLs for background images used on cover pages:
          - Specification background (for specs documents)
          - Estimate background (for proposal/quote documents)
          - Custom editor background (for template-based documents)

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. All file URLs are public and can be accessed without authentication.
      operationId: DocumentSettingsController_getDocumentSettings
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetDocumentSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get document settings
      tags:
        - administration
        - document-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates document settings for the workspace. All fields are optional - only provided fields will be updated.

        **What are Document Settings?**
        Document settings control the appearance and formatting of all documents automatically generated by Leadtime, including letters, invoices, quotes, specifications, and project documents.

        **How to update document settings:**
        1. **For file uploads** (logo, background images):
           - Upload image files using POST /api/public/workspace/upload
           - Get the file ID from the upload response
           - Use the file ID in the corresponding field (`documentLogoId`, `specificationBackgroundId`, etc.)
           - To remove a file, set the field to `null`
        2. **For font selection**: Choose from available fonts (Inter, Degular, Roboto, Arimo, Open Sans, PT Sans, Oswald, EB Garamond, Permanent Marker, IBM Plex Mono)
        3. **For project document options**: Set boolean flags and heading style enum values

        **Available heading styles:**
        - `Normal`: Headings without numbering
        - `Sequential`: Continuous numbering from level 1 (e.g., 1., 1.1., 1.1.1.)
        - `SequentialFromSecondLevel`: Top heading unnumbered, subchapters numbered (e.g., 1. Heading 2, 1.1. Heading 3)
        - `SequentialWithParagraphs`: Numbering with § prefix from level 2 (e.g., § 1., § 1.1.) - ideal for contracts

        **Background image recommendations:**
        - Recommended size: 820 × 600 pixels
        - Formats: PNG, JPG, or SVG
        - Used on cover pages of generated documents

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect all documents generated after the update. Use GET endpoint to preview current settings.
      operationId: DocumentSettingsController_updateDocumentSettings
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchDocumentSettingsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update document settings
      tags:
        - administration
        - document-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /sales/estimates/grid:
    get:
      description: |-
        **What are Sales Estimates?**
        Sales Estimates provides a unified overview of all ongoing offers and sales opportunities in Leadtime. This endpoint combines two types of estimates into a single grid view:
        - **Project Documents**: Detailed estimates linked to larger projects, created through the project planning process with products, modules, and custom items
        - **Express Quotations**: Quick estimates created directly from tickets for smaller, ad-hoc customer requests

        **What data is returned:**
        The response includes a paginated grid of estimate items with comprehensive pricing information:
        - Each item represents either a project document estimate or an express quotation
        - Only the latest estimate per project or task is returned (older versions are excluded)
        - Pricing breakdown includes fixed prices, subscription prices, and per-unit prices
        - Aggregated totals across all filtered results are provided at the response level

        **Pricing components:**
        Each estimate can have three types of pricing:
        - **Fixed Price**: One-time charges for services or products
        - **Subscriptions**: Recurring prices (monthly, quarterly, semi-annually, or annually)
        - **Per Unit**: Prices based on quantity (e.g., price per user or workspace)

        The response includes both individual item pricing and aggregated totals (totalPriceFixed, totalPriceSubscriptions, totalPricePerUnit) across all filtered results.

        **Estimate types:**
        - **PROJECT_DOCUMENT**: Estimates created as part of project planning, typically more detailed and structured
        - **EXPRESS_QUOTATION**: Quick estimates created from tickets, ideal for small add-on tasks or support services

        **Estimate statuses:**
        - **Draft**: Estimate is being prepared and has not been sent
        - **WaitingForApproval**: Estimate is pending internal approval before sending
        - **Final**: Estimate has been finalized and approved
        - **Pending**: Express quotation has been sent to customer, awaiting response
        - **Accepted**: Customer has accepted the estimate
        - **Rejected**: Customer has rejected the estimate
        - **Parked**: Estimate has been set aside temporarily

        **Access restrictions:**
        - This endpoint is restricted to employees only. Organization members (external contacts) cannot access it
        - Results are automatically filtered by the user's project access permissions
        - Only estimates for projects the user has access to are returned

        **Supported operations:**
        - Filter by type, organization, project, task, status, pricing components, creator, and creation date
        - Sort by any field including title, organization, project, status, pricing, and dates
        - Quick search across estimate titles
        - Pagination with configurable page size
        - Field selection to return only specific fields in the response

        **Use cases:**
        - Generate sales pipeline reports showing all active estimates
        - Track revenue potential across different estimate types
        - Monitor estimate status distribution (draft, pending, accepted, etc.)
        - Analyze pricing breakdowns by organization or project
        - Export estimate data for external reporting tools

        Retrieves a paginated grid of estimates with filtering and sorting capabilities. Quick search available on: title, displayTitle. Filterable fields: id, type, title, displayTitle, organizationId, projectId, taskId, createdAt, status, priceFixed, priceSubscriptions, pricePerUnit, createdById, offerNumber. Sortable fields: id, type, title, displayTitle, organizationId, organizationName, projectId, projectName, taskId, taskShortNumber, createdAt, status, total, priceFixed, priceSubscriptions, pricePerUnit, createdById, createdByName, offerNumber.
      operationId: SalesEstimatesController_getEstimatesGrid
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: title, displayTitle'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Estimate ID
            - **type** (string): Estimate type (PROJECT_DOCUMENT or EXPRESS_QUOTATION)
            - **title** (string): Estimate title
            - **displayTitle** (string): Display title (combined title/offerNumber based on documentStyle)
            - **organizationId** (string): Organization ID
            - **projectId** (string): Project ID
            - **taskId** (string): Task ID (only for EXPRESS_QUOTATION)
            - **createdAt** (date): Creation timestamp
            - **status** (string): Estimate status (Draft, Final, WaitingForApproval, Pending, etc.)
            - **priceFixed** (number): Fixed price
            - **priceSubscriptions** (number): Subscription price
            - **pricePerUnit** (number): Per-unit price
            - **createdById** (string): Creator user ID
            - **offerNumber** (number): Offer number (for PROJECT_DOCUMENT type)

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Estimate ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (string): Estimate type (PROJECT_DOCUMENT or EXPRESS_QUOTATION) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **title** (string): Estimate title (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **displayTitle** (string): Display title (combined title/offerNumber based on documentStyle) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **taskId** (string): Task ID (only for EXPRESS_QUOTATION) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **status** (string): Estimate status (Draft, Final, WaitingForApproval, Pending, etc.) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **priceFixed** (number): Fixed price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **priceSubscriptions** (number): Subscription price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **pricePerUnit** (number): Per-unit price (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **createdById** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **offerNumber** (number): Offer number (for PROJECT_DOCUMENT type) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Estimate ID
            - **type**: Estimate type (PROJECT_DOCUMENT or EXPRESS_QUOTATION)
            - **title**: Estimate title
            - **displayTitle**: Display title (combined title/offerNumber based on documentStyle)
            - **organizationId**: Organization ID
            - **organizationName**: Organization name
            - **projectId**: Project ID
            - **projectName**: Project name
            - **taskId**: Task ID (only for EXPRESS_QUOTATION)
            - **taskShortNumber**: Task short number (only for EXPRESS_QUOTATION)
            - **createdAt**: Creation timestamp
            - **status**: Estimate status (Draft, Final, WaitingForApproval, Pending, etc.)
            - **total**: Total price (for express quotations)
            - **priceFixed**: Fixed price
            - **priceSubscriptions**: Subscription price
            - **pricePerUnit**: Per-unit price
            - **createdById**: Creator user ID
            - **createdByName**: Creator name
            - **offerNumber**: Offer number (for PROJECT_DOCUMENT type)

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, type, title, displayTitle, organizationId, organizationName, projectId, projectName, taskId, taskShortNumber, createdAt, status, total, priceFixed, priceSubscriptions, pricePerUnit, priceSubscriptionsDetail, pricePerUnitDetail, createdById, createdByName, offerNumber, formattedOfferNumber
          schema:
            example: id,type
            type: array
            items: {}
      responses:
        '200':
          description: Estimates grid with pricing totals (totalPriceFixed, totalPriceSubscriptions, totalPricePerUnit)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EstimateGridResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get sales estimates grid
      tags:
        - sales
        - estimates
      x-required-permissions:
        - projects.manageDocuments
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /sales/opportunities:
    get:
      description: |-
        Retrieves a paginated grid of sales opportunities with filtering and sorting capabilities. Quick search available on: name, shortName, orgName. Filterable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, deadline, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt. Sortable fields: id, name, shortName, type, valueGroup, categoryId, statusId, phaseId, deadline, organizationId, orgName, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt.

        **What are Sales Opportunities?**

        Sales opportunities represent potential projects in the customer lifecycle where a customer has not placed any order yet. They are used to organize leads, track sales stages, manage contacts, and systematically move potential deals forward. In Leadtime, sales opportunities work as projects that are assigned to an external organization (customer). The goal of every opportunity is to create an offer, and if successful, it can turn into an implementation project.

        **How Sales Opportunities Work:**

        - Sales opportunities are Single-type projects that have an organizationId (external projects)
        - They can be linked to one or more offers or estimates
        - They use project phases to track progress through the sales process (e.g., Uncontacted, Research, Contact made, Reminder, Implementation, Won, Finished)
        - They support custom fields for storing additional information like probability, source, region, or segment
        - They include a journal for documenting activities, feedback, and interactions with potential customers

        **Automatic Filtering:**

        This endpoint automatically applies three filters to ensure only relevant sales opportunities are returned:
        1. **Project type**: Must be Single (only single projects can be sales opportunities)
        2. **Organization ID**: Must not be empty (only external projects linked to organizations)
        3. **Status exclusion**: Projects with Done status type are automatically excluded (only active opportunities are shown)

        **Data Returned:**

        The response includes project grid items with fields such as:
        - Project identification: id, name, shortName, icon
        - Project classification: type, valueGroup, categoryId, statusId, phaseId
        - Organization information: organizationId, orgName, orgColor
        - Project metadata: createdAt, editedAt, isFavorite, isArchived, guestAccess
        - Task information: openTasksCount

        **Access Control:**

        Users can only see opportunities for projects they have access to based on their project permissions. The endpoint requires the Opportunities.manageSalesOpportunities permission.
      operationId: SalesOpportunitiesController_listOpportunities
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: name, shortName, orgName'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Project ID
            - **name** (string): Project name
            - **shortName** (string): Project short name (organization prefix + number)
            - **type** (set): Project type
            - **valueGroup** (set): Value group classification
            - **categoryId** (string): Project category ID
            - **statusId** (string): Project status ID
            - **phaseId** (string): Project phase ID
            - **deadline** (date): Project deadline date
            - **organizationId** (string): Organization ID
            - **orgName** (string): Organization name
            - **guestAccess** (boolean): Whether guest access is enabled
            - **isFavorite** (boolean): Whether the project is marked as favorite by the current user
            - **isArchived** (boolean): Whether the project is archived
            - **openTasksCount** (number): Number of open tasks
            - **createdAt** (date): Creation timestamp
            - **editedAt** (date): Last update timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **name** (string): Project name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **shortName** (string): Project short name (organization prefix + number) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Project type (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **valueGroup** (set): Value group classification (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **categoryId** (string): Project category ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **statusId** (string): Project status ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **phaseId** (string): Project phase ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **deadline** (date): Project deadline date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **orgName** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **guestAccess** (boolean): Whether guest access is enabled (boolean filter). Available comparisons: equal, not_equal
            - **isFavorite** (boolean): Whether the project is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal
            - **isArchived** (boolean): Whether the project is archived (boolean filter). Available comparisons: equal, not_equal
            - **openTasksCount** (number): Number of open tasks (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Project ID
            - **name**: Project name
            - **shortName**: Project short name (organization prefix + number)
            - **type**: Project type
            - **valueGroup**: Value group classification
            - **categoryId**: Project category ID
            - **statusId**: Project status ID
            - **phaseId**: Project phase ID
            - **deadline**: Project deadline date
            - **organizationId**: Organization ID
            - **orgName**: Organization name
            - **guestAccess**: Whether guest access is enabled
            - **isFavorite**: Whether the project is marked as favorite by the current user
            - **isArchived**: Whether the project is archived
            - **openTasksCount**: Number of open tasks
            - **createdAt**: Creation timestamp
            - **editedAt**: Last update timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, name, shortName, icon, type, valueGroup, categoryId, statusId, phaseId, deadline, organizationId, orgName, orgColor, guestAccess, isFavorite, isArchived, openTasksCount, createdAt, editedAt
          schema:
            example: id,name
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of sales opportunities
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List sales opportunities
      tags:
        - sales
      x-required-permissions:
        - opportunities.manageSalesOpportunities
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /administration/subscription-status:
    get:
      description: |-
        **What is Subscription Status?**
        Subscription Status provides comprehensive information about your workspace subscription, billing details, and payment information. This endpoint returns all data needed to display subscription management interfaces, including current plan details, upcoming invoices, payment history, and feature availability.

        **What data is returned:**

        **Core Subscription Information:**
        - **billingMode**: The workspace billing tier (free, regular, or enterprise)
        - **billingStatus**: Current subscription status (active, trialing, canceled, etc.)
        - **planCode**: The specific plan identifier
        - **billingInterval**: Billing frequency (monthly or yearly)
        - **features**: List of enabled workspace features
        - **featuresInfo**: Detailed feature information with pricing for each enabled feature

        **Stripe Integration:**
        - **stripeCustomerId**: Stripe customer identifier (null for free workspaces)
        - **stripeSubscriptionId**: Stripe subscription identifier (null for free workspaces)
        - **hasPaymentMethod**: Whether a payment method is on file

        **Trial Information:**
        - **trialEnd**: End date of trial period (null if not in trial)

        **Subscription Details** (Enterprise workspaces only):
        - **status**: Current subscription status from Stripe
        - **currentPeriodStart/End**: Current billing period dates
        - **items**: List of subscription items with product names, quantities, and pricing
        - **dashboardUrl**: Direct link to Stripe dashboard for this subscription

        **Invoice Information** (Paid workspaces only):
        - **nextInvoice**: Preview of upcoming invoice with:
          - Line items with descriptions and quantities
          - Subtotal, tax, and total amounts
          - Tax breakdown by rate
          - Due date
        - **latestInvoices**: Up to 5 most recent invoices with:
          - Invoice number and status
          - Amount and currency
          - Payment dates and due dates
          - Direct links to view invoices

        **Seat Management:**
        - **billableUsersCount**: Current number of billable users
        - **committedSeats**: Seats currently committed for billing
        - **pendingSeats**: Seats scheduled to be added in next period
        - **pendingEffectiveAt**: When pending seats become effective

        **Cancellation Information:**
        - **cancelAtPeriodEnd**: Whether subscription will cancel at period end
        - **cancellationEffectiveAt**: When cancellation becomes effective
        - **cancellationRequestedAt**: When cancellation was requested
        - **cancellationReason**: Reason provided for cancellation

        **Billing Interval Changes:**
        - **nextBillingInterval**: Scheduled billing interval change (if any)
        - **billingIntervalChangeScheduled**: When the change takes effect
        - **stillAvailableFeatures**: Features that remain available after interval change

        **Important Notes:**
        - This is a read-only endpoint - it does not modify subscription state
        - Free workspaces return basic information without Stripe details
        - Enterprise workspaces include detailed Stripe subscription information
        - Trial workspaces may have limited invoice history
        - All monetary amounts are in cents (divide by 100 for display)
        - Dates are returned in ISO 8601 format
      operationId: SubscriptionStatusController_getSubscriptionStatus
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubscriptionStatusResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get workspace subscription and billing status
      tags:
        - administration
        - subscription-status
      x-required-permissions:
        - ws.manageSubscription
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/invoices/new:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of invoices that are created but not yet sent, or sent but not yet overdue.

        **What are New Invoices?**
        The "New" tab shows all invoices that have been created but not yet sent, or invoices that have been sent but are not yet overdue. These invoices are in status "New" or "Sent" and have either no due date or a due date in the future.

        **What is returned:**
        - Invoice identification (ID, number)
        - Project reference (projectId)
        - Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
        - Creation and sending dates (createdAt, sentAt)
        - Payment due date (dueDate)
        - Net amount before taxes and fees (netto)
        - Invoice status (New, Sent)

        **Filtering and search:**
        - Quick search across invoice number
        - Filter by project, type, status, dates, and amount
        - Sort by any field

        Retrieves a paginated grid of new invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, sentAt, dueDate, netto, status. Sortable fields: id, number, projectId, type, createdAt, sentAt, dueDate, netto, status.
      operationId: InvoicesController_getNewInvoices
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: number'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Invoice ID
            - **number** (string): Invoice number
            - **projectId** (string): Project ID
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt** (date): Invoice creation date
            - **sentAt** (date): Date when invoice was sent
            - **dueDate** (date): Payment due date
            - **netto** (number): Net amount (before taxes and fees)
            - **status** (set): Invoice status (New, Sent)

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **sentAt** (date): Date when invoice was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **dueDate** (date): Payment due date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **status** (set): Invoice status (New, Sent) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Invoice ID
            - **number**: Invoice number
            - **projectId**: Project ID
            - **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt**: Invoice creation date
            - **sentAt**: Date when invoice was sent
            - **dueDate**: Payment due date
            - **netto**: Net amount (before taxes and fees)
            - **status**: Invoice status (New, Sent)

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, number, projectId, type, createdAt, sentAt, dueDate, netto, status
          schema:
            example: id,number
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved new invoices grid
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get new invoices grid
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/invoices/paid:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of invoices that have been marked as paid.

        **What are Paid Invoices?**
        The "Paid" tab shows all invoices that have been fully settled. These invoices are in status "Paid" and represent completed transactions.

        **What is returned:**
        - Invoice identification (ID, number)
        - Project reference (projectId)
        - Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
        - Creation date (createdAt)
        - Net amount before taxes and fees (netto)
        - Invoice status (Paid)

        **Filtering and search:**
        - Quick search across invoice number
        - Filter by project, type, status, date, and amount
        - Sort by any field

        Retrieves a paginated grid of paid invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, netto, status. Sortable fields: id, number, projectId, type, createdAt, netto, status.
      operationId: InvoicesController_getPaidInvoices
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: number'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Invoice ID
            - **number** (string): Invoice number
            - **projectId** (string): Project ID
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt** (date): Invoice creation date
            - **netto** (number): Net amount (before taxes and fees)
            - **status** (set): Invoice status (Paid)

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **status** (set): Invoice status (Paid) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Invoice ID
            - **number**: Invoice number
            - **projectId**: Project ID
            - **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt**: Invoice creation date
            - **netto**: Net amount (before taxes and fees)
            - **status**: Invoice status (Paid)

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, number, projectId, type, createdAt, netto, status
          schema:
            example: id,number
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved paid invoices grid
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get paid invoices grid
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/invoices/canceled:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of invoices that have been cancelled.

        **What are Canceled Invoices?**
        The "Canceled" tab shows all invoices that were officially cancelled after being sent out. Cancellation happens via credit note and balances out the original amount correctly from an accounting point of view.

        **What is returned:**
        - Invoice identification (ID, number)
        - Project reference (projectId)
        - Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
        - Creation date (createdAt)
        - Cancellation date (statusChangedAt)
        - Net amount before taxes and fees (netto)
        - Invoice status (Cancelled)

        **Filtering and search:**
        - Quick search across invoice number
        - Filter by project, type, status, dates, and amount
        - Sort by any field

        Retrieves a paginated grid of canceled invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, statusChangedAt, netto, status. Sortable fields: id, number, projectId, type, createdAt, statusChangedAt, netto, status.
      operationId: InvoicesController_getCanceledInvoices
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: number'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Invoice ID
            - **number** (string): Invoice number
            - **projectId** (string): Project ID
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt** (date): Invoice creation date
            - **statusChangedAt** (date): Date when invoice was cancelled
            - **netto** (number): Net amount (before taxes and fees)
            - **status** (set): Invoice status (Cancelled)

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **statusChangedAt** (date): Date when invoice was cancelled (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **status** (set): Invoice status (Cancelled) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Invoice ID
            - **number**: Invoice number
            - **projectId**: Project ID
            - **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt**: Invoice creation date
            - **statusChangedAt**: Date when invoice was cancelled
            - **netto**: Net amount (before taxes and fees)
            - **status**: Invoice status (Cancelled)

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** statusChangedAt (desc)
          schema:
            example: '[{"field":"statusChangedAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, number, projectId, type, createdAt, statusChangedAt, netto, status
          schema:
            example: id,number
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved canceled invoices grid
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get canceled invoices grid
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/invoices/overdue:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of invoices that are past their payment due date.

        **What are Overdue Invoices?**
        The "Overdue" tab shows all invoices where the payment term has passed. These invoices are in status "New" or "Sent" and have a due date that is in the past. This view helps you monitor open receivables, send reminders, and manage payment deadlines.

        **What is returned:**
        - Invoice identification (ID, number)
        - Project reference (projectId)
        - Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
        - Creation and sending dates (createdAt, sentAt)
        - Payment due date (dueDate)
        - Reminder dates (firstReminderAt, secondReminderAt)
        - Number of days overdue (overdueDays)
        - Net amount before taxes and fees (netto)
        - Invoice status (New, Sent)

        **Filtering and search:**
        - Quick search across invoice number
        - Filter by project, type, status, dates, amount, and overdue days
        - Sort by any field (default: overdueDays descending)

        Retrieves a paginated grid of overdue invoices with filtering and sorting capabilities. Quick search available on: number. Filterable fields: id, number, projectId, type, createdAt, sentAt, dueDate, firstReminderAt, secondReminderAt, netto, status, overdueDays. Sortable fields: id, number, projectId, type, createdAt, sentAt, dueDate, firstReminderAt, secondReminderAt, netto, status, overdueDays.
      operationId: InvoicesController_getOverdueInvoices
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: number'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Invoice ID
            - **number** (string): Invoice number
            - **projectId** (string): Project ID
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt** (date): Invoice creation date
            - **sentAt** (date): Date when invoice was sent
            - **dueDate** (date): Payment due date
            - **firstReminderAt** (date): Date when first reminder was sent
            - **secondReminderAt** (date): Date when second reminder was sent
            - **netto** (number): Net amount (before taxes and fees)
            - **status** (set): Invoice status (New, Sent)
            - **overdueDays** (number): Number of days overdue

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Invoice ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **number** (string): Invoice number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (string): Project ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **createdAt** (date): Invoice creation date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **sentAt** (date): Date when invoice was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **dueDate** (date): Payment due date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **firstReminderAt** (date): Date when first reminder was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **secondReminderAt** (date): Date when second reminder was sent (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **netto** (number): Net amount (before taxes and fees) (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **status** (set): Invoice status (New, Sent) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **overdueDays** (number): Number of days overdue (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Invoice ID
            - **number**: Invoice number
            - **projectId**: Project ID
            - **type**: Invoice type (Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment)
            - **createdAt**: Invoice creation date
            - **sentAt**: Date when invoice was sent
            - **dueDate**: Payment due date
            - **firstReminderAt**: Date when first reminder was sent
            - **secondReminderAt**: Date when second reminder was sent
            - **netto**: Net amount (before taxes and fees)
            - **status**: Invoice status (New, Sent)
            - **overdueDays**: Number of days overdue

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** overdueDays (desc)
          schema:
            example: '[{"field":"overdueDays","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, number, projectId, type, createdAt, sentAt, dueDate, firstReminderAt, secondReminderAt, netto, status, overdueDays
          schema:
            example: id,number
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved overdue invoices grid
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get overdue invoices grid
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/invoices:
    post:
      description: |-
        Creates a new invoice from a potential invoice. A potential invoice represents billable items (tasks, products, subscriptions, express quotations) that are ready to be invoiced.

        **How invoice creation works:**
        1. First, retrieve available potential invoices using GET /billing/potential-invoices
        2. Review and customize the potential invoice details if needed (adjust billing periods, skip tasks, add manual positions)
        3. Create the invoice using this endpoint with the potential invoice ID
        4. The system automatically:
           - Generates a sequential invoice number in the format: `{ORG}{PRJ}-{YY}{MM}-{NNN}`
           - Calculates all amounts, taxes, and fees
           - Marks tasks, subscriptions, and express quotations as billed
           - Updates project billing status
           - Handles interim payments

        **What happens after creation:**
        - The invoice is created in status "New" (not yet sent)
        - All billable items included in the invoice are marked as billed
        - The invoice appears in the "New" invoices grid
        - You can then send the invoice using the invoice sending endpoints

        **Potential Invoice ID Format:**
        The `potentialInvoiceId` follows the format: `{type}::{projectId}::{dateFrom}::{dateTo}`
        - Type: One of Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment
        - ProjectId: UUID of the project
        - DateFrom/DateTo: Dates in YYYY-MM-DD format
        Example: `SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31`

        **What is returned:**
        - Invoice ID (UUID) - Use this to reference the invoice in other endpoints
        - Invoice number - The official sequential invoice number for accounting
      operationId: InvoicesController_createInvoice
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,number
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        description: Potential invoice identifier to create invoice from
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateInvoiceDto'
      responses:
        '200':
          description: Invoice created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvoiceResponseDto'
        '400':
          description: Invalid potential invoice ID or validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new invoice
      tags:
        - billing
        - invoices
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - number
        allowedFields:
          - id
          - number
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/invoiceContacts/{projectId}:
    get:
      description: |-
        Returns a list of organization members (contacts) from the project's customer organization who can be selected as invoice recipients. These contacts are the people who will receive invoices when they are sent via email.

        **What are Invoice Contacts?**
        Invoice contacts are organization members from the customer organization associated with the project. These are the people who can receive invoices via email. Each contact must have a valid email address to receive invoices.

        **What data is returned:**
        - List of organization members from the project's customer organization
        - Each contact includes ID (UUID), full name, avatar file ID (if available), and email address
        - Contacts are sorted alphabetically by first name, then last name
        - Only active (non-deleted) organization members are returned

        **How to use:**
        1. Get the list of available contacts for a project
        2. Select a contact ID from the list
        3. Use the POST /billing/setInvoiceContactId endpoint to set the contact for an invoice
        4. When the invoice is sent, it will be emailed to this contact

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project to view contacts.
      operationId: InvoicesController_getInvoiceContacts
      parameters:
        - name: projectId
          required: true
          in: path
          description: Project ID (UUID) to get contacts for
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Invoice contacts retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/InvoiceContactResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Project not found or access denied
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get invoice contacts for a project
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/invoices/{id}:
    get:
      description: |-
        Returns complete details of an existing invoice, including all billable items, financial totals, and payment tracking information.

        **What is returned:**
        - Invoice identification (ID, number, type, status)
        - Project reference and billing period (billedFrom, billedTo)
        - All billable items:
          - Tasks with time tracking and hourly rates
          - Products with options, quantities, and pricing
          - Components with nested items and time estimates
          - Subscriptions (fixed, variable, one-time payments)
          - Express quotations with items and options
          - Support contingent charges (retainer plan charges for Support-type projects)
          - Support contingent usage (compensation/deductions from support contingents, negative amounts)
          - Manual positions (custom billing items)
        - Financial totals:
          - Net amount (before taxes and fees)
          - VAT amount and percentage
          - Leadtime fee (if applicable)
          - Gross amount (after taxes)
          - Full total (including all fees)
        - Payment tracking:
          - Due date and overdue status
          - Sent dates and recipients
          - Reminder dates (first and second)
          - Payment received date
          - Cancellation information
        - Overdue fees and interest calculations

        **Description fields:**
        All description fields (products, components, manual positions, express quotations) are returned as HTML format for easy display in web applications.

        **Use cases:**
        - View complete invoice breakdown for accounting
        - Display invoice details in customer portals
        - Generate custom invoice documents
        - Track payment status and overdue amounts
        - Audit invoice contents and calculations
      operationId: InvoicesController_getInvoiceDetails
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Successfully retrieved invoice details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvoiceDetailsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get invoice details
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    delete:
      description: |-
        Deletes a new invoice that has not yet been sent. When an invoice is deleted, all included billable items are returned to potential invoices and can be billed again.

        **What happens when you delete an invoice:**
        1. The invoice is soft-deleted (marked with deletedAt timestamp)
        2. All included billable items are reverted to unbilled status:
           - Tasks: marked as unbilled (`billed=false`), invoice time cleared
           - Express quotations: marked as unbilled (`billed=false`)
           - Subscription billing rows: marked as unbilled (`billed=false`)
           - Projects: marked as unbilled (`billed=false`) for SingleProject invoices
           - Interim payments: marked as unbilled (`isBilled=false`)
        3. All items become available again in potential invoices (GET /billing/potential-invoices)

        **Important restrictions:**
        - Only invoices with status "New" can be deleted
        - Invoices that have been sent (status "Sent", "Paid", or "Cancelled") cannot be deleted
        - To cancel a sent invoice, use the cancel invoice endpoint instead

        **Use cases:**
        - Remove an invoice that was created by mistake
        - Return items to potential invoices for re-billing with different settings
        - Correct billing errors before sending the invoice

        **What is returned:**
        - Success response confirming the invoice was deleted
        - All billable items are automatically returned to potential invoices
      operationId: InvoicesController_deleteInvoice
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Invoice deleted successfully. All billable items have been returned to potential invoices.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invoice cannot be deleted (only invoices with status "New" can be deleted)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete an invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/{id}/mark-as-paid:
    post:
      description: |-
        Marks an invoice as paid, indicating that payment has been received from the customer. This changes the invoice status from "New" or "Sent" to "Paid" and records the payment date and runtime.

        **What happens when you mark an invoice as paid:**
        1. Invoice status changes to "Paid"
        2. Payment date (`paidAt`) is recorded as the current date
        3. Payment runtime (`paidRuntime`) is calculated as the number of days between invoice creation and payment
        4. Status change timestamp and user are recorded
        5. The invoice moves from the "New" or "Overdue" tab to the "Paid" tab

        **Important restrictions:**
        - Only invoices with status "New" or "Sent" can be marked as paid
        - Invoices that are already "Paid" or "Cancelled" cannot be marked as paid
        - To revert a paid invoice, use the mark as unpaid endpoint

        **Use cases:**
        - Record payment received from customer
        - Update invoice status after manual payment confirmation
        - Track payment timing for financial reporting

        **What is returned:**
        - Success response confirming the invoice was marked as paid
      operationId: InvoicesController_markAsPaid
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Invoice marked as paid successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invoice cannot be marked as paid (only invoices with status "New" or "Sent" can be marked as paid)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Mark invoice as paid
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/{id}/mark-as-unpaid:
    post:
      description: |-
        Reverts a paid invoice back to unpaid status. This changes the invoice status from "Paid" back to "New" or "Sent" (depending on whether it was sent before), allowing you to correct payment records.

        **What happens when you mark an invoice as unpaid:**
        1. Invoice status changes from "Paid" back to "New" or "Sent"
           - If the invoice was sent before, it returns to "Sent" status
           - If the invoice was never sent, it returns to "New" status
        2. Payment date and runtime are cleared
        3. Status change timestamp and user are recorded
        4. The invoice moves from the "Paid" tab back to the appropriate tab

        **Important restrictions:**
        - Only invoices with status "Paid" can be marked as unpaid
        - Invoices that are "Cancelled" cannot be marked as unpaid
        - This operation is useful for correcting payment recording errors

        **Use cases:**
        - Correct payment recording errors
        - Revert accidental payment marking
        - Handle payment reversals or refunds

        **What is returned:**
        - Success response confirming the invoice was marked as unpaid
      operationId: InvoicesController_markAsUnpaid
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Invoice marked as unpaid successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invoice cannot be marked as unpaid (only invoices with status "Paid" can be marked as unpaid)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Mark invoice as unpaid
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/{id}/mark-as-sent:
    post:
      description: |-
        Marks an invoice as sent to the customer. This changes the invoice status from "New" to "Sent", records the sending date, and calculates the payment due date based on organization or workspace payment terms.

        **What happens when you mark an invoice as sent:**
        1. Invoice status changes from "New" to "Sent"
        2. Sending date (`sentAt`) is recorded as the current date
        3. Sending type is set to "Manual" (indicating manual marking, not email sending)
        4. Contact recipient is recorded (from invoice contactId)
        5. Payment due date (`dueDate`) is calculated based on:
           - Organization-specific payment terms (`invoiceDueDays`), or
           - Workspace default payment terms (`defaultInvoiceDueDays`)
        6. Status change timestamp and user are recorded
        7. The payment term starts running from this point

        **Important restrictions:**
        - Only invoices with status "New" or "Sent" can be marked as sent
        - Invoices that are "Paid" or "Cancelled" cannot be marked as sent
        - This endpoint marks the invoice as sent manually (use send invoice endpoint for email sending)

        **Use cases:**
        - Record that an invoice was sent manually (e.g., via postal mail or other channels)
        - Update invoice status after external sending
        - Start payment term tracking

        **What is returned:**
        - Success response confirming the invoice was marked as sent
      operationId: InvoicesController_markAsSent
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Invoice marked as sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invoice cannot be marked as sent (only invoices with status "New" or "Sent" can be marked as sent)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Mark invoice as sent
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/{id}/mark-first-reminder-as-sent:
    post:
      description: |-
        Marks the first reminder for an invoice as sent to the customer. This records the first reminder date and contact information.

        **What happens when you mark a first reminder as sent:**
        1. First reminder date (`firstReminderAt`) is recorded as the current date
        2. First reminder type is set to "Manual" (indicating manual marking, not email sending)
        3. Contact recipient is recorded (from invoice contactId)
        4. User who marked it as sent is recorded

        **Important restrictions:**
        - Only invoices with status "New" or "Sent" can have reminders marked as sent
        - The invoice must be overdue (past its due date) to send reminders
        - This endpoint marks the reminder as sent manually (use send reminder endpoint for email sending)

        **Use cases:**
        - Record that a first reminder was sent manually (e.g., via postal mail or other channels)
        - Update reminder status after external sending
        - Track reminder history

        **What is returned:**
        - Success response confirming the first reminder was marked as sent
      operationId: InvoicesController_markFirstReminderAsSent
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: First reminder marked as sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invoice cannot have reminder marked as sent
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Mark first reminder as sent
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/{id}/mark-second-reminder-as-sent:
    post:
      description: |-
        Marks the second reminder for an invoice as sent to the customer. This records the second reminder date and contact information.

        **What happens when you mark a second reminder as sent:**
        1. Second reminder date (`secondReminderAt`) is recorded as the current date
        2. Second reminder type is set to "Manual" (indicating manual marking, not email sending)
        3. Contact recipient is recorded (from invoice contactId)
        4. User who marked it as sent is recorded

        **Important restrictions:**
        - Only invoices with status "New" or "Sent" can have reminders marked as sent
        - The invoice must be overdue (past its due date) to send reminders
        - A first reminder should typically be sent before a second reminder
        - This endpoint marks the reminder as sent manually (use send reminder endpoint for email sending)

        **Use cases:**
        - Record that a second reminder was sent manually (e.g., via postal mail or other channels)
        - Update reminder status after external sending
        - Track reminder history

        **What is returned:**
        - Success response confirming the second reminder was marked as sent
      operationId: InvoicesController_markSecondReminderAsSent
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Second reminder marked as sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invoice cannot have reminder marked as sent
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Mark second reminder as sent
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/setInvoiceContactId:
    post:
      description: |-
        Updates the contact ID for an existing invoice. The contact must be a valid organization member from the invoice's project customer organization.

        **What happens when you set an invoice contact:**
        1. The invoice's contactId field is updated
        2. The contact must belong to the project's customer organization
        3. If contactId is set to null, the contact is cleared
        4. The contact will receive the invoice when it is sent via email

        **Important restrictions:**
        - The contact must be a valid organization member from the project's customer organization
        - Use GET /billing/invoices/invoiceContacts/{projectId} to get available contacts
        - The contact must have a valid email address to receive invoices

        **Use cases:**
        - Set or change the invoice recipient before sending
        - Clear the contact if needed
        - Update contact after invoice creation

        **What is returned:**
        - Success response confirming the contact was set
      operationId: InvoicesController_setInvoiceContactId
      parameters: []
      requestBody:
        required: true
        description: Invoice ID and contact ID to set
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetInvoiceContactIdDto'
      responses:
        '200':
          description: Invoice contact set successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid invoice ID or contact ID
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found or contact not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Set invoice contact ID
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/sendInvoice:
    post:
      description: |-
        Sends an invoice to the customer via email as a PDF attachment. The invoice must have a contact with a valid email address set.

        **What happens when you send an invoice:**
        1. Invoice PDF document is generated
        2. Invoice status changes from "New" to "Sent"
        3. Sending date (`sentAt`) is recorded as the current date
        4. Sending type is set to "Email" (indicating email sending)
        5. Contact recipient is recorded (from invoice contactId)
        6. Payment due date (`dueDate`) is calculated based on:
           - Organization-specific payment terms (`invoiceDueDays`), or
           - Workspace default payment terms (`defaultInvoiceDueDays`)
        7. Status change timestamp and user are recorded
        8. Email is sent to the contact's email address with PDF attachment
        9. The payment term starts running from this point

        **Important restrictions:**
        - Only invoices with status "New" or "Sent" can be sent
        - Invoices that are "Paid" or "Cancelled" cannot be sent
        - The invoice must have a contact with a valid email address
        - Use POST /billing/setInvoiceContactId to set the contact if needed

        **Use cases:**
        - Send invoice to customer via email
        - Automate invoice delivery
        - Track invoice sending and payment terms

        **What is returned:**
        - Success response confirming the invoice was sent
      operationId: InvoicesController_sendInvoice
      parameters: []
      requestBody:
        required: true
        description: Invoice ID to send
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendInvoiceDto'
      responses:
        '200':
          description: Invoice sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invoice cannot be sent (only invoices with status "New" or "Sent" can be sent, or contact email is missing)
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Send invoice via email
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/{id}/settings:
    get:
      description: |-
        Returns invoice-specific settings including language, reminder fees, interest rates, and custom text templates. Invoice settings can override organization or workspace defaults.

        **What is returned:**
        - Invoice-specific settings (language, reminder fees, interest rates)
        - Parent settings (from organization or workspace) for reference
        - Custom text templates (returned as HTML) for:
          - Closing salutation (withBestRegards)
          - Invoice greeting
          - Invoice footer
          - Reminder fee warning
          - First and second reminder greetings and footers
          - Cancellation body

        **Settings hierarchy:**
        Invoice settings override organization settings, which override workspace defaults. If a setting is null at the invoice level, the parent setting is used.
      operationId: InvoicesController_getInvoiceSettings
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
      responses:
        '200':
          description: Successfully retrieved invoice settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvoiceSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get invoice settings
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Partially updates invoice-specific settings including language, reminder fees, interest rates, and custom text templates. Only provided fields are updated; omitted fields remain unchanged.

        **What can be updated:**
        - Invoice language
        - Reminder fee settings (enable/disable, amount)
        - Interest settings (enable/disable, base rate, rate)
        - Custom text templates (accepts HTML or Markdown)

        **Settings behavior:**
        - Setting a value to `null` will use the parent setting (organization or workspace default)
        - Custom texts can be provided as HTML or Markdown and will be converted to internal format
        - Empty custom texts are treated as null (uses defaults)

        **Custom text templates:**
        All custom text fields support invoice variables (e.g., `#invoiceNumber`, `#projectName`, `#clientCompanyName`). See the invoice text variables documentation for a complete list of available variables.

        **What is returned:**
        Updated invoice settings with custom texts returned as HTML.
      operationId: InvoicesController_updateInvoiceSettings
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        description: Invoice settings to update (all fields optional)
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchInvoiceSettingsDto'
      responses:
        '200':
          description: Invoice settings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvoiceSettingsResponseDto'
        '400':
          description: Invalid request data or validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update invoice settings
      tags:
        - billing
        - invoices
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
          - invoiceLanguage
          - enableInvoiceReminderFee
          - invoiceReminderFee
          - enableInvoiceInterest
          - baseInvoiceInterest
          - invoiceInterest
          - customTexts
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/invoices/{id}/default-texts:
    get:
      description: |-
        Returns default invoice text templates for a specific language. These are the default texts that will be used if no custom texts are set for the invoice.

        **How default texts are determined:**
        1. Workspace-level defaults for the specified language
        2. Organization-level defaults (if available)
        3. System defaults (translated to the specified language)

        **Language selection:**
        If no language is provided, the system uses:
        1. Invoice language (if set)
        2. Organization invoice language (if set)
        3. Workspace invoice language (if set)
        4. Workspace default language

        **What is returned:**
        All default text templates as HTML:
        - Closing salutation (withBestRegards)
        - Invoice greeting
        - Invoice footer
        - Reminder fee warning
        - First and second reminder greetings and footers
        - Cancellation body

        **Use cases:**
        - Preview default texts before customizing
        - Reset custom texts to defaults
        - Display default texts in UI for reference
      operationId: InvoicesController_getDefaultInvoiceTexts
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            format: uuid
            type: string
        - name: language
          required: false
          in: query
          description: Language code for default texts (e.g., "en", "de"). If not provided, uses invoice/organization/workspace language.
          schema:
            example: en
            type: string
      responses:
        '200':
          description: Successfully retrieved default invoice texts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DefaultInvoiceTextsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get default invoice texts
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/invoices/{id}/file-url:
    get:
      description: |-
        Returns a public URL to access the generated invoice file. The file is always regenerated to ensure it reflects the latest invoice settings and data. Files are stored with a hash-based key (invoice ID + file type + document type) to avoid creating duplicate files for the same invoice variant.

        **What is returned:**
        - Public URL that can be used to download or access the invoice file
        - Filename of the generated file

        **File types supported:**
        - `pdf`: PDF with embedded ZUGFeRD/XRechnung XML (hybrid PDF)
        - `pdf-without-invoice`: PDF without embedded XML
        - `xml-einvoice`: XML E-Invoice file (XRechnung format)

        **Document types supported:**
        - `invoice`: Standard invoice document
        - `cancel`: Cancellation/credit note document
        - `first-reminder`: First payment reminder
        - `second-reminder`: Second payment reminder

        **File storage behavior:**
        Files are stored with a hash-based key (invoice ID + file type + document type) to ensure the same file URL is returned for the same invoice variant. However, files are always regenerated to ensure they reflect the latest invoice settings and data. This ensures accuracy while maintaining consistent URLs.

        **Use cases:**
        - Generate invoice PDFs for customer portals
        - Download invoices for accounting purposes
        - Access invoice files programmatically
        - Share invoice URLs with customers or partners
      operationId: InvoicesController_getInvoiceFileUrl
      parameters:
        - name: id
          required: true
          in: path
          description: Invoice ID (UUID)
          schema:
            example: 123e4567-e89b-12d3-a456-426614174000
            type: string
        - name: fileType
          required: true
          in: query
          description: Type of file to generate
          schema:
            example: pdf
            type: string
            enum:
              - pdf
              - pdf-without-invoice
              - xml-einvoice
        - name: documentType
          required: true
          in: query
          description: Type of document to generate
          schema:
            example: invoice
            type: string
            enum:
              - invoice
              - cancel
              - first-reminder
              - second-reminder
      responses:
        '200':
          description: Invoice file URL retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvoiceFileUrlResponseDto'
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Invoice not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get invoice file URL
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/potential-invoices:
    get:
      description: |-
        **What are Potential Invoices?**
        Potential invoices represent billable items that are ready to be invoiced but have not been converted to actual invoices yet. These are invoice drafts that appear in the invoice review section, waiting to be checked, adjusted, and finalized into official invoice documents.

        **What gets included:**
        - Subscription billing rows that have passed their billing date
        - Time-based work (tasks with logged time) that has not been billed yet
        - Express quotations that have been accepted by customers
        - Products and components that are ready for billing
        - Manual positions and interim payments

        **What data is returned:**
        - List of all potential invoices grouped by project, organization, and billing period
        - Each potential invoice includes the billing period (dateFrom, dateTo), total net amount, and invoice type
        - Results are automatically filtered based on project access permissions
        - Each entry shows which organization and project it belongs to

        **Invoice Types:**
        - **Mixed**: Combined invoice with multiple billing types (time, subscriptions, products, components)
        - **Subscription**: Recurring subscription billing only (monthly or periodic fees)
        - **TimeBased**: Time-based work (logged hours on tasks) only
        - **ExpressQuotation**: Accepted express quotations only (quick add-ons from tickets)
        - **SingleProject**: Single project invoice (project-based billing)
        - **InterimPayment**: Interim or advance payment invoice

        **How it works:**
        The system automatically creates potential invoices when billable items become due. For example, completed tickets appear as time-based items, subscriptions generate entries on their billing dates, and accepted express quotations become billable immediately.

        **Permission requirements:**
        Requires Invoices.manage permission to view potential invoices. Results are scoped to projects you have access to.
      operationId: PotentialInvoicesController_getPotentialInvoices
      parameters: []
      responses:
        '200':
          description: Potential invoices retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PotentialInvoiceResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get potential invoices
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/potential-invoices/{id}/contacts:
    get:
      description: |-
        **What are Invoice Contacts?**
        Returns a list of organization members (contacts) from the customer organization who can be selected as invoice recipients. These contacts are the people who will receive the invoice when it is sent via email. You can set a contact for each potential invoice to specify who should receive the final invoice document.

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
        The projectId is extracted from the invoice ID to determine which organization is the customer, and then returns contacts from that organization.

        **What data is returned:**
        - List of organization members from the project customer organization
        - Each contact includes ID (UUID), full name, avatar file ID (if available), and email address
        - Contacts are sorted alphabetically by first name, then last name
        - Only active (non-deleted) organization members are returned

        **How to use:**
        1. Get the list of available contacts for a potential invoice
        2. Select a contact ID from the list
        3. Use the PATCH endpoint to set the contactId for the potential invoice
        4. When the invoice is finalized and sent, it will be emailed to this contact

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project to view contacts.
      operationId: PotentialInvoicesController_getInvoiceContacts
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
      responses:
        '200':
          description: Invoice contacts retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/InvoiceContactResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get available contacts for potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /billing/potential-invoices/{id}:
    get:
      description: |-
        **What are Potential Invoice Details?**
        Returns the complete breakdown of a specific potential invoice, showing all billable items that make up the total amount. This is the detailed view you see when reviewing an invoice before finalizing it. You can see exactly what is being billed, adjust quantities or amounts, and configure additional settings.

        **What is included in the details:**
        - **Subscription billing rows**: Recurring fees with quantities, unit prices, and payment types (fixed, per-unit, one-time)
        - **Time-based tasks**: Completed tickets with logged hours, estimated time, billed time, and hourly rates
        - **Express quotations**: Accepted quick quotes from tickets with items, quantities, and unit prices
        - **Products**: Items from the product catalog with options, quantities, discounts, and pricing
        - **Components**: Project components with time estimates, hourly rates, and nested items
        - **Support contingent charges**: Retainer plan charges for Support-type projects, based on allocated hours and pricing for billing periods
        - **Manual positions**: Custom billing items (third-party costs, flat fees, interim payments)
        - **Leadtime fee settings**: Configuration for adding the Leadtime service fee to the invoice

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
        - Type: One of Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment, SupportContingentCharge
        - ProjectId: UUID of the project
        - DateFrom/DateTo: Dates in YYYY-MM-DD format representing the billing period

        **What data is returned:**
        - Complete invoice structure with all billable items organized by type
        - Billing period (periodFrom, periodTo) and custom billed dates (billedFrom, billedTo)
        - Tax calculations (tax amount, reverse charge settings)
        - Contact information (contactId for invoice recipient)
        - Leadtime fee settings (percentage, method, task distribution, custom title)
        - Lists of skipped tasks and express quotations (items excluded from billing)
        - All items include detailed pricing, discounts, descriptions (as HTML), and metadata
        - Product and component descriptions are converted from ProseMirror IDoc format to HTML

        **How to use:**
        1. Get the list of potential invoices to find the invoice ID
        2. Use this endpoint to get full details of a specific invoice
        3. Review all billable items and their amounts
        4. Use the PATCH endpoint to adjust settings (billed dates, contact, etc.)
        5. Use other endpoints to skip items, add manual positions, or configure fees
        6. Once reviewed and adjusted, the invoice can be finalized into an actual invoice document

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project to view potential invoice details.
      operationId: PotentialInvoicesController_getPotentialInvoiceDetails
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
      responses:
        '200':
          description: Potential invoice details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PotentialInvoiceDetailsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get potential invoice details
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        **What can be updated:**
        This endpoint allows partial updates to potential invoice settings. Only the fields you provide in the request body will be updated; all other fields remain unchanged. This means you can update just one field, multiple fields, or all fields in a single request.

        **Updatable fields:**
        - **billedFrom**: Billed from date (ISO 8601 date string). Sets the start date for the billing period that will appear on the final invoice document. This can differ from the actual service period (periodFrom) if you want to show a different date range on the invoice.
        - **billedTo**: Billed to date (ISO 8601 date string). Sets the end date for the billing period that will appear on the final invoice document. This can differ from the actual service period (periodTo) if you want to show a different date range on the invoice.
        - **contactId**: Contact ID (organization member UUID). Sets the invoice recipient who will receive the invoice when it is sent via email. Set to null to clear the contact and remove the recipient assignment.

        **How partial updates work:**
        - If you only provide billedFrom, only that field will be updated
        - If you only provide contactId, only that field will be updated
        - You can provide any combination of the three fields
        - Fields you omit will remain unchanged

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Date format:**
        Dates can be provided in ISO 8601 format:
        - Date-only: "2025-10-01" (recommended)
        - ISO timestamp: "2025-10-01T00:00:00.000Z"

        **Contact validation:**
        When setting a contactId, the system validates that:
        - The contact exists and is active
        - The contact belongs to the project customer organization
        - If the contact is invalid, a NotFoundException is returned

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project to update potential invoice settings.
      operationId: PotentialInvoicesController_patchPotentialInvoice
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchPotentialInvoiceDto'
      responses:
        '200':
          description: Potential invoice updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update potential invoice settings
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/reset:
    post:
      description: |-
        **What does reset do?**
        Resets all customizations and metadata for a potential invoice, returning it to its default state. This clears all manual adjustments you have made, so the invoice will show the original billable items as they were automatically calculated by the system.

        **What gets cleared:**
        - Billed from and billed to dates (reverts to default billing period)
        - Contact ID (removes invoice recipient)
        - Subscription quantities and order (reverts to default quantities)
        - Custom manual positions (removes any manually added items)
        - Skipped tasks and express quotations (includes all items back in the invoice)
        - Leadtime fee settings (reverts to default configuration)

        **When to use:**
        - When you want to start fresh with default invoice settings
        - After making mistakes in invoice configuration and want to undo all changes
        - To clear all customizations before creating a new invoice from scratch
        - When you need to see the original calculated amounts without any adjustments

        **Important notes:**
        - This operation cannot be undone
        - The invoice will revert to showing all automatically calculated billable items
        - Any custom manual positions you added will be removed
        - The invoice will use default billing period dates

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project to reset potential invoice.
      operationId: PotentialInvoicesController_resetPotentialInvoice
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
      responses:
        '200':
          description: Potential invoice reset successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reset potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/tasks/{taskId}/toggle-skipped:
    post:
      description: |-
        **What does this do?**
        Toggles whether a task is skipped (excluded) from the potential invoice. When a task is skipped, it is added to the skipped tasks list and will not appear on the final invoice. Toggling again removes it from the skipped list and includes it back in the invoice.

        **When to use:**
        - To exclude a task from billing without permanently marking it as "do not bill"
        - To temporarily remove a task from an invoice during review
        - To include a previously skipped task back in the invoice

        **How it works:**
        - First call: Adds task to skipped tasks list (excludes from invoice)
        - Second call: Removes task from skipped tasks list (includes in invoice)
        - The task itself is not modified, only the invoice metadata is updated

        **Task requirements:**
        - Task must belong to the potential invoice's project
        - Task must not be already billed (billed: false)
        - User must have access to the project

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_toggleSkippedTask
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
        - name: taskId
          required: true
          in: path
          description: Task ID (UUID) to toggle skipped status for
          schema:
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      responses:
        '200':
          description: Task skipped status toggled successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice or task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Toggle task skipped status
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/tasks/{taskId}/billed-time:
    post:
      description: |-
        **What does this do?**
        Sets the billed time (hours) for a task in a potential invoice. The billed time is the amount of time that will be charged to the customer, which may differ from the actual time spent on the task.

        **When to use:**
        - To adjust the billed hours up or down from the actual time spent
        - To bill the estimated time instead of actual time
        - To correct billing amounts before finalizing the invoice

        **How it works:**
        - Updates the task's billedTime field directly
        - The billed time is used to calculate the total charge (billedTime × hourly rate)
        - Can be set to any non-negative number
        - Setting a different value than spent time will show a difference in the invoice details

        **Task requirements:**
        - Task must belong to the potential invoice's project
        - Task must not be already billed (billed: false)
        - User must have access to the project

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_setTaskBilledTime
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
        - name: taskId
          required: true
          in: path
          description: Task ID (UUID) to set billed time for
          schema:
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetTaskBilledTimeDto'
      responses:
        '200':
          description: Task billed time set successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice or task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Set task billed time
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/tasks/{taskId}/details:
    post:
      description: |-
        **What does this do?**
        Sets both the billed time and billed title for a task in a potential invoice. This allows you to customize how the task appears on the invoice, including adjusting the time charged and providing a more customer-friendly title.

        **When to use:**
        - To adjust both the billed hours and the task title in one operation
        - To customize how tasks appear on invoices for better customer clarity
        - To correct billing details before finalizing the invoice

        **How it works:**
        - Updates both task.billedTime and task.billedTitle fields
        - Both fields are optional - you can update just one or both
        - billedTime can be set to null to clear it (revert to using spent time)
        - billedTitle can be set to null to use the original task title
        - The billed time is used to calculate the total charge (billedTime × hourly rate)

        **Task requirements:**
        - Task must belong to the potential invoice's project
        - Task must not be already billed (billed: false)
        - User must have access to the project

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_setTaskDetails
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
        - name: taskId
          required: true
          in: path
          description: Task ID (UUID) to update details for
          schema:
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetTaskDetailsDto'
      responses:
        '200':
          description: Task details updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice or task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Set task billing details
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/tasks/{taskId}/skip-from-billing:
    post:
      description: |-
        **What does this do?**
        Sets the skipFromBilling flag on a task, which permanently marks the task as excluded from billing. Unlike toggling skipped status (which only affects the current invoice), this flag prevents the task from appearing in any future potential invoices.

        **When to use:**
        - To permanently exclude a task from billing (e.g., internal work, non-billable tasks)
        - To mark tasks that should never be invoiced
        - To undo a previous "skip from billing" decision (set to false)

        **How it works:**
        - Updates the task.skipFromBilling field directly on the task
        - When set to true, the task will not appear in any potential invoices
        - When set to false, the task can be included in invoices again
        - This is different from toggling skipped status, which only affects the current invoice metadata

        **Task requirements:**
        - Task must belong to the potential invoice's project
        - Task must not be already billed (billed: false)
        - User must have access to the project

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_skipFromBilling
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
        - name: taskId
          required: true
          in: path
          description: Task ID (UUID) to set skip from billing flag for
          schema:
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SkipFromBillingDto'
      responses:
        '200':
          description: Task skip from billing flag set successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice or task not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Set task skip from billing flag
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/manual-positions:
    post:
      description: |-
        **What does this do?**
        Adds a custom manual position (line item) to a potential invoice. Manual positions allow you to bill for external costs, flat fees, or other items that aren't automatically created by tickets, products, or subscriptions.

        **When to use:**
        - To add external costs (e.g., translation services, license costs)
        - To add travel expenses or other one-time charges
        - To add flat rates with no ticket reference
        - To add items that were missing from the original offer

        **How it works:**
        - Creates a new manual position entry stored in the potential invoice metadata
        - The position appears immediately in the invoice preview
        - Manual positions are fully integrated into tax and total calculations
        - Only custom manual positions (type: manual) can be added, updated, or deleted
        - Project manual positions (from project snapshots) are read-only and cannot be modified

        **Position requirements:**
        - Must provide a unique UUID for the position ID
        - Title and price are required
        - Price must be a positive number (net amount before tax)

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_addManualPosition
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddManualPositionDto'
      responses:
        '200':
          description: Manual position added successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Add manual position to potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/manual-positions/{positionId}:
    patch:
      description: |-
        **What does this do?**
        Updates an existing custom manual position in a potential invoice. You can modify the title and/or price of the position.

        **When to use:**
        - To correct the title or price of a manual position
        - To adjust pricing before finalizing the invoice
        - To update position details after initial creation

        **How it works:**
        - Updates the manual position stored in the potential invoice metadata
        - Only custom manual positions (type: manual) can be updated
        - Project manual positions (from project snapshots) are read-only and cannot be modified
        - Both title and price must be provided (even if only one is changing)

        **Position requirements:**
        - Position must exist in the potential invoice
        - Position must be a custom manual position (not a project position)
        - Title and price are required

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_updateManualPosition
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
        - name: positionId
          required: true
          in: path
          description: Manual position ID (UUID) to update
          schema:
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateManualPositionDto'
      responses:
        '200':
          description: Manual position updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice or manual position not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update manual position in potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What does this do?**
        Removes a custom manual position from a potential invoice. The position is permanently removed from the invoice and will no longer appear in the invoice preview or be included in calculations.

        **When to use:**
        - To remove a manual position that was added by mistake
        - To remove a position that is no longer needed
        - To clean up the invoice before finalizing

        **How it works:**
        - Removes the manual position from the potential invoice metadata
        - Only custom manual positions (type: manual) can be deleted
        - Project manual positions (from project snapshots) are read-only and cannot be deleted
        - The position is immediately removed from the invoice preview

        **Position requirements:**
        - Position must exist in the potential invoice
        - Position must be a custom manual position (not a project position)

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_deleteManualPosition
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
        - name: positionId
          required: true
          in: path
          description: Manual position ID (UUID) to delete
          schema:
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      responses:
        '200':
          description: Manual position deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice or manual position not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete manual position from potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/manual-positions/sort:
    post:
      description: |-
        **What does this do?**
        Reorders the manual positions in a potential invoice. The positions will appear in the order specified by the items array.

        **When to use:**
        - To change the display order of manual positions on the invoice
        - To group related positions together
        - To organize positions in a logical sequence

        **How it works:**
        - Updates the sort order of manual positions stored in the potential invoice metadata
        - Only custom manual positions (type: manual) can be reordered
        - Project manual positions (from project snapshots) maintain their original order
        - The items array should contain all position IDs in the desired order
        - Positions not included in the array will be placed at the end

        **Position requirements:**
        - All position IDs in the items array must exist in the potential invoice
        - Only custom manual positions can be reordered
        - The array can include a subset of positions (others will be placed at the end)

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_saveManualPositionsSort
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveManualPositionsSortDto'
      responses:
        '200':
          description: Manual positions reordered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder manual positions in potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/subscriptions/sort:
    post:
      description: |-
        **What does this do?**
        Reorders the subscription billing rows in a potential invoice. The subscriptions will appear on the invoice in the order specified by the items array.

        **When to use:**
        - To change the display order of subscription billing rows on the invoice
        - To group related subscriptions together
        - To organize subscriptions in a logical sequence (e.g., by priority or billing type)

        **How it works:**
        - Updates the sort order of subscription billing rows stored in the potential invoice metadata
        - The items array should contain all subscription billing IDs (billingId) in the desired order
        - Subscriptions not included in the array will maintain their original order
        - The order affects how subscriptions appear on the final invoice document

        **Subscription requirements:**
        - All subscription billing IDs in the items array must exist in the potential invoice
        - Use the billingId field from subscription billing rows (not the id field)
        - The array can include a subset of subscriptions (others will maintain their order)

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_saveSubscriptionSort
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveSubscriptionSortDto'
      responses:
        '200':
          description: Subscriptions reordered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder subscriptions in potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /billing/potential-invoices/{id}/subscriptions/{itemId}/quantity:
    post:
      description: |-
        **What does this do?**
        Sets a custom quantity for a subscription billing row in a potential invoice. For per-unit pricing subscriptions, this allows you to adjust how many units are billed, which may differ from the default quantity calculated by the system.

        **When to use:**
        - To adjust the quantity for per-unit pricing subscriptions before finalizing the invoice
        - To bill a different number of units than the default calculated amount
        - To correct billing quantities based on actual usage or customer agreements
        - To clear a custom quantity and revert to the default calculated quantity

        **How it works:**
        - Updates the subscription quantity stored in the potential invoice metadata
        - The quantity is used to calculate the total charge (quantity × unit price)
        - Setting value to null or omitting it clears the custom quantity and reverts to default
        - The quantity must be a non-negative integer
        - Only affects the current potential invoice; does not modify the subscription billing configuration

        **Subscription requirements:**
        - Subscription billing ID (itemId) must exist in the potential invoice
        - Use the billingId field from subscription billing rows
        - Only per-unit pricing subscriptions (payment type: perUnit) typically use quantities
        - Fixed monthly fees and one-time charges may not use quantities

        **Invoice ID Format:**
        The invoice ID follows the format: {type}::{projectId}::{dateFrom}::{dateTo}
        - Example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31

        **Permission requirements:**
        Requires Invoices.manage permission and access to the project.
      operationId: PotentialInvoicesController_changeQuantity
      parameters:
        - name: id
          required: true
          in: path
          description: 'Potential invoice ID in format: {type}::{projectId}::{dateFrom}::{dateTo}'
          schema:
            example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
            type: string
        - name: itemId
          required: true
          in: path
          description: Subscription billing ID (billingId UUID) to change quantity for
          schema:
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChangeQuantityDto'
      responses:
        '200':
          description: Subscription quantity updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: User lacks Invoices.manage permission or project access
        '404':
          description: Potential invoice or subscription billing not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Change subscription quantity in potential invoice
      tags:
        - billing
        - invoices
      x-required-permissions:
        - invoices.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /helpdesk/settings:
    get:
      description: |-
        Retrieves workspace-wide helpdesk settings including default configurations and email account settings.

        **What is Helpdesk?**
        Helpdesk is an automated email-to-task conversion system that transforms incoming emails into tasks. When enabled, the system automatically:
        - Pulls emails from configured email accounts (IMAP, Gmail, Microsoft Graph)
        - Converts emails into tasks with proper assignment, priority, and categorization
        - Sends automatic replies to new senders (if configured)
        - Links emails to tasks for full conversation history

        **What is returned:**
        - **Workspace settings**: Global helpdesk configuration including:
          - `isHelpdeskEnabled`: Whether helpdesk is enabled for the workspace
          - `defaultSettings`: Default task creation settings (project, type, status, priority, assignment)
          - `defaultReplyBody`: HTML template for automatic replies (supports variables like `taskNumber`)
          - `sendDefaultReply`: Whether to automatically send replies to new senders
        - **Email account settings**: List of all email accounts with their helpdesk status:
          - Each account shows if helpdesk is enabled
          - Account-specific overrides that override workspace defaults
          - Settings include task defaults, auto-reply configuration, and assignment rules

        **Settings inheritance:**
        Email accounts can override workspace defaults. When an account has its own settings:
        - Non-null fields override workspace defaults
        - Null fields inherit from workspace defaults
        - `sendDefaultReply` supports 3-state inheritance: `null` (use default), `true` (enabled), `false` (disabled)

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission.
      operationId: HelpdeskController_getHelpdeskSettings
      parameters: []
      responses:
        '200':
          description: Workspace helpdesk settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HelpdeskSettingsApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get workspace helpdesk settings
      tags:
        - helpdesk
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates workspace-wide default helpdesk settings. These settings apply to all email accounts unless overridden at the account level.

        **What can be configured:**
        - **Default task priority**: Priority level for all helpdesk tasks (Urgent, High, Normal, Low)
        - **Default task type**: Task type ID that will be assigned to helpdesk tasks
        - **Default task status**: Task status ID for newly created helpdesk tasks
        - **Auto-assign user**: Optional user ID to automatically assign tasks to
        - **Default reply body**: HTML template for automatic replies (supports variables like `taskNumber`)
        - **Send default reply**: Whether to automatically send replies to new email senders

        **How it works:**
        - These settings become the base configuration for all helpdesk operations
        - Individual email accounts can override any of these defaults
        - When an email account has no specific settings, it uses these workspace defaults
        - The `isHelpdeskEnabled` flag is preserved from current settings (not updated via this endpoint)

        **Auto-reply system:**
        When `sendDefaultReply` is enabled, the system automatically sends replies to initial emails from new senders:
        - Only triggers for first-time senders (not replies to existing conversations)
        - Uses the `defaultReplyBody` template with variable substitution
        - Variables like `taskNumber` are replaced with actual values
        - Replies are sent from the same email account that received the original email

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect all email accounts that don't have account-specific overrides.
      operationId: HelpdeskController_updateHelpdeskSettings
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: workspace
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateHelpdeskSettingsDto'
      responses:
        '200':
          description: Workspace helpdesk settings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HelpdeskSettingsApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update workspace helpdesk settings
      tags:
        - helpdesk
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - workspace
        allowedFields:
          - workspace
          - emailAccounts
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /helpdesk/email-accounts:
    get:
      description: |-
        Retrieves all email accounts in the workspace with their helpdesk enabled status and settings.

        **What is returned:**
        A list of all email accounts configured in the workspace, each containing:
        - **Account identification**: ID, email address, and display name
        - **Helpdesk status**: Whether helpdesk is enabled for this account
        - **Account settings**: If enabled, includes all account-specific helpdesk settings:
          - Task defaults (project, type, status, priority, assignment)
          - Auto-reply configuration
          - Any overrides that differ from workspace defaults

        **Use cases:**
        - View which email accounts have helpdesk enabled
        - Check account-specific settings and overrides
        - Identify accounts that are using workspace defaults vs. custom settings

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission.
      operationId: HelpdeskController_getEmailAccounts
      parameters: []
      responses:
        '200':
          description: Email accounts retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EmailAccountHelpdeskStatusResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get email accounts with helpdesk status
      tags:
        - helpdesk
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /helpdesk/email-accounts/{accountId}/settings:
    get:
      description: |-
        Retrieves helpdesk settings for a specific email account, including any account-level overrides.

        **What is returned:**
        - **Account information**: ID, email address, display name
        - **Helpdesk status**: Whether helpdesk is enabled for this account
        - **Account settings**: If enabled, returns all account-specific settings:
          - Task defaults that override workspace defaults
          - Auto-reply configuration (with 3-state inheritance support)
          - Assignment rules
          - Any other account-level overrides

        **Settings inheritance:**
        If the account has custom settings:
        - Non-null fields override workspace defaults
        - Null fields inherit from workspace defaults
        - The response shows the effective settings (merged defaults + overrides)

        **Error handling:**
        Returns 404 if the email account is not found or doesn't belong to the workspace.

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission.
      operationId: HelpdeskController_getEmailAccountSettings
      parameters:
        - name: accountId
          required: true
          in: path
          description: Email account ID
          schema:
            example: account-uuid-123
            type: string
      responses:
        '200':
          description: Email account settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountHelpdeskStatusResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get specific email account helpdesk settings
      tags:
        - helpdesk
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates helpdesk settings for a specific email account. These settings override workspace defaults. Omitted fields inherit from workspace defaults.

        **What can be configured:**
        All fields are optional - only provided fields will override workspace defaults:
        - **Task priority**: Override default priority for this account
        - **Task type**: Override default task type ID
        - **Task status**: Override default task status ID
        - **Auto-assign user**: Override default assignment (set to `null` to remove override)
        - **Default reply body**: Override auto-reply template (supports variables like `taskNumber`)
        - **Send default reply**: 3-state control - `null` (inherit), `true` (enabled), `false` (disabled)

        **How inheritance works:**
        - Providing a value overrides the workspace default
        - Omitting a field (or setting to `null` for some fields) inherits from workspace defaults
        - `sendDefaultReply` has special 3-state behavior: `null` = use workspace default, `true`/`false` = override
        - Updating settings automatically enables helpdesk for the account (`isEnabled: true`)

        **Example scenarios:**
        - Set only `defaultTaskPriority: High` → Account uses High priority, all other settings from workspace
        - Set `sendDefaultReply: false` → Disables auto-reply for this account only
        - Set `sendDefaultReply: null` → Account inherits workspace auto-reply setting

        **Error handling:**
        Returns 404 if the email account is not found or doesn't belong to the workspace.

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission.
      operationId: HelpdeskController_updateEmailAccountSettings
      parameters:
        - name: accountId
          required: true
          in: path
          description: Email account ID
          schema:
            example: account-uuid-123
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,email,helpdeskEnabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEmailAccountHelpdeskSettingsDto'
      responses:
        '200':
          description: Email account settings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountHelpdeskStatusResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update email account helpdesk settings
      tags:
        - helpdesk
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - email
          - helpdeskEnabled
        allowedFields:
          - id
          - email
          - displayName
          - helpdeskEnabled
          - settings
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /helpdesk/email-accounts/{accountId}/enable:
    post:
      description: |-
        Enables helpdesk functionality for a specific email account. The account will use workspace default settings unless account-level overrides are configured.

        **What this does:**
        - Enables helpdesk processing for the specified email account
        - The account will start receiving and processing emails automatically
        - Uses workspace default settings if no account-specific overrides exist
        - Preserves any existing account-level settings if they were previously configured

        **How it works:**
        1. The system verifies the email account exists and belongs to the workspace
        2. Sets `isEnabled: true` for the account's helpdesk settings
        3. If no account settings exist, they are created with workspace defaults
        4. If account settings exist, only the `isEnabled` flag is updated
        5. The account becomes eligible for automated email pulling via cron jobs

        **After enabling:**
        - Emails sent to this account will be automatically pulled (every minute via cron)
        - Incoming emails will be converted to tasks using workspace defaults (or account overrides)
        - Auto-replies will be sent if configured in workspace or account settings
        - Tasks will be created with proper assignment, priority, and categorization

        **Error handling:**
        Returns 404 if the email account is not found or doesn't belong to the workspace.

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. Workspace helpdesk must be enabled (`isHelpdeskEnabled: true`) for this to work.
      operationId: HelpdeskController_enableHelpdeskForAccount
      parameters:
        - name: accountId
          required: true
          in: path
          description: Email account ID
          schema:
            example: account-uuid-123
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,email,helpdeskEnabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: Helpdesk enabled for email account successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountHelpdeskStatusResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Enable helpdesk for email account
      tags:
        - helpdesk
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - email
          - helpdeskEnabled
        allowedFields:
          - id
          - email
          - displayName
          - helpdeskEnabled
          - settings
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /helpdesk/email-accounts/{accountId}/disable:
    delete:
      description: |-
        Disables helpdesk functionality for a specific email account. Any account-level override settings are preserved but not applied until re-enabled.

        **What this does:**
        - Disables helpdesk processing for the specified email account
        - Stops automatic email pulling and task creation for this account
        - Preserves all account-specific settings for future re-enabling
        - The account settings remain in the database but are not used

        **How it works:**
        1. The system verifies the email account exists and belongs to the workspace
        2. Sets `isEnabled: false` for the account's helpdesk settings
        3. All account-level overrides are preserved (task defaults, auto-reply, etc.)
        4. The account is removed from automated email pulling cron jobs

        **After disabling:**
        - No new emails will be pulled from this account
        - No tasks will be created from emails sent to this account
        - Existing tasks and email history remain unchanged
        - Settings can be restored by re-enabling the account

        **Use cases:**
        - Temporarily disable helpdesk for an account (e.g., during maintenance)
        - Stop processing emails for a specific account without losing configuration
        - Test different configurations by disabling/enabling accounts

        **Error handling:**
        Returns 404 if the email account is not found or doesn't belong to the workspace.

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission.
      operationId: HelpdeskController_disableHelpdeskForAccount
      parameters:
        - name: accountId
          required: true
          in: path
          description: Email account ID
          schema:
            example: account-uuid-123
            type: string
      responses:
        '200':
          description: Helpdesk disabled for email account successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAccountHelpdeskStatusResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Email account not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Disable helpdesk for email account
      tags:
        - helpdesk
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /helpdesk/organization-settings:
    get:
      description: |-
        **This endpoint is deprecated.** Use `GET /helpdesk/organizations/:organizationId/settings` instead.

        This endpoint was designed to retrieve organization-specific helpdesk settings, but requires an organization ID to function properly. Please use the endpoint with the organization ID in the path parameter.
      operationId: HelpdeskController_getOrganizationHelpdeskSettings
      parameters: []
      responses:
        '200':
          description: Organization helpdesk settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationHelpdeskSettingsApiResponse'
        '400':
          description: Organization ID is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization helpdesk settings (deprecated)
      tags:
        - helpdesk
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /helpdesk/organizations/{organizationId}/settings:
    get:
      description: |-
        Retrieves organization-specific helpdesk settings for a given organization.

        **What is Organization Helpdesk?**
        Organizations can have their own helpdesk settings that override workspace defaults. This allows different organizations to have different task routing, priorities, and auto-reply configurations when emails are sent to the same helpdesk email account.

        **Settings priority (highest to lowest):**
        1. **Organization settings with mode='all'**: Override everything for all senders
        2. **Organization settings for member**: Apply when sender is an organization member (mode='onlyMembers')
        3. **Workspace email account settings**: Account-specific overrides at workspace level
        4. **Workspace defaults**: Base configuration for all helpdesk operations

        **What is returned:**
        - **Organization settings**: Organization-level configuration including:
          - Organization ID and name
          - Email account settings with organization-specific overrides
          - Organization mode (`onlyMembers` or `all`)
          - Task defaults, auto-reply configuration, and assignment rules
        - **Email account settings**: List of email accounts with organization-level settings:
          - Each account shows organization-specific overrides
          - Includes effective defaults (merged from workspace + organization settings)
          - Shows which settings override workspace defaults

        **Organization modes:**
        - **`onlyMembers`**: Settings apply only when the email sender is a member of this organization
        - **`all`**: Settings apply to ALL emails sent to the account, regardless of sender

        **Error handling:**
        Returns 404 if the organization is not found or doesn't belong to the workspace.

        **Note:** This endpoint requires the `Organizations.edit` permission.
      operationId: HelpdeskController_getOrganizationHelpdeskSettingsById
      parameters:
        - name: organizationId
          required: true
          in: path
          description: Organization ID
          schema:
            example: org-uuid-123
            type: string
      responses:
        '200':
          description: Organization helpdesk settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationHelpdeskSettingsApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Organization not found
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization helpdesk settings
      tags:
        - helpdesk
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates organization-specific helpdesk settings. These settings override workspace defaults for the specified organization.

        **What can be configured:**
        - **Organization mode**: How settings apply (`onlyMembers` or `all`)
          - `onlyMembers`: Settings apply only when sender is a member of this organization
          - `all`: Settings apply to ALL emails sent to the account, regardless of sender
        - **Default task priority**: Priority level for organization helpdesk tasks
        - **Default task type**: Task type ID for organization helpdesk tasks
        - **Default task status**: Task status ID for organization helpdesk tasks
        - **Auto-assign user**: Optional user ID to automatically assign tasks to
        - **Default reply body**: HTML template for automatic replies (supports variables like `taskNumber`)
        - **Send default reply**: Whether to automatically send replies to new email senders

        **How it works:**
        1. Organization settings override workspace defaults when emails are processed
        2. Settings apply based on the organization mode:
           - `onlyMembers`: Only when sender is an organization member
           - `all`: For all emails to the account (highest priority)
        3. Null fields inherit from workspace email account settings or workspace defaults
        4. Multiple organizations can have settings for the same email account

        **Settings resolution example:**
        If an email is sent to `support@company.com`:
        - First check: Any org with mode='all'? → Use that org's settings
        - Second check: Is sender a member? → Use their org's settings (if mode='onlyMembers')
        - Third check: Workspace email account settings
        - Final fallback: Workspace defaults

        **Error handling:**
        Returns 404 if the organization is not found or doesn't belong to the workspace.

        **Note:** This endpoint requires the `Organizations.edit` permission. Organization settings provide fine-grained control over helpdesk behavior per organization.
      operationId: HelpdeskController_updateOrganizationHelpdeskSettings
      parameters:
        - name: organizationId
          required: true
          in: path
          description: Organization ID
          schema:
            example: org-uuid-123
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: settings
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOrganizationHelpdeskSettingsDto'
      responses:
        '200':
          description: Organization helpdesk settings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationHelpdeskSettingsApiResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Organization not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization helpdesk settings
      tags:
        - helpdesk
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - settings
        allowedFields:
          - settings
          - emailAccounts
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/appearance-settings:
    get:
      description: |-
        Retrieves the current workspace appearance settings.

        **What is returned:**
        - **Main workspace color**: The primary color theme used throughout the workspace
        - **Logo URL**: Public URL for the workspace logo (shown in light theme)
        - **Logo dark theme URL**: Public URL for the workspace logo used in dark theme

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. Appearance settings affect how the workspace looks to all users in the workspace.
      operationId: AppearanceSettingsController_getAppearanceSettings
      parameters: []
      responses:
        '200':
          description: Appearance settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppearanceSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get workspace appearance settings
      tags:
        - administration
        - appearance-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates workspace appearance settings. All fields are optional - only provided fields will be updated.

        **What can be updated:**
        - **Main workspace color**: Changes the primary color theme used throughout the workspace
        - **Logo**: Upload a logo file for light theme display
        - **Logo dark theme**: Upload a separate logo file optimized for dark theme display

        **To upload logo files:**
        1. Call POST /api/public/workspace/upload to upload each logo file
        2. Get the file ID from the response
        3. Use that file ID in the `logoId` or `logoDarkThemeId` field
        4. Set to `null` to remove an existing logo

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect how the workspace appears to all users. The system validates that provided file IDs exist and belong to the workspace.
      operationId: AppearanceSettingsController_updateAppearanceSettings
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchAppearanceSettingsDto'
      responses:
        '200':
          description: Appearance settings updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: File not found - Invalid logoId or logoDarkThemeId provided
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update workspace appearance settings
      tags:
        - administration
        - appearance-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/basic-settings:
    get:
      description: |-
        Retrieves the current basic workspace settings.

        **What is returned:**
        - **Workspace identification**: ID and brand color
        - **Localization**: Default language, country of operation, timezone
        - **Attendance tracking**: Whether attendance is enabled, color thresholds for time tracking quality
        - **Default working hours**: Default start and end times for the workspace
        - **Time tracker**: Reminder interval settings
        - **Sprint configuration**: Sprint duration, start day, and end day
        - **Journal notifications**: User IDs who receive notifications for journal entries

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. These settings affect how the entire workspace operates and are used as defaults throughout the system.
      operationId: BasicSettingsController_getBasicSettings
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetBasicSettingsDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get workspace basic settings
      tags:
        - administration
        - basic-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates workspace basic settings. All fields are optional - only provided fields will be updated.

        **What can be updated:**
        - **Brand color**: Hex color code for workspace branding
        - **Default language**: Default language for the workspace (e.g., "en", "de")
        - **Country of operation**: ISO country code (e.g., "US", "DE")
        - **Timezone**: IANA timezone identifier (e.g., "America/New_York", "Europe/Berlin")
        - **Attendance tracking**: Enable or disable attendance tracking for employees
        - **Time tracking thresholds**: Percentage thresholds for marking time tracking as "good" or "bad"
        - **Default working hours**: Default start and end times for employees
        - **Time tracker reminders**: Interval in minutes for reminding users to track time (5-120 minutes)
        - **Sprint configuration**: Sprint duration in days, start day (1=Monday), and end day
        - **Journal notifications**: List of user IDs who should receive notifications for journal entries

        **Validation rules:**
        - `timeColorBadPercent` must be less than `timeColorGoodPercent`
        - `defaultTimeTo` must be after `defaultTimeFrom`
        - `timeTrackerAnnoyanceInMin` must be between 5 and 120
        - `sprintStartDay` and `sprintEndDay` must be between 1 (Monday) and 7 (Sunday)

        **Note:** This endpoint requires the `WsSettings.manageSettings` permission. Changes affect the entire workspace and all users.
      operationId: BasicSettingsController_patchBasicSettings
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchBasicSettingsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update workspace basic settings
      tags:
        - administration
        - basic-settings
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /holidays/settings:
    get:
      description: |-
        Retrieves the workspace holiday configuration, including the default holiday country/region and all available holiday sources.

        **What are Holiday Settings?**
        Holiday settings determine which regional holiday rules the system uses for capacity planning. Leadtime needs to know when employees are not available due to regional holidays, which is essential for accurate capacity management.

        **What is returned:**
        - `defaultHolidayCode`: The default holiday country/region code for the workspace (e.g., "US", "DE", "US-CA")
        - `availableCountries`: List of all available holiday countries and states with their codes and localized names

        **How it works:**
        - The default holiday code is automatically applied to all new employees
        - Individual employees can have different holiday rules set in their profile
        - Holidays are displayed in the team calendar and used for capacity calculations
        - Use the `language` query parameter to get country names in a specific language (default: "en")

        **Note:** This endpoint requires the `manageSettings` permission.
      operationId: HolidaysController_getHolidaySettings
      parameters:
        - name: language
          required: true
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Holiday settings retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HolidaySettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get holiday settings
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates the default holiday country/region code for the workspace.

        **What this does:**
        Sets the default holiday source that will be automatically applied to all new employees. This ensures consistent holiday rules across your workspace while still allowing individual customization.

        **Important:**
        - This only affects new employees - existing employees keep their current holiday settings
        - The country code must be one of the available codes from `GET /holidays/countries`
        - Examples: "US" (United States), "DE" (Germany), "US-CA" (California), "AT" (Austria)

        **Use cases:**
        - Setting up a new workspace with a primary location
        - Changing the default for a workspace that has moved or expanded to a new region
        - Standardizing holiday rules across the organization
      operationId: HolidaysController_updateHolidaySettings
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateHolidaySettingsDto'
      responses:
        '200':
          description: Holiday settings updated successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update holiday settings
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /holidays/countries:
    get:
      description: |-
        Retrieves the complete list of available holiday countries and states that can be used for holiday configurations.

        **What is returned:**
        - List of all supported holiday regions with:
          - `code`: Country/region code (e.g., "US", "DE", "US-CA", "AT")
          - `name`: Localized country/region name (e.g., "United States", "Germany", "California", "Austria")

        **How to use:**
        - Use these codes when setting default holiday settings or individual employee holiday rules
        - Country codes follow ISO 3166-1 alpha-2 format (e.g., "US", "DE")
        - State codes use format "COUNTRY-STATE" (e.g., "US-CA" for California, "US-NY" for New York)
        - Use the `language` query parameter to get country names in a specific language (default: "en")

        **Note:** This list includes all regions for which Leadtime has official holiday data. Holidays are automatically loaded from official sources for these regions.
      operationId: HolidaysController_getHolidayCountries
      parameters:
        - name: language
          required: true
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Holiday countries retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/HolidayCountryDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get available holiday countries
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /holidays/years:
    get:
      description: |-
        Retrieves the complete holiday configuration for a specific year and country/region.

        **What is returned:**
        The endpoint returns one of three types of holiday configurations:

        1. **Default** (`type: "default"`): Official holidays from the holiday data source
           - No `id` field (not a custom year)
           - Cannot be modified directly
           - Used when no custom configuration exists

        2. **Custom** (`type: "custom"`): Workspace-specific holiday configuration
           - Has an `id` field (UUID)
           - Can be modified (add, edit, delete holidays)
           - Created when you customize holidays for a year
           - May have a `validTill` year that controls propagation to future years

        3. **Derived** (`type: "derived"`): Configuration inherited from a previous custom year
           - Has an `id` field (UUID)
           - Shows `derivedFrom` year that it was copied from
           - Created automatically when a custom year's `validTill` extends to future years
           - Can be modified like custom years

        **Holiday types:**
        Each holiday in the list can be:
        - **Fixed**: A specific date (e.g., "2024-12-25" for Christmas)
        - **Rule**: A computed date based on a rule (e.g., "25 december" or "easter monday")

        **Response fields:**
        - `id`: Holiday year ID (only for custom/derived types)
        - `year`: The year this configuration applies to
        - `country`: Country/region code
        - `type`: Configuration type ("default", "custom", or "derived")
        - `derivedFrom`: Source year for derived configurations
        - `validTill`: Last year this configuration applies to (null = indefinite)
        - `holidays`: Array of all holidays with name, date, type, rule, and substitute flag

        **Use cases:**
        - Displaying holidays in a calendar view
        - Checking if a year has been customized
        - Getting the complete holiday list for capacity planning
        - Understanding the relationship between years (derived configurations)
      operationId: HolidaysController_getHolidayYear
      parameters:
        - name: year
          required: true
          in: query
          description: The year to retrieve holidays for. Must be 1900 or later. The endpoint will return the appropriate configuration type (default, custom, or derived) for this year.
          schema:
            example: 2024
            type: number
        - name: country
          required: true
          in: query
          description: Country or region code (e.g., "US", "DE", "US-CA"). Must be one of the available codes from `GET /holidays/countries`.
          schema:
            example: US
            type: string
      responses:
        '200':
          description: Holiday year retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HolidayYearResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get holiday year
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a custom holiday year configuration for the workspace, allowing you to customize holidays for a specific year and country/region.

        **What this does:**
        Creates a workspace-specific holiday configuration by copying the default holidays for the specified year and country. Once created, you can add, modify, or delete holidays using the holiday days endpoints.

        **Important behavior:**
        - If a custom year already exists for the specified year/country, it will be **deleted and recreated**
        - This effectively resets the year to default holidays
        - All existing custom holidays for that year will be lost
        - Use this endpoint to start customizing a year or to reset it back to defaults

        **Validity period (`validTill`):**
        The `validTill` field controls how the custom configuration propagates to future years:
        - If `validTill` is set (e.g., 2026), the configuration applies to all years from the specified year through `validTill`
        - If `validTill` is `null`, the configuration applies indefinitely to future years
        - Future years will automatically be created as "derived" configurations
        - You can update `validTill` later using `PUT /holidays/years/:yearId`

        **Use cases:**
        - Adding company-specific holidays (e.g., "Company Outing", "Festivus")
        - Removing holidays that don't apply to your organization
        - Modifying holiday dates for your specific needs
        - Resetting a customized year back to defaults

        **Next steps:**
        After creating a custom year, use:
        - `POST /holidays/days` to add new holidays
        - `PUT /holidays/days/:dayId` to modify existing holidays
        - `DELETE /holidays/days/:dayId` to remove holidays
      operationId: HolidaysController_createHolidayYear
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateHolidayYearDto'
      responses:
        '201':
          description: Custom holiday year created successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create custom holiday year
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /holidays/years/{yearId}:
    put:
      description: |-
        Updates the validity period (`validTill`) of a custom holiday year configuration.

        **What this does:**
        Changes how long a custom holiday configuration applies to future years. This is useful when you want to extend or limit the scope of your customizations.

        **How `validTill` works:**
        - **Set to a specific year** (e.g., `2026`): The configuration applies from the year's start year through 2026
          - Future years within this range will be automatically created as "derived" configurations
          - Years beyond `validTill` will use default holidays

        - **Set to `null`**: The configuration applies indefinitely to all future years
          - All future years will automatically inherit this configuration as "derived" years
          - Useful for permanent company holidays that should always apply

        **Example scenarios:**
        1. **Temporary change**: You add a special holiday for 2024-2025 only
           - Create year 2024 with `validTill: 2025`
           - Both 2024 and 2025 will use the custom configuration
           - 2026 and beyond will use defaults

        2. **Permanent company holiday**: You add "Company Foundation Day" that should always be a holiday
           - Create year 2024 with `validTill: null`
           - All future years will automatically include this holiday

        3. **Extending validity**: You previously set `validTill: 2025` but want to extend to 2027
           - Update the year with `validTill: 2027`
           - Years 2026 and 2027 will now also use the custom configuration

        **Note:** This endpoint only works on custom holiday years (those with an `id`). Default years cannot be updated - you must create a custom year first using `POST /holidays/years`.
      operationId: HolidaysController_updateHolidayYear
      parameters:
        - name: yearId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateHolidayYearDto'
      responses:
        '200':
          description: Holiday year updated successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update holiday year
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **Note:** This endpoint is not implemented. To reset a custom holiday year back to defaults, use `POST /holidays/years` instead.

        **How to reset a year:**
        To delete a custom holiday year and return to default holidays:
        1. Call `POST /holidays/years` with the same `year` and `country` values
        2. This will delete the existing custom year and recreate it with default holidays
        3. All custom holidays for that year will be removed

        **Why this approach?**
        The holiday service uses a "recreate" pattern where creating a custom year automatically deletes any existing custom year for the same year/country combination. This ensures a clean reset to defaults while maintaining data consistency.
      operationId: HolidaysController_deleteHolidayYear
      parameters:
        - name: yearId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Holiday year deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete custom holiday year
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /holidays/days:
    post:
      description: |-
        Adds a new holiday day to a custom holiday year configuration.

        **What this does:**
        Creates a new holiday in a custom holiday year. This allows you to add company-specific holidays that don't exist in the default holiday data (e.g., "Company Outing", "Festivus", "Team Building Day").

        **Holiday types:**
        Only **Fixed** holidays can be created via this endpoint:
        - **Fixed**: A specific date that doesn't change (e.g., "2024-12-25" for Christmas)
        - **Rule-based**: Holidays computed from rules (e.g., "25 december", "easter monday") come from default definitions
          - Rule-based holidays can only be edited, not created
          - They exist in default holiday configurations and can be modified in custom years

        **Requirements:**
        - The `yearId` must belong to a custom holiday year (created via `POST /holidays/years`)
        - You cannot add holidays to default years - create a custom year first
        - The `date` must be in ISO 8601 format (e.g., "2024-12-25")
        - The `name` should be descriptive (e.g., "Company Foundation Day")

        **Use cases:**
        - Adding company-specific holidays
        - Adding regional holidays not in the default data
        - Creating special event days that should be treated as holidays

        **Example:**
        To add "Company Outing" on June 15, 2024:
        ```json
        {
          "yearId": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Company Outing",
          "type": "Fixed",
          "date": "2024-06-15"
        }
        ```
      operationId: HolidaysController_createHolidayDay
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,yearId,name,date
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateHolidayDayDto'
      responses:
        '201':
          description: Holiday day created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HolidayDayResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Add holiday day
      tags:
        - holidays
        - time-management
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - yearId
          - name
          - date
        allowedFields:
          - id
          - yearId
          - name
          - type
          - date
          - rule
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /holidays/days/{dayId}:
    put:
      description: |-
        Updates an existing holiday day in a custom holiday year configuration.

        **What this does:**
        Modifies a holiday's name, type, date, or rule. This allows you to customize holidays from the default set or modify holidays you've previously added.

        **Holiday types:**
        You can update holidays to be either type:

        1. **Fixed** (`type: "Fixed"`):
           - A specific date that doesn't change
           - Requires a `date` field in ISO 8601 format (e.g., "2024-12-25")
           - Example: "Christmas Day" on December 25

        2. **Rule** (`type: "Rule"`):
           - A computed date based on a rule string
           - Requires a `rule` field (e.g., "25 december", "easter monday", "first monday in september")
           - The date is automatically computed from the rule for each year
           - If converting from Fixed to Rule, you must provide a `rule` (or it will use the existing rule if available)

        **Important constraints:**
        - You cannot convert a Fixed holiday to Rule without providing a `rule`
        - If the holiday already has a rule and you're converting to Rule type, the existing rule will be preserved if you don't provide one
        - The holiday must belong to a custom year (not a default year)
        - The `yearId` is automatically determined from the existing holiday

        **Use cases:**
        - Renaming a holiday (e.g., "Christmas" → "Holiday Break")
        - Changing a holiday date
        - Converting a fixed holiday to a rule-based one (e.g., "First Monday in September" for Labor Day)
        - Modifying rule-based holidays from the default set

        **Example - Converting Fixed to Rule:**
        ```json
        {
          "name": "Labor Day",
          "type": "Rule",
          "rule": "first monday in september"
        }
        ```
      operationId: HolidaysController_updateHolidayDay
      parameters:
        - name: dayId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,yearId,name,date
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateHolidayDayDto'
      responses:
        '200':
          description: Holiday day updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HolidayDayResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update holiday day
      tags:
        - holidays
        - time-management
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - yearId
          - name
          - date
        allowedFields:
          - id
          - yearId
          - name
          - type
          - date
          - rule
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes a holiday day from a custom holiday year configuration.

        **What this does:**
        Removes a holiday from the custom year configuration. The holiday is soft-deleted, meaning it's marked as deleted but not permanently removed from the database.

        **Important:**
        - Only holidays in custom years can be deleted
        - You cannot delete holidays from default years - create a custom year first
        - This is useful for removing holidays that don't apply to your organization
        - The holiday will no longer appear in holiday lists or affect capacity calculations

        **Use cases:**
        - Removing regional holidays that don't apply to your organization
        - Removing religious holidays that aren't observed by your company
        - Cleaning up holidays you previously added but no longer need

        **Note:** If you want to restore a deleted holiday, you can either:
        - Recreate the custom year using `POST /holidays/years` (resets to defaults)
        - Add the holiday back using `POST /holidays/days`
      operationId: HolidaysController_deleteHolidayDay
      parameters:
        - name: dayId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Holiday day deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete holiday day
      tags:
        - holidays
        - time-management
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /contacts/grid:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of all contacts in the workspace.

        **What are Contacts?**
        The contacts grid provides a unified view of all people in your workspace:
        - **Employees**: Internal team members from your own company
        - **Organization Members**: External contacts from partner or customer organizations

        This unified view allows you to search, filter, and manage all contacts in one place, regardless of whether they are internal employees or external contacts.

        **What is returned:**
        - Contact identification (ID, name, type)
        - Contact information (email, phone, position, title, degree)
        - Organization information (for organization members)
        - Complete address details
        - Employee-specific fields (employment mode, entry/exit dates, tax class)
        - Organization member-specific fields (influence level, attitude, personality type)
        - Status information (active, can login)
        - Personal details (birth date, gender, avatar)

        **Filtering and search:**
        - Quick search across name, email, phone, position, organization, and address fields
        - Advanced filtering by contact type, status, organization, and many other fields
        - Sorting by any sortable field
        - Server-side pagination for large contact lists

        **Note:** This endpoint requires the `Contacts.canSeeContacts` permission. The contacts grid is the foundation for the Address Book feature, which can be synced via CardDAV or exported as vCard.
      operationId: ContactsController_getContactsGrid
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: firstName, lastName, email, phone, position, organizationName, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Contact ID
            - **type** (set): Contact type (EMPLOYEE, ORGANIZATION_MEMBER)
            - **firstName** (string): First name
            - **lastName** (string): Last name
            - **email** (string): Email address
            - **phone** (string): Phone number
            - **position** (string): Job position
            - **title** (string): Title (e.g., Dr., Prof.)
            - **degree** (string): Academic degree
            - **organizationId** (string): Organization ID (for organization members)
            - **organizationName** (string): Organization name (for organization members)
            - **addressStreet** (string): Street address
            - **addressZip** (string): ZIP/Postal code
            - **addressCity** (string): City
            - **addressCountry** (string): Country
            - **addressHouseNumber** (string): House number
            - **avatarId** (string): Avatar file ID
            - **isActive** (boolean): Whether contact is active
            - **canLogin** (boolean): Whether contact can login to the system
            - **employmentMode** (string): Employment mode (for employees)
            - **entryDate** (date): Entry date (for employees)
            - **exitDate** (date): Exit date (for employees)
            - **incomeTaxClass** (string): Income tax class (for employees)
            - **influenceLevel** (string): Influence level (for organization members)
            - **attitudeLevel** (string): Attitude level (for organization members)
            - **personalityType** (string): Personality type (for organization members)
            - **socialNetworkLink** (string): Social network profile link (for organization members)
            - **birthDate** (date): Birth date
            - **gender** (string): Gender
            - **createdAt** (date): Creation timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Contact ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Contact type (EMPLOYEE, ORGANIZATION_MEMBER) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **firstName** (string): First name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **lastName** (string): Last name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **phone** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **position** (string): Job position (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **title** (string): Title (e.g., Dr., Prof.) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **degree** (string): Academic degree (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **organizationId** (string): Organization ID (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **organizationName** (string): Organization name (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressStreet** (string): Street address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressZip** (string): ZIP/Postal code (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressHouseNumber** (string): House number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **avatarId** (string): Avatar file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **isActive** (boolean): Whether contact is active (boolean filter). Available comparisons: equal, not_equal
            - **canLogin** (boolean): Whether contact can login to the system (boolean filter). Available comparisons: equal, not_equal
            - **employmentMode** (string): Employment mode (for employees) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **entryDate** (date): Entry date (for employees) (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **exitDate** (date): Exit date (for employees) (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **incomeTaxClass** (string): Income tax class (for employees) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **influenceLevel** (string): Influence level (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **attitudeLevel** (string): Attitude level (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **personalityType** (string): Personality type (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **socialNetworkLink** (string): Social network profile link (for organization members) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **birthDate** (date): Birth date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **gender** (string): Gender (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Contact ID
            - **type**: Contact type (EMPLOYEE, ORGANIZATION_MEMBER)
            - **firstName**: First name
            - **lastName**: Last name
            - **name**: Full name (firstName + lastName, virtual field)
            - **position**: Job position
            - **organizationId**: Organization ID (for organization members)
            - **organizationName**: Organization name (for organization members)
            - **addressCity**: City
            - **addressCountry**: Country
            - **isActive**: Whether contact is active
            - **canLogin**: Whether contact can login to the system
            - **employmentMode**: Employment mode (for employees)
            - **entryDate**: Entry date (for employees)
            - **exitDate**: Exit date (for employees)
            - **influenceLevel**: Influence level (for organization members)
            - **attitudeLevel**: Attitude level (for organization members)
            - **personalityType**: Personality type (for organization members)
            - **birthDate**: Birth date
            - **createdAt**: Creation timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** name (asc)
          schema:
            example: '[{"field":"name","direction":"asc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, type, firstName, lastName, name, email, phone, position, title, degree, organizationId, organizationName, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber, avatarId, isActive, canLogin, employmentMode, entryDate, exitDate, incomeTaxClass, influenceLevel, attitudeLevel, personalityType, socialNetworkLink, birthDate, gender, createdAt
          schema:
            example: id,type
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved contacts grid
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get contacts grid
      tags:
        - contacts
      x-required-permissions:
        - contacts.canSeeContacts
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /administration/custom-icons:
    get:
      description: |-
        Retrieves all custom icons configured for the workspace.

        **What are Custom Icons?**
        Custom icons are user-uploaded symbols (images) that can be used throughout the workspace to visually organize and tag content. They help make information easier to grasp quickly.

        **Where can custom icons be used:**
        - Projects
        - Tickets/Tasks
        - Documents
        - Organization profiles
        - Employee profiles

        **What is returned:**
        - Icon ID and name (e.g., `:company_logo:` or `:team_sales:`)
        - Image file ID and public URL
        - Author information (who created the icon)
        - Creation and last update timestamps

        **Note:** This endpoint requires the `CustomIcons.create` permission. All custom icons are available to all users in the workspace.
      operationId: CustomIconsController_getCustomIcons
      parameters: []
      responses:
        '200':
          description: Custom icons retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CustomIconResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List custom icons
      tags:
        - administration
        - custom-icons
      x-required-permissions:
        - custom-icons.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new custom icon for the workspace.

        **What are Custom Icons?**
        Custom icons are user-uploaded symbols (images) that can be used throughout the workspace to visually organize and tag content. They help make information easier to grasp quickly.

        **How to create a custom icon:**
        1. Upload an image file (PNG or SVG format) using POST /api/public/workspace/upload
        2. Get the file ID from the upload response
        3. Provide a unique icon name (e.g., `:company_logo:`, `:team_sales:`, `:lt_editor:`)
        4. Use the file ID in the `imageId` field

        **Icon name requirements:**
        - Must be alphanumeric characters and underscores only
        - Cannot conflict with default emoji names
        - Format: typically wrapped in colons (e.g., `:icon_name:`)

        **Note:** This endpoint requires the `CustomIcons.create` permission. Once created, the icon is available to all users in the workspace and can be used in projects, tickets, documents, and profiles.
      operationId: CustomIconsController_createCustomIcon
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomIconDto'
      responses:
        '201':
          description: Custom icon created successfully
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create custom icon
      tags:
        - administration
        - custom-icons
      x-required-permissions:
        - custom-icons.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/custom-icons/{id}:
    put:
      description: |-
        Updates an existing custom icon by replacing its image.

        **What can be updated:**
        - **Image only**: The icon image can be replaced with a new one
        - **Name is readonly**: The icon name cannot be changed after creation

        **How to update a custom icon:**
        1. Upload a new image file (PNG or SVG format) using POST /api/public/workspace/upload
        2. Get the file ID from the upload response
        3. Use the file ID in the `imageId` field

        **Note:** This endpoint requires the `CustomIcons.create` permission. The icon name remains unchanged - only the image is updated. The updated icon will be reflected throughout the workspace wherever it's used.
      operationId: CustomIconsController_updateCustomIcon
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomIconDto'
      responses:
        '200':
          description: Custom icon updated successfully
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update custom icon
      tags:
        - administration
        - custom-icons
      x-required-permissions:
        - custom-icons.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Permanently deletes a custom icon from the workspace.

        **What happens:**
        - The custom icon is permanently removed from the workspace
        - The icon will no longer be available for use in projects, tickets, documents, or profiles
        - Any existing uses of the icon may display as missing/broken
        - This action cannot be undone

        **Note:** This endpoint requires the `CustomIcons.create` permission. Use with caution as deleting an icon may affect content that references it.
      operationId: CustomIconsController_deleteCustomIcon
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Custom icon deleted successfully
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete custom icon
      tags:
        - administration
        - custom-icons
      x-required-permissions:
        - custom-icons.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /timebookings/timeGrid:
    get:
      description: |-
        Retrieves a paginated, filterable grid of all time booking entries in the workspace.

        **What are Time Bookings?**
        Time bookings (also called time logs or time entries) are records of working hours that employees have logged to specific tasks or projects. They document how much time was spent on different activities, which is essential for:
        - Project planning and resource allocation
        - Billing customers accurately
        - Analyzing productivity and team workload
        - Understanding where time is being spent in the organization

        **What data is returned:**
        The grid returns time booking entries with the following information:
        - **Date**: When the time was logged
        - **Hours**: Amount of time logged (decimal format, e.g., 8.5 for 8 hours 30 minutes)
        - **Comment**: Optional notes about the work performed
        - **Project ID**: The project the time was logged to
        - **Task ID**: The specific task (if time was logged to a task rather than just a project)
        - **User ID**: Which employee logged the time
        - **Activity ID**: The type of work performed (e.g., development, research, management)
        - **Value Group**: The project value group for categorization

        **Response includes:**
        - Paginated list of time booking items
        - Total count of matching entries
        - **totalHours**: Sum of all hours across filtered entries (useful for reporting)
        - Current page and page size

        **Filtering and sorting:**
        You can filter by date range, project, task, user, activity, or value group. Quick search is available on comments. Results are sorted by date (newest first) by default.

        **Permissions:**
        Requires either "seeLoggedTimeDetails" (to see your own time) or "seeCompanyLoggedTime" (to see all company time).
      operationId: TimebookingsController_getTimeGrid
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: comment'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **date** (date): Date of the time log entry
            - **hours** (number): Hours logged
            - **comment** (string): Comment for the time log entry
            - **projectId** (set): Project ID
            - **taskId** (set): Task ID
            - **userId** (set): User ID who logged the time
            - **activityId** (set): Activity ID
            - **valueGroup** (set): Project value group

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **date** (date): Date of the time log entry (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **hours** (number): Hours logged (number filter). Available comparisons: equal, not_equal, >, <, >=, <=, is_empty, is_not_empty
            - **comment** (string): Comment for the time log entry (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **projectId** (set): Project ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **taskId** (set): Task ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **userId** (set): User ID who logged the time (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **activityId** (set): Activity ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **valueGroup** (set): Project value group (set filter). Available comparisons: in, not_in, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **date**: Date of the time log entry
            - **hours**: Hours logged
            - **projectId**: Project ID
            - **taskId**: Task ID
            - **userId**: User ID who logged the time
            - **activityId**: Activity ID

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** date (desc)
          schema:
            example: '[{"field":"date","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** date, hours, comment, projectId, taskId, userId, activityId, valueGroup
          schema:
            example: date,hours
            type: array
            items: {}
        - name: group
          required: false
          in: query
          description: 'Group time bookings by date. When set, response includes groupMeta with fragment/full totals per group. Use "none" for ungrouped list. Default: none.'
          schema:
            enum:
              - day
              - week
              - month
              - none
            type: string
      responses:
        '200':
          description: Successfully retrieved time bookings grid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimebookingsGridResponseDto'
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get time bookings grid
      tags:
        - timebookings
        - time-tracking
      x-required-permissions:
        - tasks.seeLoggedTimeDetails
        - tasks.seeCompanyLoggedTime
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /timebookings:
    post:
      description: |-
        Creates a new time booking entry to record working hours spent on a task or project.

        **What is a Time Booking?**
        A time booking is a record of working hours that you have spent on a specific task or project. Time bookings are used for retroactive time tracking - documenting work that has already been completed.

        **Two types of time bookings:**
        1. **Task-based**: Time logged to a specific task within a project. Use this when you want to track time for a particular work item (e.g., "Fix bug in login form"). The time automatically counts toward the parent project.
        2. **Project-based**: Time logged directly to a project without a specific task. Use this for general project activities that are not tied to a particular task (e.g., "Marketing research for Q1 campaign").

        **Required information:**
        - **Type**: Either "task" or "project"
        - **Task ID** (if type is "task"): The UUID of the task you worked on
        - **Project ID** (if type is "project"): The UUID of the project
        - **Activity ID**: The type of work performed (e.g., development, design, management). Activities are defined in your workspace settings.
        - **Hours**: The amount of time spent (decimal format, e.g., 2.5 for 2 hours 30 minutes)
        - **Date**: The date when the work was performed (ISO date format: YYYY-MM-DD)

        **Optional information:**
        - **Minutes**: Additional minutes (0-59) to add to the hours. If you provide both hours and minutes, they are combined (e.g., hours: 2, minutes: 30 = 2.5 hours total).
        - **Comment**: Notes about what was accomplished during this time

        **How it works:**
        - Time bookings are always created for the authenticated user (you cannot log time for other users)
        - When logging time to a task, the system automatically links it to the task's parent project
        - The time entry is immediately available in time tracking reports and project analytics
        - Hours and minutes are combined: if you provide hours: 2 and minutes: 30, the total is 2.5 hours

        **Example use cases:**
        - Documenting time spent on a completed feature
        - Recording billable hours for client projects
        - Tracking time for internal projects and activities
        - Completing time logs at the end of the day or week

        **Note:** For real-time time tracking while working, use the Time Tracker feature instead of manual time bookings.
      operationId: TimebookingsController_createTimeBooking
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTimeBookingDto'
      responses:
        '201':
          description: Time booking created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid request data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing permission to log time
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new time booking
      tags:
        - timebookings
        - time-tracking
      x-required-permissions:
        - tasks.logTime
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /timebookings/{id}:
    patch:
      description: |-
        Updates an existing time booking entry. You can modify any field of the time booking.

        **Who can update time bookings:**
        - You can always update your own time bookings
        - If you have the "editAnyTimeLog" permission, you can update any time booking in the workspace
        - Otherwise, you cannot update time bookings created by other users

        **What can be updated:**
        All fields are optional in the update request. Only provide the fields you want to change:
        - **Type**: Change between task-based and project-based booking
        - **Task ID**: Switch to a different task (if type is "task")
        - **Project ID**: Switch to a different project (if type is "project")
        - **Activity ID**: Change the activity type
        - **Hours**: Adjust the logged hours
        - **Minutes**: Adjust additional minutes (0-59)
        - **Comment**: Update the comment text
        - **Date**: Change the date when the work was performed

        **How it works:**
        - Fields not provided in the update request will keep their existing values
        - If you change the type from "task" to "project" (or vice versa), make sure to provide the appropriate ID field
        - Hours and minutes are combined: if you provide both, they are added together
        - The system validates that the time booking exists and belongs to your workspace

        **Common use cases:**
        - Correcting a typo in hours or comment
        - Moving time from one task to another
        - Changing the activity type for better categorization
        - Updating the date if it was logged incorrectly
      operationId: TimebookingsController_updateTimeBooking
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchTimeBookingDto'
      responses:
        '200':
          description: Time booking updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '400':
          description: Invalid request data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing permission to edit this time booking
        '404':
          description: Time booking not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update an existing time booking
      tags:
        - timebookings
        - time-tracking
      x-required-permissions:
        - tasks.logTime
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a time booking entry. The entry is marked as deleted but not permanently removed from the database.

        **Who can delete time bookings:**
        - You can always delete your own time bookings
        - If you have the "deleteAnyTimeLog" permission, you can delete any time booking in the workspace
        - Otherwise, you cannot delete time bookings created by other users

        **How it works:**
        - The time booking is soft deleted (marked as deleted, not permanently removed)
        - Deleted entries are no longer visible in time tracking grids and reports
        - The system validates that the time booking exists and belongs to your workspace
        - If the time booking was linked to a task, the task's total time will be recalculated

        **Use cases:**
        - Removing accidentally created duplicate entries
        - Deleting time logged to the wrong task or project
        - Cleaning up test or incorrect time entries
      operationId: TimebookingsController_deleteTimeBooking
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Time booking deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Missing permission to delete this time booking
        '404':
          description: Time booking not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a time booking
      tags:
        - timebookings
        - time-tracking
      x-required-scopes:
        - api:write
  /time/timetable:
    get:
      description: |-
        Retrieves comprehensive time table data for employees over a specified date range. The time table provides a calendar view showing attendance, time bookings, vacations, sickness, holidays, and work hours for team members.

        **What is the Time Table?**
        The time table is a unified calendar view that combines multiple time-related data sources:
        - Attendance tracking (clock in/out times, total hours, mood)
        - Time bookings (hours logged on projects and tasks)
        - Vacation requests (pending and approved)
        - Sickness records
        - Public holidays (based on employee holiday codes)
        - Work day configuration (working days, work day length)
        - Chargeable time goals and actuals

        **Required Permission:**
        User must be an Employee type. Additionally, one of the following time table viewing permissions is required:
        - TimeTablePermissions.viewAll: View all employees
        - TimeTablePermissions.viewOwn: View own data only
        - TimeTablePermissions.viewTeams: View own team members

        **User Filtering Options:**
        The usersFilter parameter controls which employees are included in the response:
        - "all": Returns all employees in the workspace. Requires TimeTablePermissions.viewAll permission.
        - "me": Returns only the authenticated user. Requires TimeTablePermissions.viewOwn or TimeTablePermissions.viewTeams permission.
        - "team:{teamId}": Returns all members of the specified team. Requires TimeTablePermissions.viewTeams permission, and the user must be a member of that team.

        **Data Visibility Rules:**
        The response includes different data fields based on permissions and workspace settings:
        - spentHours and chargeableTime: Only included if user has Tasks.seeLoggedTime permission
        - Attendance data (timeFrom, timeTo, totalHours, mood): Only included if workspace has attendanceEnabled flag set
        - If attendance is disabled, totalHours shows time booking data instead
        - Returns empty object {} if user has no time table viewing permissions

        **Date Handling:**
        - Dates must be in ISO 8601 format (YYYY-MM-DD)
        - Dates are normalized to UTC at midnight
        - If dateStart > dateEnd, they are automatically swapped
        - Response includes all days in the range, including weekends

        **Response Structure:**
        Returns an object keyed by userId. Each key contains:
        - employeeId: Employee UUID
        - userId: User UUID
        - name: Employee full name
        - days: Array of day objects, one for each date in the range

        **Day Object Fields:**
        Each day object contains:
        - type: 0 for Attendance mode, 1 for TimeBooking mode
        - date: ISO date string
        - isWeekend: Whether the day is a weekend based on employee work schedule
        - workDayLength: Expected work hours for the day
        - mood: Attendance mood (if attendance enabled)
        - timeFrom/timeTo: Clock in/out times (if attendance enabled)
        - totalHours: Total hours worked (attendance) or logged (time booking)
        - spentHours: Hours logged on tasks (requires Tasks.seeLoggedTime permission)
        - chargeableTime: Hours logged on billable projects (requires Tasks.seeLoggedTime permission)
        - chargeableGoal: Daily chargeable hours goal for the employee
        - holiday: Name of public holiday if applicable
        - vacation: Vacation request data if day falls within a vacation period
        - sickness: Sickness record data if day falls within a sickness period
        - contractStarted: Whether employee contract has started by this date
        - comment: Optional comment for the day
        - highlight: Whether the day should be highlighted in UI
      operationId: TimetableController_getTimeTable
      parameters:
        - name: dateStart
          required: true
          in: query
          description: Start date of the time table range in ISO 8601 format (YYYY-MM-DD). The date is normalized to UTC at midnight. If dateStart is greater than dateEnd, they will be automatically swapped.
          schema:
            example: '2025-10-06'
            type: string
        - name: dateEnd
          required: true
          in: query
          description: End date of the time table range in ISO 8601 format (YYYY-MM-DD). The date is normalized to UTC at midnight. All days from dateStart to dateEnd (inclusive) are included in the response.
          schema:
            example: '2025-10-26'
            type: string
        - name: usersFilter
          required: true
          in: query
          description: 'Filter to control which employees are included in the response. Options: "all" (requires TimeTablePermissions.viewAll), "me" (requires TimeTablePermissions.viewOwn or viewTeams), or "team:{teamId}" (requires TimeTablePermissions.viewTeams and user must be in the team). The team ID format must be exactly "team:" followed by the team UUID with no spaces.'
          schema:
            example: me
            type: string
      responses:
        '200':
          description: Time table data successfully retrieved. Returns an object keyed by userId.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  $ref: '#/components/schemas/TimeTableUserRowDto'
        '400':
          description: Invalid date format or filter value
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get time table data for date range
      tags:
        - time
        - timetable
      x-required-scopes:
        - api:read
  /account/time-tracker:
    get:
      description: |-
        Returns all time trackers for the current user. The Time Tracker is a real-time time tracking feature that allows users to measure work time while actively working on tasks or projects.

        Each tracker can be associated with a specific task or project, includes activity categorization, optional comments, and tracks time through intervals (start/end periods).

        Response includes calculated fields:
        - isRunning: Whether the tracker currently has an active interval
        - hours: Total tracked time in hours (sum of all intervals plus correction)
        - autoEnded: Whether any interval was automatically ended at end of day
        - isOld: Whether the tracker contains intervals from previous days
        - hoursPadded/minutesPadded: Formatted time strings for display

        Also returns lastInteraction timestamp indicating when the user last interacted with any tracker.
      operationId: TimeTrackerController_list
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrackerListResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all time trackers
      tags:
        - account
        - time-tracker
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new time tracker and starts real-time tracking. When a new tracker is created, it automatically stops all other running trackers for the user (only one tracker can run at a time).

        The tracker immediately starts with an active interval beginning at the current time. You can specify:
        - timeTrackingType: Whether to track on Task or Project level
        - projectId: Optional project to associate with
        - taskId: Optional task to associate with (requires timeTrackingType: Task)
        - activityId: Optional activity category
        - comment: Optional description of the work
        - correction: Optional time adjustment in minutes (can be positive or negative)

        Returns the created tracker with calculated fields including isRunning: true.
      operationId: TimeTrackerController_create
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,timeTrackingType,projectId,taskId,isRunning,hours
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTrackerCreateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackerResponseDto'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create new time tracker
      tags:
        - account
        - time-tracker
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - isRunning
          - hours
        allowedFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - activityId
          - comment
          - correction
          - createdAt
          - intervals
          - isRunning
          - hours
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /account/time-tracker/{id}:
    get:
      description: |-
        Returns a specific time tracker by ID with all calculated fields. The tracker must belong to the authenticated user.

        Calculated fields include running status, total hours (from intervals and correction), formatted time strings, and flags for auto-ended intervals or old trackers.
      operationId: TimeTrackerController_getOne
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackerResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Tracker not found or does not belong to user
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get single time tracker
      tags:
        - account
        - time-tracker
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates metadata of an existing time tracker. You can modify any of the following fields:
        - timeTrackingType: Change between Task and Project tracking
        - projectId: Change or remove project association
        - taskId: Change or remove task association
        - activityId: Change or remove activity category
        - comment: Update or remove the comment
        - correction: Adjust tracked time in minutes (positive to add time, negative to subtract)

        All fields are optional - only provided fields will be updated. The tracker must belong to the authenticated user.

        Note: This does not affect the tracker intervals or running status. Use pause/resume endpoints to control time tracking.
      operationId: TimeTrackerController_update
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,timeTrackingType,projectId,taskId,isRunning,hours
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTrackerUpdateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackerResponseDto'
        '400':
          description: Invalid input data
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Tracker not found or does not belong to user
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update existing time tracker
      tags:
        - account
        - time-tracker
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - isRunning
          - hours
        allowedFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - activityId
          - comment
          - correction
          - createdAt
          - intervals
          - isRunning
          - hours
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Permanently deletes a time tracker and all its associated intervals. This action cannot be undone.

        Only the tracker owner can delete their own trackers. If the tracker is currently running, it will be stopped before deletion.
      operationId: TimeTrackerController__delete
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Tracker not found or does not belong to user
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete time tracker
      tags:
        - account
        - time-tracker
      x-required-scopes:
        - api:write
  /account/time-tracker/{id}/pause:
    post:
      description: |-
        Pauses a running time tracker by ending its current active interval. The interval end time is set to the current time.

        After pausing, the tracker stops accumulating time but can be resumed later using the resume endpoint. This is useful for breaks or when switching to other work.

        If the tracker is not currently running, this endpoint has no effect.
      operationId: TimeTrackerController_pause
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,timeTrackingType,projectId,taskId,isRunning,hours
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackerResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Tracker not found or does not belong to user
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Pause running time tracker
      tags:
        - account
        - time-tracker
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - isRunning
          - hours
        allowedFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - activityId
          - comment
          - correction
          - createdAt
          - intervals
          - isRunning
          - hours
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /account/time-tracker/{id}/resume:
    post:
      description: |-
        Resumes a paused time tracker by creating a new active interval starting at the current time.

        When resuming a tracker, all other running trackers for the user are automatically paused (only one tracker can run at a time). This ensures accurate time tracking without overlaps.

        The tracker will continue accumulating time until paused or stopped again.
      operationId: TimeTrackerController_resume
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,timeTrackingType,projectId,taskId,isRunning,hours
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackerResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Tracker not found or does not belong to user
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Resume paused time tracker
      tags:
        - account
        - time-tracker
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - isRunning
          - hours
        allowedFields:
          - id
          - timeTrackingType
          - projectId
          - taskId
          - activityId
          - comment
          - correction
          - createdAt
          - intervals
          - isRunning
          - hours
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /administration/company-settings:
    get:
      description: |-
        Retrieves the company master data including legal entity information, contact details, and address.

        **What is Company Data?**
        Company data contains all the legal, administrative, and contact details of your company. This information is used automatically throughout the system in invoices, contracts, projects, and other documents.

        **What is returned:**
        - **Company identification**: Official company name and legal form (e.g., GmbH, AG, LLC)
        - **Complete address**: Country, postal code, city, street, house number
        - **Contact information**: Phone number, email address, website URL, fax number
        - **Tax and registry details**: Tax identification number, business registration number, registration court
        - **Company description**: Short description of what the company does
        - **Financial information**: Bank account number (IBAN) used for invoices

        **Note:** This endpoint requires the `Company.view` permission. Company data serves as the foundation for correct legal information, consistent communication, and automated document generation throughout Leadtime.
      operationId: CompanySettingsController_getCompanySettings
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetCompanySettingsDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get company settings
      tags:
        - administration
        - company-settings
      x-required-permissions:
        - company.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates company master data. All fields are optional - only provided fields will be updated.

        **What is Company Data?**
        Company data contains all the legal, administrative, and contact details of your company. This information is used automatically throughout the system in invoices, contracts, projects, and other documents.

        **What can be updated:**
        - **Company identification**: Official company name and legal form (e.g., GmbH, AG, LLC, sole proprietorship)
        - **Complete address**: Country, postal code, city, street, house number
        - **Contact information**: Phone number, email address, website URL, fax number
        - **Tax and registry details**: Tax identification number (needed for invoices), business registration number, registration court
        - **Company description**: Short description of what the company does (e.g., "Manufacturer of Leadtime – an ERP system for software providers")
        - **Financial information**: Bank account number (IBAN format) used for invoices

        **Validation rules:**
        - Email addresses must be valid email format
        - Website URLs must be valid URL format
        - All string fields can be empty strings to clear values

        **Note:** This endpoint requires the `Company.manage` permission. Changes take effect immediately and are used throughout the system for document generation and communication.
      operationId: CompanySettingsController_patchCompanySettings
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchCompanySettingsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update company settings
      tags:
        - administration
        - company-settings
      x-required-permissions:
        - company.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/members/journal:
    get:
      description: |-
        Retrieves a paginated grid of organization member journal entries with filtering and sorting capabilities. Filterable fields: id, memberId, createdAt, createdBy, mood, reminder. Sortable fields: id, memberId, createdAt, createdBy, mood, reminder.

        **What are Organization Member Journals?**
        Organization member journals are used to document notes, observations, conversations, and feedback about external organization members (contacts). They help track interactions, agreements, and important information about business partners, customers, suppliers, or other external contacts.

        **What is returned:**
        - Journal entry ID and associated member ID
        - Rich text content (body) converted to HTML format
        - Mood indicator (Happy, Neutral, or Sad)
        - Optional reminder date for follow-ups
        - Creation and update timestamps
        - Creator user ID

        **Permission-based filtering:**
        - Users with `Organizations.seeJournal` permission: See all journal entries for all members
        - Users without `Organizations.seeJournal` permission: Only see entries for their own member record where `visibleToSubject = true`
        - Organization members can create their own entries, which are automatically marked as visible to themselves

        **Filtering options:**
        - Filter by `memberId` to view entries for a specific organization member
        - Filter by `mood` to find entries with specific emotional indicators
        - Filter by `createdAt` or `lastUpdated` to find entries within date ranges
        - Filter by `reminder` to find entries with upcoming or past reminders

        **Use cases:**
        - Track conversations and agreements with external contacts
        - Document feedback and observations about business partners
        - Set reminders for follow-up actions
        - Maintain a chronological record of interactions with organization members
      operationId: OrganizationMembersJournalController_listMemberJournals
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Journal entry ID
            - **memberId** (string): Organization member ID
            - **createdAt** (date): Journal entry creation timestamp
            - **createdBy** (string): Creator user ID
            - **mood** (set): Journal entry mood
            - **reminder** (date): Reminder date

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **memberId** (string): Organization member ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Journal entry ID
            - **memberId**: Organization member ID
            - **createdAt**: Journal entry creation timestamp
            - **createdBy**: Creator user ID
            - **mood**: Journal entry mood
            - **reminder**: Reminder date

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, memberId, createdAt, createdBy, mood, reminder, body
          schema:
            example: id,memberId
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of organization member journal entries
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List organization member journal entries
      tags:
        - organizations
        - organization-members
        - journal
        - organizations
        - organization-members
      x-required-permissions:
        - organizations.seeJournal
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new journal entry for an organization member (external contact).

        **What you need to provide:**
        - `memberId` (required): UUID of the organization member this entry is about
        - `body` (required): Rich text content in HTML or Markdown format. Supports:
          - Headings, paragraphs, lists, and formatting
          - Links and embedded content
          - The content is automatically converted to the internal document format
        - `mood` (optional): Emotional indicator - Happy, Neutral, or Sad (defaults to Neutral)
        - `reminder` (optional): ISO 8601 date string for follow-up reminders (e.g., "2024-12-31")

        **Access control:**
        - Users with `Organizations.writeJournal` permission can create entries for any member
        - Organization members can create entries for their own member record
        - The `memberId` must exist and be accessible to the current user
        - Returns 400 if the member does not exist or is not accessible

        **Visibility behavior:**
        - If the user is creating an entry for their own member record, `visibleToSubject` is automatically set to `true`
        - If the user has `Organizations.writeJournal` permission and creates an entry for another member, `visibleToSubject` is set to `false` (internal only)

        **What is returned:**
        The complete journal entry with all fields, including the body converted to HTML format and timestamps.
      operationId: OrganizationMembersJournalController_createMemberJournal
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,memberId,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrganizationMemberJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationMemberJournalResponseDto'
        '400':
          description: Validation errors or member not accessible
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      memberId:
                        - Member ID is required
                        - Invalid memberId or member not accessible
                      body:
                        - Body content is required
                      mood:
                        - 'Mood must be one of: Happy, Neutral, Sad'
                      reminder:
                        - Reminder must be a valid ISO 8601 date string
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create organization member journal entry
      tags:
        - organizations
        - organization-members
        - journal
        - organizations
        - organization-members
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - memberId
          - createdAt
        allowedFields:
          - id
          - memberId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /organizations/members/journal/{id}:
    get:
      description: |-
        Returns complete details for a specific organization member journal entry.

        **What is returned:**
        - Journal entry ID and associated member ID
        - Rich text content (body) converted to HTML format from the internal document format
        - Mood indicator (Happy, Neutral, or Sad)
        - Optional reminder date in ISO 8601 format (null if not set)
        - Creation timestamp (when the entry was created)
        - Last update timestamp (when the entry was last modified)
        - Creator user ID (who created the entry)

        **Access control:**
        - Creator of the entry can access
        - Users with `Organizations.writeJournal` permission can access any journal entry
        - Returns 404 if the entry does not exist, has been deleted, or the user does not have access

        **Note:** The body content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications.
      operationId: OrganizationMembersJournalController_getMemberJournalDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationMemberJournalResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization member journal entry details
      tags:
        - organizations
        - organization-members
        - journal
        - organizations
        - organization-members
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates specific fields of an existing organization member journal entry. Only provided fields are updated; all fields are optional.

        **What you can update:**
        - `memberId` (optional): Change which member this entry is associated with
        - `body` (optional): Update the rich text content (HTML or Markdown format)
        - `mood` (optional): Change the emotional indicator (Happy, Neutral, or Sad)
        - `reminder` (optional): Update or clear the reminder date
          - Provide an ISO 8601 date string (e.g., "2024-12-31") to set/update
          - Provide `null` to clear an existing reminder
          - Omit the field to keep the existing reminder unchanged

        **Access control:**
        - Users with `Organizations.writeJournal` permission can update any journal entry
        - Organization members can update their own entries where `visibleToSubject = true`
        - If updating `memberId`, the new member must exist and be accessible
        - Returns 404 if the entry does not exist, has been deleted, or the user does not have access
        - Returns 400 if the new `memberId` is invalid or not accessible

        **Visibility behavior:**
        - If changing `memberId` to the user's own member record, `visibleToSubject` is automatically set to `true`
        - Otherwise, the existing `visibleToSubject` value is preserved

        **What is returned:**
        The complete updated journal entry with all fields, including the body converted to HTML format and updated timestamps.
      operationId: OrganizationMembersJournalController_patchMemberJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,memberId,lastUpdated
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchOrganizationMemberJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationMemberJournalResponseDto'
        '400':
          description: Validation errors or member not accessible
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update organization member journal entry
      tags:
        - organizations
        - organization-members
        - journal
        - organizations
        - organization-members
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - memberId
          - lastUpdated
        allowedFields:
          - id
          - memberId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes an organization member journal entry. The entry is marked as deleted but not permanently removed from the database.

        **What happens:**
        - The journal entry is marked with a `deletedAt` timestamp
        - The entry will no longer appear in list queries or be accessible via the API
        - The data is preserved in the database for audit purposes
        - The entry cannot be restored through the API (restoration would require database access)

        **Access control:**
        - Users with `Organizations.writeJournal` permission can delete any journal entry
        - Organization members can delete their own entries where `visibleToSubject = true`
        - Returns 404 if the entry does not exist, has already been deleted, or the user does not have access

        **Note:** This is a soft delete operation. The entry is not permanently removed and can potentially be recovered by database administrators if needed.
      operationId: OrganizationMembersJournalController_deleteMemberJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete organization member journal entry
      tags:
        - organizations
        - organization-members
        - journal
        - organizations
        - organization-members
      x-required-scopes:
        - api:write
  /organizations/members:
    get:
      description: |-
        **Organization Members** are external contacts associated with organizations (customers, partners, suppliers, etc.). They represent people you work with outside your company.

        This endpoint returns a paginated, filterable, and sortable list of all organization members in your workspace.

        **What you can do:**
        - Filter members by organization, status, position, or role
        - Search by name or email
        - Sort by any column
        - Select specific fields to return

        **Returns:** A grid response with organization member data including name, position, email, active status, login capability, and avatar information.

        Retrieves a paginated grid of organization members with filtering and sorting capabilities. Quick search available on: firstName, lastName, email. Filterable fields: id, organizationId, firstName, lastName, email, position, isActive, canLogin, gender, phone, addressCity, addressCountry, roleId, userId, avatarId, createdAt, editedAt. Sortable fields: id, organizationId, firstName, lastName, email, position, isActive, canLogin, gender, addressCity, addressCountry, roleId, userId, createdAt, editedAt.
      operationId: OrganizationMembersController_listOrganizationMembers
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: firstName, lastName, email'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Organization member ID
            - **organizationId** (string): Organization ID
            - **firstName** (string): First name
            - **lastName** (string): Last name
            - **email** (string): Email address
            - **position** (string): Position/title in organization
            - **isActive** (boolean): Whether member is active
            - **canLogin** (boolean): Whether member can login (derived from user status)
            - **gender** (set): Gender
            - **phone** (string): Phone number
            - **addressCity** (string): City
            - **addressCountry** (string): Country
            - **roleId** (string): User role ID
            - **userId** (string): Associated user ID
            - **avatarId** (string): Avatar file ID
            - **createdAt** (date): Creation timestamp
            - **editedAt** (date): Last update timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Organization member ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **organizationId** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **firstName** (string): First name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **lastName** (string): Last name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **position** (string): Position/title in organization (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **isActive** (boolean): Whether member is active (boolean filter). Available comparisons: equal, not_equal
            - **canLogin** (boolean): Whether member can login (derived from user status) (boolean filter). Available comparisons: equal, not_equal
            - **gender** (set): Gender (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **phone** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **roleId** (string): User role ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **userId** (string): Associated user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **avatarId** (string): Avatar file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Organization member ID
            - **organizationId**: Organization ID
            - **firstName**: First name
            - **lastName**: Last name
            - **email**: Email address
            - **position**: Position/title in organization
            - **isActive**: Whether member is active
            - **canLogin**: Whether member can login (derived from user status)
            - **gender**: Gender
            - **addressCity**: City
            - **addressCountry**: Country
            - **roleId**: User role ID
            - **userId**: Associated user ID
            - **createdAt**: Creation timestamp
            - **editedAt**: Last update timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** lastName (asc)
          schema:
            example: '[{"field":"lastName","direction":"asc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, organizationId, firstName, lastName, email, position, isActive, canLogin, gender, phone, addressCity, addressCountry, roleId, userId, avatarId, createdAt, editedAt
          schema:
            example: id,organizationId
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of organization members
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List organization members
      tags:
        - organizations
        - organization-members
      x-required-permissions:
        - organizations.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new organization member (external contact) for an organization. This automatically creates a user account that can be used for guest access to Leadtime.

        **What happens:**
        1. A new organization member record is created
        2. A user account is automatically created and linked to the member
        3. If `canLogin` is `true`:
           - User status is set to Active
           - In ready workspaces, an invitation email is sent with a magic link
           - During initial workspace setup, invitation email delivery is deferred until setup is finished
           - The user can log in as a guest user
        4. If `canLogin` is `false` or omitted:
           - User status is set to Inactive
           - No invitation is sent
           - The member exists as a contact only

        **Required fields:**
        - `organizationId` - The organization this member belongs to
        - `firstName` - Member's first name
        - `lastName` - Member's last name
        - `email` - Email address (used for user account)
        - `roleId` - User role ID (e.g., `workspace-id_guest_` for guest access)

        **Avatar management:**
        - `avatarId` is an avatar configuration ID. During the transition, an image file ID from `POST /workspace/upload` is also accepted and converted to an uploaded avatar configuration.

        **Personality classifications:**
        - **Influence level:** Low, Medium, High, Dominant
        - **Attitude level:** Enthusiastic, WellMeaning, Neutral, Opposing
        - **Personality type:** Dominant, Conscientious, Steady, Initiative

        **Returns:** The created organization member with all details including the generated user ID.
      operationId: OrganizationMembersController_createOrganizationMember
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,email
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrganizationMemberDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationMemberResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      firstName:
                        - First name is required
                      lastName:
                        - Last name is required
                      email:
                        - Email is required
                        - Email must be valid
                      roleId:
                        - Role ID is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create organization member
      tags:
        - organizations
        - organization-members
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - email
        allowedFields:
          - id
          - userId
          - email
          - firstName
          - lastName
          - organizationId
          - roleId
          - isActive
          - canLogin
          - position
          - phone
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.createMembers
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/members/{id}:
    get:
      description: |-
        Returns complete details for a specific organization member.

        **What's included:**
        - **Basic information:** Name, position, academic degree, date of birth, gender, active status
        - **Access to Leadtime:** Whether the member can log in, their role ID, and associated user ID
        - **Contact details:** Full address (street, city, ZIP, country, house number), phone, email, social network links
        - **Personality traits:** Influence level, attitude level, and personality type classifications
        - **Avatar:** Profile picture URL if available
        - **Timestamps:** Creation and last update dates

        **Use cases:**
        - View complete member profile
        - Check access permissions and login status
        - Retrieve contact information for communication
        - Review personality classifications for relationship management
      operationId: OrganizationMembersController_getOrganizationMemberDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationMemberResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization member details
      tags:
        - organizations
        - organization-members
      x-required-permissions:
        - organizations.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Partially updates an existing organization member. Only the fields you provide will be updated; all other fields remain unchanged.

        **Automatic user synchronization:**
        Changes to certain fields automatically sync to the associated user account:
        - `firstName` → Updates user's first name
        - `lastName` → Updates user's last name
        - `email` → Updates user's email address
        - `roleId` → Updates user's role
        - `canLogin` → Controls user access and status

        **Login access changes:**
        - **Enabling login** (`canLogin: false → true`):
          - User status changes to Active
          - In ready workspaces, an invitation email is sent with a magic link
          - During initial workspace setup, invitation email delivery is deferred until setup is finished
          - User can now log in as a guest user
        - **Disabling login** (`canLogin: true → false`):
          - User status changes to Inactive
          - Invite token is cleared
          - User can no longer log in

        **Avatar management:**
        - To update avatar: provide an avatar configuration ID. During the transition, an uploaded image file ID is also accepted.
        - To remove avatar: Set `avatarId` to `null` or an empty string

        **All other fields** (address, phone, personality traits, etc.) can be updated independently without affecting the user account.
      operationId: OrganizationMembersController_updateOrganizationMember
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,email
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchOrganizationMemberDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationMemberResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization member
      tags:
        - organizations
        - organization-members
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - email
        allowedFields:
          - id
          - userId
          - email
          - firstName
          - lastName
          - organizationId
          - roleId
          - isActive
          - canLogin
          - position
          - phone
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.editMembers
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes an organization member and the associated user account.

        **What happens:**
        - The organization member is marked as deleted (soft delete)
        - The associated user account is also marked as deleted
        - The avatar file is permanently deleted from storage
        - Records remain in the database but are hidden from normal queries

        **Note:** This is a soft delete operation. The data is not permanently removed and could potentially be restored through database operations if needed.
      operationId: OrganizationMembersController_deleteOrganizationMember
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete organization member
      tags:
        - organizations
        - organization-members
      x-required-permissions:
        - organizations.editMembers
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/letters:
    get:
      description: |-
        **What are Organization Letters?**
        Organization letters are formal business correspondence documents that can be created, managed, and stored directly in Leadtime. They provide centralized, traceable communication linked to each organization, reducing media discontinuity and making it easier to document business correspondence.

        **What this endpoint does:**
        Returns a paginated, sortable, and filterable list of all organization letters in the workspace. You can view letters for a specific organization or all letters across the workspace.

        **What data is returned:**
        - Letter metadata: ID, subject, date, organization, member (if applicable)
        - Timestamps: creation date, last update date
        - Creator information: who created the letter
        - Address type: whether the letter is addressed to the organization or a specific member

        **Supported operations:**
        Retrieves a paginated grid of organization letters with filtering and sorting capabilities. Quick search available on: subject. Filterable fields: id, organizationId, organizationName, memberId, subject, dateOfLetter, addressType, createdAt, lastUpdated, createdBy. Sortable fields: id, organizationId, organizationName, memberId, subject, dateOfLetter, addressType, createdAt, lastUpdated, createdBy.

        **Use cases:**
        - View all letters for a specific organization
        - Search and filter letters by subject, date, or member
        - Track letter creation and modification history
        - Export letter lists for reporting

        **Note:** Requires `seeLetters` permission on the Organizations resource.
      operationId: OrganizationLettersController_listOrganizationLetters
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: subject'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Letter ID
            - **organizationId** (set): Organization ID
            - **organizationName** (string): Organization name
            - **memberId** (set): Organization member ID (null if not member-specific)
            - **subject** (string): Letter subject
            - **dateOfLetter** (date): Date of the letter
            - **addressType** (set): Address type (Organization or Member)
            - **createdAt** (date): Creation timestamp
            - **lastUpdated** (date): Last update timestamp
            - **createdBy** (set): ID of user who created the letter

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Letter ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **organizationId** (set): Organization ID (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **organizationName** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **memberId** (set): Organization member ID (null if not member-specific) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **subject** (string): Letter subject (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **dateOfLetter** (date): Date of the letter (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **addressType** (set): Address type (Organization or Member) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **lastUpdated** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdBy** (set): ID of user who created the letter (set filter). Available comparisons: in, not_in, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Letter ID
            - **organizationId**: Organization ID
            - **organizationName**: Organization name
            - **memberId**: Organization member ID (null if not member-specific)
            - **subject**: Letter subject
            - **dateOfLetter**: Date of the letter
            - **addressType**: Address type (Organization or Member)
            - **createdAt**: Creation timestamp
            - **lastUpdated**: Last update timestamp
            - **createdBy**: ID of user who created the letter

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** lastUpdated (desc)
          schema:
            example: '[{"field":"lastUpdated","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, organizationId, organizationName, memberId, subject, dateOfLetter, addressType, createdAt, lastUpdated, createdBy
          schema:
            example: id,organizationId
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of organization letters
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List organization letters
      tags:
        - organizations
        - organization-letters
        - organizations
      x-required-permissions:
        - organizations.seeLetters
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        **What this endpoint does:**
        Creates a new formal business letter linked to an organization. Letters can be addressed to the organization itself or to a specific organization member (contact person).

        **Required information:**
        - **Organization ID**: The organization this letter belongs to (must exist in workspace)
        - **Subject**: Title or topic of the letter
        - **Date of letter**: The date shown on the letter (can be backdated if needed)
        - **Address type**: Choose between organization address or member's private address
        - **Body content**: The letter text in HTML or Markdown format

        **Optional information:**
        - **Member ID**: If provided, links the letter to a specific organization member/contact person. The member must belong to the specified organization.

        **Content formatting:**
        Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

        For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

        Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

        In HTML you can use:
        ## Node Types and Marks

        ### Nodes

        **paragraph**
        Type: block
        Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
        Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
        Atts:
        - extraPlaceholder: null
        ```html
        <p class="paragraph-base" extraplaceholder="null/some-value"></p>
        ```

        **heading**
        Type: block
        Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
        Atts:
        - level: 1
        ```html
        <h1 class="heading-base"></h1>
        ```

        **bulletList**
        Type: block list
        Content: listItem+
        Atts: none
        ```html
        <ul class="list-base"><li></li></ul>
        ```

        **hardBreak**
        Type: inline
        Atts: none
        ```html
        <br>
        ```

        **horizontalRule**
        Type: block
        Atts: none
        ```html
        <hr class="hr-base">
        ```

        **orderedList**
        Type: block list
        Content: listItem+
        Atts:
        - start: 1
        - type: null
        ```html
        <ol class="order-list-base" type="null/some-value"><li></li></ol>
        ```

        **listItem**
        Content: (paragraph|list)* (paragraphs and/or lists, in any order)
        Atts: none
        ```html
        <li></li>
        ```

        **blockquote**
        Type: block
        Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
        Atts: none
        ```html
        <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
        ```

        **variable**
        Type: inline
        Document/template variable token (span with data-type="variable" and data-id).
        - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
        Atts:
        - id: null
        ```html
        <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
        ```

        ### Marks

        **link**
        Atts:
        - href: null
        - target: _blank
        - rel: noopener noreferrer nofollow
        - class: null
        - title: null
        ```html
        <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
        ```

        **bold**
        Atts: none
        ```html
        <strong>bold text example</strong>
        ```

        **code**
        Atts: none
        ```html
        <code>code text example</code>
        ```

        **italic**
        Atts: none
        ```html
        <em>italic text example</em>
        ```

        **strike**
        Atts: none
        ```html
        <s>strike text example</s>
        ```

        **underline**
        Atts: none
        ```html
        <u>underline text example</u>
        ```


        ## Available Template Variables

        You can use the following variables in your organization letter body. Variables are represented as HTML spans:

        `clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`

        **Example HTML**: `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`, `<span data-type="variable" data-id="contactPerson">John Doe</span>`

        **Validation rules:**
        - Organization ID must exist and be accessible in your workspace
        - If member ID is provided, it must belong to the specified organization
        - Subject cannot be empty
        - Body content cannot be empty
        - Date must be a valid date

        **What happens after creation:**
        - Letter is saved and linked to the organization
        - Letter appears in the organization's letters list
        - Letter can be viewed, edited, or downloaded as PDF
        - Letter body is automatically converted from HTML/Markdown to internal format (IDoc)

        **Use cases:**
        - Create formal correspondence with customers or partners
        - Generate personalized letters using template variables
        - Document business communication in a centralized location
        - Track all letters sent to specific organizations or members

        **Note:** Requires `writeLetters` permission on the Organizations resource.
      operationId: OrganizationLettersController_createOrganizationLetter
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,organizationId,subject,dateOfLetter
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateLetterDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LetterResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      organizationId:
                        - Organization ID is required
                      subject:
                        - Subject is required
                      dateOfLetter:
                        - Date is required
                      addressType:
                        - Address type is required
                      body:
                        - Body content is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create organization letter
      tags:
        - organizations
        - organization-letters
        - organizations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - organizationId
          - subject
          - dateOfLetter
        allowedFields:
          - id
          - organizationId
          - memberId
          - subject
          - dateOfLetter
          - addressType
          - body
          - createdAt
          - lastUpdated
          - createdBy
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.writeLetters
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/letters/{id}:
    get:
      description: |-
        **What this endpoint does:**
        Retrieves complete details of a specific organization letter, including the full letter body content converted to HTML format.

        **What is returned:**
        - Letter metadata: ID, subject, date, organization ID, member ID (if applicable)
        - Address type: Organization or Member
        - Body content: Full letter text in HTML format (converted from internal IDoc format)
        - Timestamps: Creation date and last update date
        - Creator: User ID of who created the letter

        **Use cases:**
        - Display letter content in a viewer or editor
        - Generate PDF exports of letters
        - Retrieve letter details for editing
        - Audit letter history and metadata

        **Note:** Requires `seeLetters` permission on the Organizations resource.
      operationId: OrganizationLettersController_getOrganizationLetterDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LetterResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization letter details
      tags:
        - organizations
        - organization-letters
        - organizations
      x-required-permissions:
        - organizations.seeLetters
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        **What this endpoint does:**
        Updates specific fields of an existing organization letter. Only the fields you provide will be updated; all other fields remain unchanged. This allows you to make targeted changes without resubmitting the entire letter.

        **What can be updated:**
        - **Organization ID**: Change which organization the letter belongs to
        - **Member ID**: Link/unlink the letter to a specific member (use `null` to clear the member association)
        - **Subject**: Update the letter title
        - **Date of letter**: Change the date shown on the letter
        - **Address type**: Switch between organization address and member address
        - **Body content**: Update the letter text

        **Content formatting:**
        Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

        For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

        Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

        In HTML you can use:
        ## Node Types and Marks

        ### Nodes

        **paragraph**
        Type: block
        Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
        Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
        Atts:
        - extraPlaceholder: null
        ```html
        <p class="paragraph-base" extraplaceholder="null/some-value"></p>
        ```

        **heading**
        Type: block
        Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
        Atts:
        - level: 1
        ```html
        <h1 class="heading-base"></h1>
        ```

        **bulletList**
        Type: block list
        Content: listItem+
        Atts: none
        ```html
        <ul class="list-base"><li></li></ul>
        ```

        **hardBreak**
        Type: inline
        Atts: none
        ```html
        <br>
        ```

        **horizontalRule**
        Type: block
        Atts: none
        ```html
        <hr class="hr-base">
        ```

        **orderedList**
        Type: block list
        Content: listItem+
        Atts:
        - start: 1
        - type: null
        ```html
        <ol class="order-list-base" type="null/some-value"><li></li></ol>
        ```

        **listItem**
        Content: (paragraph|list)* (paragraphs and/or lists, in any order)
        Atts: none
        ```html
        <li></li>
        ```

        **blockquote**
        Type: block
        Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
        Atts: none
        ```html
        <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
        ```

        **variable**
        Type: inline
        Document/template variable token (span with data-type="variable" and data-id).
        - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
        Atts:
        - id: null
        ```html
        <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
        ```

        ### Marks

        **link**
        Atts:
        - href: null
        - target: _blank
        - rel: noopener noreferrer nofollow
        - class: null
        - title: null
        ```html
        <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
        ```

        **bold**
        Atts: none
        ```html
        <strong>bold text example</strong>
        ```

        **code**
        Atts: none
        ```html
        <code>code text example</code>
        ```

        **italic**
        Atts: none
        ```html
        <em>italic text example</em>
        ```

        **strike**
        Atts: none
        ```html
        <s>strike text example</s>
        ```

        **underline**
        Atts: none
        ```html
        <u>underline text example</u>
        ```


        ## Available Template Variables

        You can use the following variables in your organization letter body. Variables are represented as HTML spans:

        `clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`

        **Example HTML**: `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`, `<span data-type="variable" data-id="contactPerson">John Doe</span>`

        **Validation rules:**
        - Letter must exist and be accessible in your workspace
        - If organization ID is provided, it must exist and be accessible
        - If member ID is provided (or set to `null`), the member must belong to the specified organization (or the letter's current organization if organization ID is not being updated)
        - All field validations from the create endpoint apply to updated fields

        **How partial updates work:**
        - Fields not provided in the request remain unchanged
        - If you provide `memberId: null`, it clears the member association
        - Body content is automatically converted from HTML/Markdown to internal format (IDoc) if provided
        - The `lastUpdated` timestamp is automatically updated

        **Use cases:**
        - Correct typos or update letter content
        - Change the recipient (member) of an existing letter
        - Update letter date if backdating is needed
        - Modify letter subject or address type

        **Note:** Requires `writeLetters` permission on the Organizations resource.
      operationId: OrganizationLettersController_patchOrganizationLetter
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,organizationId,subject,dateOfLetter
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchLetterDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LetterResponseDto'
        '400':
          description: Validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update organization letter
      tags:
        - organizations
        - organization-letters
        - organizations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - organizationId
          - subject
          - dateOfLetter
        allowedFields:
          - id
          - organizationId
          - memberId
          - subject
          - dateOfLetter
          - addressType
          - body
          - createdAt
          - lastUpdated
          - createdBy
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.writeLetters
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        **What this endpoint does:**
        Soft deletes an organization letter, marking it as deleted without permanently removing it from the database. The letter will no longer appear in normal listings but can be recovered if needed.

        **What happens:**
        - Letter is marked as deleted (soft delete)
        - Letter disappears from organization letters list
        - Letter data is preserved in the database
        - Letter can potentially be restored through database operations

        **Use cases:**
        - Remove letters that are no longer needed
        - Clean up test or draft letters
        - Maintain data integrity while allowing cleanup

        **Note:** Requires `writeLetters` permission on the Organizations resource.
      operationId: OrganizationLettersController_deleteOrganizationLetter
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete organization letter
      tags:
        - organizations
        - organization-letters
        - organizations
      x-required-permissions:
        - organizations.writeLetters
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/organization-settings/custom-fields:
    get:
      description: |-
        Retrieves all custom fields configured for organizations in the workspace.

        **What are Custom Fields?**
        Custom fields allow you to capture additional information about organizations that matters to your company. These fields are flexible and can hold different types of data.

        **Supported field types:**
        - Text: Single-line text input
        - Textarea: Multi-line text input
        - Number: Numeric values
        - Date: Date selection
        - Checkbox: Boolean true/false
        - Select: Single selection from predefined options
        - MultiSelect: Multiple selections from predefined options

        **Common use cases:**
        - Industry classification (e.g., Technology, Healthcare, Finance)
        - Business relationship data (e.g., Contract type, Payment terms)
        - CRM-relevant information (e.g., Lead source, Account tier)
        - Individual extra info for special organization types

        **What is returned:**
        - Field ID, name, description, type, and entity (always "Organization")
        - Sort order (for display ordering)
        - Multilingual translations
        - Select options (for Select and MultiSelect types)

        **Note:** Custom fields are especially useful in CRM and add organization management features with specific data points tailored to your business needs.
      operationId: OrganizationSettingsController_getOrganizationCustomFields
      parameters: []
      responses:
        '200':
          description: Organization custom fields retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/OrganizationCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List organization custom fields
      tags:
        - administration
        - organization-settings
        - organization-custom-fields
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new custom field for capturing additional organization information.

        **Required fields:**
        - `name`: Display name for the field (e.g., "Priority Level", "Region")
        - `type`: Field type enum value (Text, Textarea, Number, Date, Checkbox, Select, MultiSelect)
        - `translations`: Array of translations for multilingual support

        **Optional fields:**
        - `id`: Optional ID for the field (for updating existing fields)
        - `description`: Additional context about the field
        - `selectOptions`: Required for Select and MultiSelect types - array of option objects with:
          - `id`: Optional identifier (use value if not provided)
          - `value`: Display value for the option
          - `translations`: Array of translations for the option name
        - `translationsDescriptions`: Translations for the field description

        **Field type requirements:**
        - Select and MultiSelect types MUST include selectOptions array
        - Other types do not use selectOptions

        **Values in organization writes:**
        Use the configured field ID as a key in organization `customFields`, with a value matching this definition type. Custom fields store typed data; they do not create native relationships or behavior.

        **Note:** Once created, the custom field will appear when creating or editing organizations. The field can be reordered using the sort endpoint.
      operationId: OrganizationSettingsController_createOrganizationCustomField
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrganizationCustomFieldDto'
      responses:
        '201':
          description: Organization custom field created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create organization custom field
      tags:
        - administration
        - organization-settings
        - organization-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - sort
          - type
          - entity
          - translations
          - selectOptions
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/organization-settings/custom-fields/{id}:
    put:
      description: |-
        Updates an existing organization custom field. All fields must be provided (full replacement).

        **Required fields:**
        - `name`: Updated display name
        - `type`: Field type enum value
        - `translations`: Complete translations array (all languages)

        **Optional fields:**
        - `description`: Updated description
        - `selectOptions`: Updated options array (required for Select and MultiSelect types)
        - `translationsDescriptions`: Updated description translations

        **Important:**
        - Changing field type may cause data loss if existing values are incompatible
        - For Select/MultiSelect types, all options must be included in the request
        - Removing options that are in use may cause validation errors

        **Note:** This is a full update operation. Use PATCH endpoint if you only want to update specific fields.
      operationId: OrganizationSettingsController_updateOrganizationCustomField
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name,type
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOrganizationCustomFieldDto'
      responses:
        '200':
          description: Organization custom field updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationCustomFieldResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization custom field
      tags:
        - administration
        - organization-settings
        - organization-custom-fields
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
          - type
        allowedFields:
          - id
          - name
          - description
          - sort
          - type
          - entity
          - translations
          - selectOptions
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Deletes an organization custom field using soft delete (marked as deleted but not permanently removed).

        **Important considerations:**
        - The field will no longer appear in organization forms
        - Existing organization data stored in this field will be retained but not displayed
        - Deleted fields can be restored using the restore defaults endpoint

        **Note:** This operation requires workspace settings management permission. Consider exporting data before deleting fields if you need to preserve historical information.
      operationId: OrganizationSettingsController_deleteOrganizationCustomField
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Organization custom field deleted successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete organization custom field
      tags:
        - administration
        - organization-settings
        - organization-custom-fields
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /administration/organization-settings/custom-fields/sort:
    post:
      description: |-
        Updates the display order of organization custom fields by providing an ordered array of field IDs.

        **How it works:**
        1. Provide an array of custom field IDs in the desired display order
        2. The first ID in the array will have sort order 0, second will have 1, etc.
        3. Fields not included in the array will maintain their current sort order

        **Example:**
        If you provide ["field-3", "field-1", "field-2"], then:
        - field-3 will appear first in organization forms
        - field-1 will appear second
        - field-2 will appear third

        **Note:** This affects the order in which custom fields appear in organization creation and editing forms.
      operationId: OrganizationSettingsController_sortOrganizationCustomFields
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SortOrganizationCustomFieldsDto'
      responses:
        '200':
          description: Organization custom fields reordered successfully
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Reorder organization custom fields
      tags:
        - administration
        - organization-settings
        - organization-custom-fields
      x-required-permissions:
        - ws.manageSettings
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of all organizations in the workspace.

        **What are Organizations?**
        Organizations represent external business partners that your company has contact with:
        - **Customers**: Existing business partners with ongoing projects
        - **Prospects**: Potential customers with first contact or active offers
        - **Suppliers**: External providers of goods or services
        - **Partners**: Strategic partners or resellers
        - **Competitors**: Competitors in the same industry
        - **Service Providers**: Supporting external providers (e.g., for support)
        - **Investors**: Financial or institutional investors
        - **Authorities**: Public institutions or government agencies
        - **Educational Institutions**: Universities, schools, or research institutes
        - **Target Groups**: Companies that belong to your target customer group

        Organizations are the foundation for managing external projects, contacts (organization members), invoices, and business relationships. They allow you to structure and analyze all external partnerships in one place.

        **What is returned:**
        - Organization identification (ID, name, short name, type, color, icon)
        - Status information (active/inactive)
        - Relationship data (number of linked projects, number of members/contacts)
        - Creation and update timestamps
        - Logo information

        **Filtering and search:**
        - Quick search across organization name, short name, and other fields
        - Advanced filtering by type, status, and other attributes
        - Sorting by any sortable field
        - Server-side pagination for large organization lists

        **Note:** This endpoint requires the Organizations.view permission. Retrieves a paginated grid of organizations with filtering and sorting capabilities. Quick search available on: name, shortName. Filterable fields: id, name, shortName, type, isActive, email, phoneNumber, website, addressCity, addressCountry, addressStreet, addressZip, startOfCollaboration, createdAt, editedAt, isFavorite, parentOrganizationId, parentOrganizationName. Sortable fields: id, name, shortName, type, isActive, addressCity, addressCountry, startOfCollaboration, createdAt, editedAt, isFavorite, parentOrganizationId.
      operationId: OrganizationsController_listOrganizations
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: name, shortName'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Organization ID
            - **name** (string): Organization name
            - **shortName** (string): Short identifier
            - **type** (set): Organization type (Customer, Vendor, Partner, etc.)
            - **isActive** (boolean): Whether organization is active
            - **email** (string): Email address
            - **phoneNumber** (string): Phone number
            - **website** (string): Website URL
            - **addressCity** (string): City
            - **addressCountry** (string): Country
            - **addressStreet** (string): Street address
            - **addressZip** (string): ZIP/Postal code
            - **startOfCollaboration** (date): Start of collaboration date
            - **createdAt** (date): Creation timestamp
            - **editedAt** (date): Last update timestamp
            - **isFavorite** (boolean): Whether the organization is marked as favorite by the current user
            - **parentOrganizationId** (string): Parent organization ID
            - **parentOrganizationName** (string): Parent organization name

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **name** (string): Organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **shortName** (string): Short identifier (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **type** (set): Organization type (Customer, Vendor, Partner, etc.) (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **isActive** (boolean): Whether organization is active (boolean filter). Available comparisons: equal, not_equal
            - **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **phoneNumber** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **website** (string): Website URL (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressStreet** (string): Street address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressZip** (string): ZIP/Postal code (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **startOfCollaboration** (date): Start of collaboration date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **editedAt** (date): Last update timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **isFavorite** (boolean): Whether the organization is marked as favorite by the current user (boolean filter). Available comparisons: equal, not_equal
            - **parentOrganizationId** (string): Parent organization ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **parentOrganizationName** (string): Parent organization name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Organization ID
            - **name**: Organization name
            - **shortName**: Short identifier
            - **type**: Organization type (Customer, Vendor, Partner, etc.)
            - **isActive**: Whether organization is active
            - **addressCity**: City
            - **addressCountry**: Country
            - **startOfCollaboration**: Start of collaboration date
            - **createdAt**: Creation timestamp
            - **editedAt**: Last update timestamp
            - **isFavorite**: Whether the organization is marked as favorite by the current user
            - **parentOrganizationId**: Parent organization ID

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** name (asc)
          schema:
            example: '[{"field":"name","direction":"asc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, name, shortName, type, color, isActive, email, phoneNumber, website, addressCity, addressCountry, addressStreet, addressZip, startOfCollaboration, createdAt, editedAt, isFavorite, parentOrganizationId, parentOrganizationName
          schema:
            example: id,name
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of organizations
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List organizations
      tags:
        - organizations
      x-required-permissions:
        - organizations.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new organization (external business partner) in your workspace.

        **What are Organizations?**
        Organizations represent external business partners like customers, suppliers, partners, or competitors. They serve as the foundation for managing external projects, contacts, invoices, and business relationships.

        **Required fields:**
        - name: Full company name (e.g., "Acme Corporation")
        - type: Organization type (Customer, Prospect, Supplier, Partner, etc.)
        - color: Hex color code for visual identification (e.g., "#FF5733")

        **Optional fields:**
        - shortName: 3-5 character abbreviation (auto-generated if not provided)
        - icon: Icon identifier for visual representation
        - legalForm: Legal company form (e.g., "GmbH", "AG", "LLC")
        - startOfCollaboration: Date when business relationship started (ISO date format: YYYY-MM-DD)
        - Contact information: Address fields, phone, fax, email, website
        - Legal information: Tax number, registration number, registration court
        - Invoice settings: Hourly rate, payment terms, reminder fees, interest rates
        - Document settings: Table of contents, title page, heading style

        - customFields: Values keyed by configured organization custom-field IDs. Values must match their definition types. Custom fields store typed data; they do not create native relationships or behavior.

        **Uploading a logo:**
        1. First, call POST /api/public/workspace/upload to upload the logo file
        2. The upload endpoint returns a file ID (UUID)
        3. Use that file ID in the logoId field when creating the organization

        **Auto-generated fields:**
        - If shortName is not provided, it will be automatically generated from the organization name
        - The organization will be created with isActive: true by default

        **What is returned:**
        The complete organization object with all fields, including computed values like logoUrl and timestamps.

        **Note:** This endpoint requires the Organizations.create permission. All validation errors will be returned with detailed field-level messages.
      operationId: OrganizationsController_createOrganization
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrganizationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      name:
                        - Name is required
                      type:
                        - Type is required
                      color:
                        - Color must be a valid hex code
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create organization
      tags:
        - organizations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - shortName
          - type
          - color
          - email
          - website
          - phoneNumber
          - isActive
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/{id}/objects:
    get:
      description: Returns objects whose organization matches this organization. Visibility rules for objects still apply.
      operationId: OrganizationsController_listOrganizationObjects
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: page
          required: false
          in: query
          schema:
            minimum: 1
            default: 1
            type: number
        - name: pageSize
          required: false
          in: query
          schema:
            minimum: 1
            maximum: 200
            default: 50
            type: number
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedObjectsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List objects for an organization
      tags:
        - organizations
      x-required-permissions:
        - objects.canView
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /organizations/{id}:
    get:
      description: |-
        Retrieves complete details of a specific organization, including all master data, contact information, legal details, and settings.

        **What is returned:**
        - **Basic information**: Name, short name, icon, type, color, legal form, description
        - **Contact details**: Full address (street, city, ZIP, country, house number), phone, fax, email, website
        - **Legal information**: Tax number, registration number, registration court
        - **Business relationship**: Start of collaboration date, active status
        - **Invoice settings**: Hourly rate, payment terms, reminder fees, interest rates, invoice language
        - **Document settings**: Table of contents, title page, heading style, default contact
        - **Logo**: Public URL to the organization logo (if uploaded)
        - **Timestamps**: Creation and last update dates

        **Note:** This endpoint requires the Organizations.view permission. The organization must exist in your workspace and not be deleted.
      operationId: OrganizationsController_getOrganizationDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization details
      tags:
        - organizations
      x-required-permissions:
        - organizations.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    put:
      description: |-
        Updates an existing organization by replacing all fields. This is a full update operation - all fields must be provided, even if you only want to change a few values.

        **When to use:**
        Use this endpoint when you want to update multiple fields or replace the entire organization data. For partial updates (changing only specific fields), use PATCH /organizations/:id instead.

        **Required fields:**
        All fields that are required for creation must be provided:
        - name: Full company name
        - type: Organization type
        - color: Hex color code

        **Optional fields:**
        All other fields can be provided or omitted (will be set to null/undefined if omitted).

        **Updating the logo:**
        1. Call POST /api/public/workspace/upload to upload the new logo file
        2. Use the returned file ID in the logoId field
        3. To remove the logo, set logoId to null

        **What is returned:**
        The complete updated organization object with all fields and computed values.

        **Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.
      operationId: OrganizationsController_updateOrganization
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOrganizationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization
      tags:
        - organizations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - shortName
          - type
          - color
          - email
          - website
          - phoneNumber
          - isActive
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes an organization and all associated projects. The organization and its projects will be marked as deleted but not permanently removed from the database.

        **What happens:**
        - The organization is marked as deleted (soft delete)
        - All associated projects are also soft deleted
        - Organization members (contacts) are not deleted
        - Historical data (invoices, journal entries, etc.) remains intact
        - The organization will no longer appear in normal listings

        **Recovery:**
        Soft-deleted organizations can potentially be recovered through database operations, but this is not exposed via the API.

        **Note:** This endpoint requires the Organizations.delete permission. The organization must exist in your workspace. This operation cannot be undone via the API.
      operationId: OrganizationsController_deleteOrganization
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete organization
      tags:
        - organizations
      x-required-permissions:
        - organizations.delete
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    patch:
      description: |-
        Partially updates an organization. Only the fields you provide will be updated; all other fields remain unchanged. This is the recommended way to update organizations when you only need to change specific fields.

        **When to use:**
        Use this endpoint for partial updates (changing only specific fields). For full updates (replacing all fields), use PUT /organizations/:id instead.

        **Available fields:**
        All organization fields can be updated, including:
        - Basic info: name, shortName, icon, type, color, legalForm, description
        - Contact details: address fields, phone, fax, email, website
        - Legal info: taxNumber, registrationNumber, registrationCourt
        - Business: startOfCollaboration, isActive (via full update)
        - Invoice settings: hourRate, invoiceDueDays, reminder fees, interest rates, invoiceLanguage
        - Document settings: table of contents, title page, heading style, default contact

        - Custom data: `customFields`, keyed by configured organization custom-field IDs; values must match each definition type and do not create native relationships or behavior

        **Updating the logo:**
        1. Call POST /api/public/workspace/upload to upload the new logo file
        2. Use the returned file ID in the logoId field
        3. To remove the logo, set logoId to null

        **Behavior:**
        - Omitted fields remain unchanged
        - Provided fields replace existing values
        - Set fields to null to clear them (where allowed)
        - Date fields should be provided in ISO format (YYYY-MM-DD)

        **What is returned:**
        The complete updated organization object with all fields and computed values.

        **Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.
      operationId: OrganizationsController_patchOrganization
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchOrganizationDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update organization
      tags:
        - organizations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - shortName
          - type
          - color
          - email
          - website
          - phoneNumber
          - isActive
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/{id}/document-settings:
    get:
      description: |-
        Retrieves document formatting settings for a specific organization. These settings control how project documents (quotes, requirement documents, contracts) are formatted for this organization.

        **What are Document Settings?**
        Document settings allow you to customize the appearance and structure of automatically generated project documents on a per-organization basis. These settings override the workspace defaults, enabling customer-specific document layouts and formatting.

        **What is returned:**
        - **Organization-specific settings**: Values set for this organization (can be null to inherit from workspace)
          - projectDocumentEnableToc: Whether to include a table of contents
          - projectDocumentTitlePage: Whether to show a title/cover page
          - projectDocumentHeadingStyle: Formatting style for headings (e.g., Sequential, Normal)
          - projectDocumentDefaultContactUserId: Default contact person for documents
        - **Workspace defaults**: The default values that apply when organization settings are null
          - projectDocumentEnableTocDefault: Workspace default for table of contents
          - projectDocumentTitlePageDefault: Workspace default for title page
          - projectDocumentHeadingStyleDefault: Workspace default heading style

        **Inheritance behavior:**
        - If an organization setting is null, the workspace default is used
        - If an organization setting has a value, it overrides the workspace default
        - This allows you to customize documents per customer while maintaining workspace-wide defaults

        **Use cases:**
        - Different document layouts for specific customers
        - Custom corporate design requirements
        - Creating documents for external partners without internal formatting

        **Note:** This endpoint requires the Organizations.view permission. The organization must exist in your workspace and not be deleted.
      operationId: OrganizationsController_getOrganizationDocumentSettings
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetOrganizationDocumentSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization document settings
      tags:
        - organizations
      x-required-permissions:
        - organizations.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Partially updates document formatting settings for an organization. Only the fields you provide will be updated; all other fields remain unchanged.

        **What are Document Settings?**
        Document settings control how project documents (quotes, requirement documents, contracts) are formatted for this organization. These settings can override workspace defaults to enable customer-specific layouts.

        **Available settings:**
        - projectDocumentEnableToc: Include table of contents (boolean or null to inherit)
        - projectDocumentTitlePage: Show title/cover page (boolean or null to inherit)
        - projectDocumentHeadingStyle: Heading formatting style (enum value or null to inherit)
        - projectDocumentDefaultContactUserId: Default contact person UUID (or null to clear)

        **Inheritance:**
        - Set any field to null to inherit the workspace default for that setting
        - Provide a value to override the workspace default for this organization
        - Omit fields you do not want to change (they remain unchanged)

        **Example use cases:**
        - Enable table of contents for a specific customer: { "projectDocumentEnableToc": true }
        - Reset to workspace default: { "projectDocumentEnableToc": null }
        - Set multiple settings at once: { "projectDocumentEnableToc": true, "projectDocumentTitlePage": false }

        **Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.
      operationId: OrganizationsController_updateOrganizationDocumentSettings
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchOrganizationDocumentSettingsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization document settings
      tags:
        - organizations
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/{id}/custom-fields:
    patch:
      description: |-
        Updates only the custom field values for an organization. This endpoint is optimized for updating custom fields without affecting other organization data.

        **Request body:**
        - `customFields`: Object with custom field IDs as keys and their values
          - Keys should match custom field IDs configured in workspace settings
          - Values must match the field type (string, number, boolean, date, etc.)
          - Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields

        **Example:**
        ```json
        {
          "customFields": {
            "field-id-1": "Text value",
            "field-id-2": 123,
            "field-id-3": true,
            "field-id-4": "2024-01-01"
          }
        }
        ```

        **Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted.
      operationId: OrganizationsController_updateOrganizationCustomFields
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customFields:
                  type: object
                  additionalProperties: true
                  description: Custom field values as key-value pairs
              required:
                - customFields
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization custom fields
      tags:
        - organizations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - shortName
          - type
          - color
          - email
          - website
          - phoneNumber
          - isActive
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /organizations/{id}/invoice-settings:
    get:
      description: |-
        Retrieves invoice and billing settings for a specific organization. These settings control payment terms, reminder rules, and custom text templates for invoices and reminders.

        **What are Invoice Settings?**
        Invoice settings allow you to define organization-specific billing parameters that override workspace defaults. This enables customer-specific payment terms, reminder rules, and invoice texts.

        **What is returned:**
        - **Payment terms**:
          - hourRate: Customer-specific hourly rate (used for all billable times)
          - invoiceDueDays: Number of days until payment is due (1-120)
          - invoiceLanguage: Language code for invoice generation (e.g., "en", "de")
        - **Reminder fees**:
          - enableInvoiceReminderFee: Whether reminder fees are charged
          - invoiceReminderFee: Amount charged for overdue invoices (0-1000)
        - **Interest on arrears**:
          - enableInvoiceInterest: Whether interest is charged on overdue invoices
          - baseInvoiceInterest: Base interest rate percentage (0-1000)
          - invoiceInterest: Interest rate percentage added to base rate (0-1000)
        - **Custom text templates**: All custom invoice and reminder texts, returned as HTML:
          - withBestRegards: Closing text for invoices
          - invoiceGreeting: Greeting text on invoices
          - invoiceFooter: Footer text on invoices
          - invoiceReminderFeeWarning: Warning about reminder fees
          - firstReminderGreeting: Greeting for first reminder
          - firstReminderFooter: Footer for first reminder
          - secondReminderGreeting: Greeting for second reminder
          - secondReminderFooter: Footer for second reminder
          - cancelationBody: Text for invoice cancellations

        **Use cases:**
        - Individual agreements with key clients (longer payment terms, no reminder fees)
        - Customer-specific hourly rates (different rates per customer type)
        - Goodwill rules for important customers (gentler reminders)
        - Stricter rules for risky customers (shorter terms, higher fees)
        - International customers (different interest rates, languages, legal requirements)

        **Note:** This endpoint requires the Organizations.view permission. The organization must exist in your workspace and not be deleted. Custom texts are stored internally in IDoc format but returned as HTML for easier consumption.
      operationId: OrganizationsController_getOrganizationInvoiceSettings
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationInvoiceSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get organization invoice settings
      tags:
        - organizations
      x-required-permissions:
        - organizations.view
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Partially updates invoice and billing settings for an organization. Only the fields you provide will be updated; all other fields remain unchanged.

        **What are Invoice Settings?**
        Invoice settings define organization-specific billing parameters that override workspace defaults, enabling customer-specific payment terms, reminder rules, and invoice texts.

        **Available settings:**
        - **Payment terms**:
          - hourRate: Customer-specific hourly rate (number or null)
          - invoiceDueDays: Days until payment is due, 1-120 (number or null)
          - invoiceLanguage: Language code for invoices (string or null)
        - **Reminder fees**:
          - enableInvoiceReminderFee: Enable reminder fees (boolean or null)
          - invoiceReminderFee: Reminder fee amount, 0-1000 (number or null)
        - **Interest on arrears**:
          - enableInvoiceInterest: Enable interest on overdue invoices (boolean or null)
          - baseInvoiceInterest: Base interest rate percentage, 0-1000 (number or null)
          - invoiceInterest: Interest rate percentage, 0-1000 (number or null)
        - **Custom text templates**: Object with HTML or Markdown content (converted to IDoc internally):
          - withBestRegards, invoiceGreeting, invoiceFooter
          - invoiceReminderFeeWarning
          - firstReminderGreeting, firstReminderFooter
          - secondReminderGreeting, secondReminderFooter
          - cancelationBody

        **Custom text templates:**
        - Can be provided as HTML or Markdown
        - Will be automatically converted to internal IDoc format
        - Support macros/variables (e.g., #dear, #clientCompanyName, #invoiceNumber)
        - Set to null or empty string to clear a template
        - Omit fields you do not want to change

        **Behavior:**
        - Omitted fields remain unchanged
        - Provided fields replace existing values
        - Set fields to null to reset to workspace defaults (where applicable)
        - Custom texts are converted from HTML/Markdown to IDoc format for storage

        **What is returned:**
        The complete updated invoice settings object, with custom texts returned as HTML.

        **Note:** This endpoint requires the Organizations.edit permission. The organization must exist in your workspace and not be deleted. Custom texts are stored internally in IDoc format but can be provided as HTML or Markdown.
      operationId: OrganizationsController_patchOrganizationInvoiceSettings
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchOrganizationInvoiceSettingsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationInvoiceSettingsResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update organization invoice settings
      tags:
        - organizations
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
        allowedFields:
          - id
          - hourRate
          - invoiceDueDays
          - enableInvoiceReminderFee
          - invoiceReminderFee
          - enableInvoiceInterest
          - baseInvoiceInterest
          - invoiceInterest
          - invoiceLanguage
          - customTexts
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - organizations.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /teams:
    get:
      description: |-
        Returns a list of all teams in the workspace with their members.

        **What are Teams?**
        Teams group employees together to simplify project access management and form the basis for structured evaluations. Instead of assigning permissions to each employee individually, you can grant entire teams access to specific projects or areas. This makes it easier to manage rights, responsibilities, and resources in one place.

        Teams are also used for:
        - Filtering charts and dashboards by team
        - Organizing employees into logical groups
        - Project access management
        - Team-based reporting and insights

        **What is returned:**
        - Team identification (ID, name, icon)
        - Number of users in the team
        - Complete list of team members with their details (ID, name, avatar)

        **Note:** This endpoint requires the Teams.manage permission.
      operationId: TeamsController_listTeams
      parameters: []
      responses:
        '200':
          description: List of teams
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TeamResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List all teams
      tags:
        - teams
      x-required-permissions:
        - teams.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new team with the specified name, icon, and members.

        **How to create a team:**
        1. Provide a team name (required) - choose any descriptive name
        2. Select an icon emoji (required) - used for visual identification
        3. Assign at least one user to the team (required) - provide an array of user IDs

        **Validation rules:**
        - Team name must be a non-empty string
        - Icon must be a non-empty string (typically an emoji)
        - Users array must contain at least one user ID
        - All user IDs must be valid UUIDs of existing employees in the workspace

        **What happens after creation:**
        - The team is immediately available for project assignments
        - Team members can be granted access to projects as a group
        - The team appears in team lists and can be used for filtering
        - The team can be edited or deleted later

        **What is returned:**
        - Complete team details including the generated team ID
        - All assigned members with their information
        - Team metadata (name, icon, user count)

        **Note:** This endpoint requires the Teams.create permission.
      operationId: TeamsController_createTeam
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTeamDto'
      responses:
        '200':
          description: Created team
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      name:
                        - Name is required
                      icon:
                        - Icon is required
                      users:
                        - At least one user is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create a new team
      tags:
        - teams
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - usersCount
          - users
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - teams.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /teams/{id}:
    get:
      description: |-
        Returns full details of a specific team including all members.

        **What is returned:**
        - Team identification (ID, name, icon emoji)
        - Number of users in the team
        - Complete list of all team members with:
          - User ID
          - First and last name
          - Avatar file ID (if available)

        **Use cases:**
        - Displaying team information in a detail view
        - Verifying team membership before making changes
        - Getting complete team data for editing

        **Note:** Returns 404 if the team does not exist or has been deleted. This endpoint requires the Teams.manage permission.
      operationId: TeamsController_getTeamDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Team details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get team details
      tags:
        - teams
      x-required-permissions:
        - teams.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates only the provided fields of a team. All fields are optional.

        **How partial updates work:**
        - Only include the fields you want to change
        - Omitted fields remain unchanged
        - You can update the name, icon, members, or any combination

        **Update scenarios:**
        - Change team name: Provide only the "name" field
        - Change icon: Provide only the "icon" field
        - Update members: Provide only the "users" array with new user IDs
        - Update multiple fields: Provide any combination of fields

        **Important notes:**
        - When updating members, provide the complete list of user IDs you want in the team
        - The users array replaces the entire team membership (not merged)
        - At least one user must remain in the team after update
        - All user IDs must be valid UUIDs of existing employees

        **What is returned:**
        - Complete updated team details
        - All current team members
        - Updated metadata (name, icon, user count)

        **Note:** Returns 404 if the team does not exist or has been deleted. This endpoint requires the Teams.edit permission.
      operationId: TeamsController_patchTeam
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,name
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchTeamDto'
      responses:
        '200':
          description: Updated team
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update a team
      tags:
        - teams
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - name
        allowedFields:
          - id
          - name
          - icon
          - usersCount
          - users
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - teams.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes a team. The team will be marked as deleted but not permanently removed from the database.

        **What happens when a team is deleted:**
        - The team is marked as deleted (soft delete)
        - The team no longer appears in team lists
        - Project assignments to this team are removed
        - Historical data and associations are preserved
        - The team cannot be restored through the API

        **Important considerations:**
        - Verify the team exists before deletion (endpoint returns 404 if not found)
        - Consider removing team members or project assignments first if needed
        - Deleted teams do not appear in GET requests
        - This operation cannot be undone through the API

        **What is returned:**
        - Success confirmation

        **Note:** Returns 404 if the team does not exist or has already been deleted. This endpoint requires the Teams.delete permission.
      operationId: TeamsController_deleteTeam
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Team successfully deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete a team
      tags:
        - teams
      x-required-permissions:
        - teams.delete
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /vacations/pending:
    get:
      description: |-
        Retrieves all pending vacation requests that are awaiting manager approval.

        **What is Vacation Management?**
        Vacation management digitizes the entire vacation process and ensures transparent, traceable, and efficient vacation planning. Employees can submit vacation requests, and managers can review and approve them while considering business workload and capacity.

        **What is returned:**
        Each vacation request includes:
        - `id`: Unique vacation request identifier
        - `dateFrom`: Start date of the vacation period (ISO 8601 date string)
        - `dateTo`: End date of the vacation period (ISO 8601 date string)
        - `status`: Current status of the request (always "Pending" for this endpoint)
        - `userId`: ID of the employee who requested the vacation
        - `capacity`: Optional capacity utilization percentage for the vacation period

        **How it works:**
        - Only vacation requests with status "Pending" are returned
        - Managers with "manageAll" permission see all pending requests across the workspace
        - Managers with "manageTeams" permission only see requests from employees in their teams
        - Use the optional `teamId` query parameter to filter requests for a specific team
        - Results are sorted by start date (earliest first)

        **Use cases:**
        - Displaying a list of vacation requests that need approval
        - Building a vacation approval dashboard
        - Filtering requests by team for team-specific managers
      operationId: VacationsController_getPendingVacations
      parameters:
        - name: teamId
          required: false
          in: query
          description: Optional team ID (UUID) to filter vacation requests for a specific team. If not provided, returns requests based on the manager permissions (all teams for manageAll, or manager teams for manageTeams). This parameter is useful for team-specific managers who want to focus on a particular team.
          schema:
            type: string
      responses:
        '200':
          description: Successfully retrieved pending vacations
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get pending vacation requests
      tags:
        - vacations
        - time-management
      x-required-permissions:
        - vacation.manageAll
        - vacation.manageTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
  /vacations/upcoming:
    get:
      description: |-
        Retrieves all approved vacations that have not yet ended (end date is today or in the future).

        **What is returned:**
        Each vacation includes:
        - `id`: Unique vacation identifier
        - `dateFrom`: Start date of the vacation period (ISO 8601 date string)
        - `dateTo`: End date of the vacation period (ISO 8601 date string)
        - `status`: Current status (always "Approved" for this endpoint)
        - `userId`: ID of the employee taking the vacation
        - `capacity`: Optional capacity utilization percentage for the vacation period

        **How it works:**
        - Only vacations with status "Approved" and end date >= today are returned
        - This includes both current vacations (in progress) and future approved vacations
        - Managers with "manageAll" permission see all upcoming vacations across the workspace
        - Managers with "manageTeams" permission only see vacations from employees in their teams
        - Use the optional `teamId` query parameter to filter vacations for a specific team
        - Results are sorted by start date (earliest first)

        **Use cases:**
        - Displaying an overview of upcoming time off for capacity planning
        - Building a team calendar showing approved absences
        - Identifying potential scheduling conflicts
        - Showing managers which employees will be absent in the near future
      operationId: VacationsController_getUpcomingVacations
      parameters:
        - name: teamId
          required: false
          in: query
          description: Optional team ID (UUID) to filter upcoming vacations for a specific team. If not provided, returns vacations based on the manager permissions (all teams for manageAll, or manager teams for manageTeams). This parameter is useful for team-specific managers who want to focus on a particular team.
          schema:
            type: string
      responses:
        '200':
          description: Successfully retrieved upcoming approved vacations
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get upcoming approved vacations
      tags:
        - vacations
        - time-management
      x-required-permissions:
        - vacation.manageAll
        - vacation.manageTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
  /vacations/{id}/approve:
    post:
      description: |-
        Approves a pending vacation request, changing its status from "Pending" to "Approved".

        **What this does:**
        - Updates the vacation request status to "Approved"
        - Records who approved the request and when (statusChangedBy, statusChangedAt)
        - Sends a notification to the employee who requested the vacation
        - The approved vacation will now appear in upcoming vacations and team calendars

        **How it works:**
        - The vacation must be in "Pending" status to be approved
        - Only managers with "manageAll" or "manageTeams" permission can approve requests
        - Managers with "manageTeams" can only approve requests from employees in their teams
        - The vacation ID is provided as a URL parameter
        - Returns 404 if the vacation request is not found

        **Use cases:**
        - Approving vacation requests from a management dashboard
        - Bulk approval workflows
        - Automated approval based on business rules

        **Note:** After approval, the vacation will be visible in the upcoming vacations endpoint and will affect capacity calculations.
      operationId: VacationsController_approveVacation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Vacation request approved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Vacation request not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Approve a vacation request
      tags:
        - vacations
        - time-management
      x-required-permissions:
        - vacation.manageAll
        - vacation.manageTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
  /vacations/{id}/decline:
    post:
      description: |-
        Declines a pending vacation request or cancels an approved vacation.

        **What this does:**
        - For pending requests: Changes status from "Pending" to "Rejected"
        - For approved vacations: Changes status from "Approved" to "Cancelled"
        - Records who declined/cancelled the request and when (statusChangedBy, statusChangedAt)
        - Optionally records a reason for the decline/cancellation
        - Sends a notification to the employee who requested the vacation

        **How it works:**
        - The vacation ID is provided as a URL parameter
        - The optional reason can be provided in the request body
        - Only managers with "manageAll" or "manageTeams" permission can decline/cancel requests
        - Managers with "manageTeams" can only decline/cancel requests from employees in their teams
        - Returns 404 if the vacation request is not found
        - After decline/cancellation, the vacation will appear in the vacation history

        **Request body:**
        - `reason` (optional): Text explanation for why the vacation was declined or cancelled
          - Example: "Insufficient coverage during this period"
          - This reason will be visible in the vacation history

        **Use cases:**
        - Rejecting vacation requests that conflict with business needs
        - Cancelling previously approved vacations due to changed circumstances
        - Providing feedback to employees about why their request was not approved
      operationId: VacationsController_declineVacation
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeclineVacationDto'
      responses:
        '200':
          description: Vacation request declined successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
        '404':
          description: Vacation request not found
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Decline or cancel a vacation request
      tags:
        - vacations
        - time-management
      x-required-permissions:
        - vacation.manageAll
        - vacation.manageTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:write
  /vacations/history:
    get:
      description: |-
        Retrieves vacation history with support for filtering, sorting, and pagination.

        **What is returned:**
        A paginated grid response containing vacation history items. Each item includes:
        - `id`: Unique vacation identifier
        - `dateFrom`: Start date of the vacation period
        - `dateTo`: End date of the vacation period
        - `status`: Vacation status ("Rejected", "Cancelled", or past "Approved" vacations)
        - `employeeId`: ID of the employee who requested the vacation
        - `statusChangedAt`: Timestamp when the status was last changed
        - `statusChangedBy`: ID of the user who changed the status (null if not changed by a user)
        - `reason`: Optional reason provided when the vacation was declined or cancelled

        **What is included in history:**
        - All rejected vacation requests
        - All cancelled vacations (previously approved but then cancelled)
        - All approved vacations that have ended (dateTo < today)
        - Does not include pending or upcoming approved vacations (use the pending/upcoming endpoints for those)

        **Grid features:**
        - **Filtering**: Filter by date range, employee, status, or any other field
        - **Sorting**: Sort by any column (default sorting can be specified)
        - **Pagination**: Standard grid pagination with page size control
        - Grid parameters are automatically parsed from query string using standard grid format

        **How it works:**
        - Managers with "manageAll" permission see all vacation history across the workspace
        - Managers with "manageTeams" permission only see history from employees in their teams
        - Use the optional `teamId` query parameter to filter history for a specific team
        - Supports standard grid server-side parameters (filters, sort, pagination)

        **Use cases:**
        - Building a vacation history table with search and filter capabilities
        - Analyzing vacation patterns and trends
        - Reviewing past vacation decisions and reasons
        - Generating reports on vacation usage
      operationId: VacationsController_getVacationHistory
      parameters:
        - name: teamId
          required: false
          in: query
          description: Optional team ID (UUID) to filter vacation history for a specific team. If not provided, returns history based on the manager permissions (all teams for manageAll, or manager teams for manageTeams). This parameter is useful for team-specific managers who want to focus on a particular team.
          schema:
            type: string
      responses:
        '200':
          description: Successfully retrieved vacation history grid
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                  total:
                    type: number
                  page:
                    type: number
                  pageSize:
                    type: number
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get vacation history grid
      tags:
        - vacations
        - time-management
      x-required-permissions:
        - vacation.manageAll
        - vacation.manageTeams
      x-required-permissions-mode: any
      x-required-scopes:
        - api:read
  /employees/journal:
    get:
      description: |-
        Retrieves a paginated grid of employee journal entries with filtering and sorting capabilities. Filterable fields: id, employeeId, createdAt, createdBy, mood, reminder. Sortable fields: id, employeeId, createdAt, createdBy, mood, reminder.

        **What are Employee Journals?**
        Employee journals are used to document notes, observations, conversations, and feedback about employees. They help track interactions, performance notes, and important information about team members.

        **What is returned:**
        - Journal entry ID and associated employee ID
        - Rich text content (body) converted to HTML format
        - Mood indicator (Happy, Neutral, or Sad)
        - Optional reminder date for follow-ups
        - Creation and update timestamps
        - Creator user ID

        **Permission-based filtering:**
        - Users with `Employees.manage` permission: See all journal entries for all employees
        - Users without `Employees.manage` permission: Only see entries for their own employee record where `visibleToSubject = true`
        - Employees can create their own entries, which are automatically marked as visible to themselves

        **Filtering options:**
        - Filter by `employeeId` to view entries for a specific employee
        - Filter by `mood` to find entries with specific emotional indicators
        - Filter by `createdAt` or `lastUpdated` to find entries within date ranges
        - Filter by `reminder` to find entries with upcoming or past reminders

        **Use cases:**
        - Track conversations and agreements with employees
        - Document feedback and observations about team members
        - Set reminders for follow-up actions
        - Maintain a chronological record of interactions with employees
      operationId: EmployeesJournalController_listEmployeeJournals
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Journal entry ID
            - **employeeId** (string): Employee ID
            - **createdAt** (date): Journal entry creation timestamp
            - **createdBy** (string): Creator user ID
            - **mood** (set): Journal entry mood
            - **reminder** (date): Reminder date

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Journal entry ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **employeeId** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **createdAt** (date): Journal entry creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **createdBy** (string): Creator user ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **mood** (set): Journal entry mood (set filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **reminder** (date): Reminder date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Journal entry ID
            - **employeeId**: Employee ID
            - **createdAt**: Journal entry creation timestamp
            - **createdBy**: Creator user ID
            - **mood**: Journal entry mood
            - **reminder**: Reminder date

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** createdAt (desc)
          schema:
            example: '[{"field":"createdAt","direction":"desc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, employeeId, createdAt, createdBy, mood, reminder, body
          schema:
            example: id,employeeId
            type: array
            items: {}
      responses:
        '200':
          description: Paginated list of employee journal entries
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List employee journal entries
      tags:
        - employees
        - journal
        - employees
      x-required-permissions:
        - employees.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      description: |-
        Creates a new journal entry for an employee.

        **What you need to provide:**
        - `employeeId` (required): UUID of the employee this entry is about
        - `body` (required): Rich text content in HTML or Markdown format. Supports:
          - Headings, paragraphs, lists, and formatting
          - Links and embedded content
          - The content is automatically converted to the internal document format
        - `mood` (optional): Emotional indicator - Happy, Neutral, or Sad (defaults to Neutral)
        - `reminder` (optional): ISO 8601 date string for follow-up reminders (e.g., "2024-12-31")

        **Access control:**
        - Users with `Employees.edit` permission can create entries for any employee
        - Employees can create entries for their own employee record
        - The `employeeId` must exist and be accessible to the current user
        - Returns 400 if the employee does not exist or is not accessible

        **Visibility behavior:**
        - If the user is creating an entry for their own employee record, `visibleToSubject` is automatically set to `true`
        - If the user has `Employees.edit` permission and creates an entry for another employee, `visibleToSubject` is set to `false` (internal only)

        **What is returned:**
        The complete journal entry with all fields, including the body converted to HTML format and timestamps.
      operationId: EmployeesJournalController_createEmployeeJournal
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,employeeId,createdAt
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEmployeeJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeJournalResponseDto'
        '400':
          description: Validation errors or employee not accessible
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      employeeId:
                        - Employee ID is required
                        - Invalid employeeId or employee not accessible
                      body:
                        - Body content is required
                      mood:
                        - 'Mood must be one of: Happy, Neutral, Sad'
                      reminder:
                        - Reminder must be a valid ISO 8601 date string
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create employee journal entry
      tags:
        - employees
        - journal
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - employeeId
          - createdAt
        allowedFields:
          - id
          - employeeId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
  /employees/journal/{id}:
    get:
      description: |-
        Returns complete details for a specific employee journal entry.

        **What is returned:**
        - Journal entry ID and associated employee ID
        - Rich text content (body) converted to HTML format from the internal document format
        - Mood indicator (Happy, Neutral, or Sad)
        - Optional reminder date in ISO 8601 format (null if not set)
        - Creation timestamp (when the entry was created)
        - Last update timestamp (when the entry was last modified)
        - Creator user ID (who created the entry)

        **Access control:**
        - Creator of the entry can access
        - Users with `Employees.edit` permission can access any journal entry
        - Returns 404 if the entry does not exist, has been deleted, or the user does not have access

        **Note:** The body content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications.
      operationId: EmployeesJournalController_getEmployeeJournalDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeJournalResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get employee journal entry details
      tags:
        - employees
        - journal
        - employees
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Updates specific fields of an existing employee journal entry. Only provided fields are updated; all fields are optional.

        **What you can update:**
        - `employeeId` (optional): Change which employee this entry is associated with
        - `body` (optional): Update the rich text content (HTML or Markdown format)
        - `mood` (optional): Change the emotional indicator (Happy, Neutral, or Sad)
        - `reminder` (optional): Update or clear the reminder date
          - Provide an ISO 8601 date string (e.g., "2024-12-31") to set/update
          - Provide `null` to clear an existing reminder
          - Omit the field to keep the existing reminder unchanged

        **Access control:**
        - Users with `Employees.edit` permission can update any journal entry
        - Employees can update their own entries (creator or subject)
        - If updating `employeeId`, the new employee must exist and be accessible
        - Returns 404 if the entry does not exist, has been deleted, or the user does not have access
        - Returns 400 if the new `employeeId` is invalid or not accessible

        **Visibility behavior:**
        - If changing `employeeId` to the user's own employee record, `visibleToSubject` is automatically set to `true`
        - Otherwise, the existing `visibleToSubject` value is preserved

        **What is returned:**
        The complete updated journal entry with all fields, including the body converted to HTML format and updated timestamps.
      operationId: EmployeesJournalController_patchEmployeeJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,employeeId,lastUpdated
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchEmployeeJournalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeJournalResponseDto'
        '400':
          description: Validation errors or employee not accessible
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Partially update employee journal entry
      tags:
        - employees
        - journal
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - employeeId
          - lastUpdated
        allowedFields:
          - id
          - employeeId
          - createdAt
          - createdBy
          - lastUpdated
          - mood
          - reminder
          - body
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes an employee journal entry. The entry is marked as deleted but not permanently removed from the database.

        **What happens:**
        - The journal entry is marked with a `deletedAt` timestamp
        - The entry will no longer appear in list queries or be accessible via the API
        - The data is preserved in the database for audit purposes
        - The entry cannot be restored through the API (restoration would require database access)

        **Access control:**
        - Users with `Employees.edit` permission can delete any journal entry
        - Employees can delete their own entries (creator or subject)
        - Returns 404 if the entry does not exist, has already been deleted, or the user does not have access

        **Note:** This is a soft delete operation. The entry is not permanently removed and can potentially be recovered by database administrators if needed.
      operationId: EmployeesJournalController_deleteEmployeeJournal
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete employee journal entry
      tags:
        - employees
        - journal
        - employees
      x-required-scopes:
        - api:write
  /employees/grid:
    get:
      description: |-
        Returns a paginated, filterable, and sortable grid of all employees in the workspace.

        **What are Employees?**
        Employees are internal team members from your own company. Each employee can have:
        - A user account with login access (if `canLogin` is true)
        - Team assignments for organizational structure
        - Employment details (entry/exit dates, employment mode, tax class)
        - Personal information (name, email, phone, address, birth date)
        - Professional details (position, title, degree)

        **What is returned:**
        - Employee identification (ID, name)
        - Contact information (email, phone, position, title, degree)
        - Complete address details (street, city, ZIP, country, house number)
        - Employment information (employment mode, entry date, exit date, income tax class)
        - Status information (isActive, canLogin, roleId)
        - Team assignments (teamIds array)
        - Personal details (birth date, avatar)
        - Timestamps (createdAt)

        **Filtering and search:**
        - Quick search across name, email, phone, position, and address fields
        - Advanced filtering by active status, employment mode, position, role, teams, and many other fields
        - Sorting by any sortable field (default: firstName ascending)
        - Server-side pagination for large employee lists

        **Note:** This endpoint requires the `Employees.manage` permission. The employees grid is separate from the contacts grid, which combines employees and organization members.
      operationId: EmployeesController_getEmployeesGrid
      parameters:
        - name: page
          required: false
          in: query
          description: Page number (1-based)
          schema:
            example: 1
            type: number
        - name: pageSize
          required: false
          in: query
          description: Number of items per page
          schema:
            example: 100
            type: number
        - name: viewId
          required: false
          in: query
          description: View identifier for saved grid configurations
          schema:
            example: all
            type: string
        - name: quickSearch
          required: false
          in: query
          description: 'Quick search text. Searches across: firstName, lastName, email, phone, position, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber'
          schema:
            example: contacts
            type: string
        - name: filters
          required: false
          in: query
          description: |-
            JSON string containing filter configuration.

            **Available Fields:**
            - **id** (string): Employee ID
            - **firstName** (string): First name
            - **lastName** (string): Last name
            - **email** (string): Email address
            - **phone** (string): Phone number
            - **position** (string): Job position
            - **title** (string): Title (e.g., Dr., Prof.)
            - **degree** (string): Academic degree
            - **addressStreet** (string): Street address
            - **addressZip** (string): ZIP/Postal code
            - **addressCity** (string): City
            - **addressCountry** (string): Country
            - **addressHouseNumber** (string): House number
            - **employmentMode** (string): Employment mode (FullTime, Contract, Temporary, Freelance)
            - **entryDate** (date): Entry date
            - **exitDate** (date): Exit date
            - **incomeTaxClass** (string): Income tax class
            - **birthDate** (date): Birth date
            - **avatarId** (string): Avatar file ID
            - **isActive** (boolean): Whether employee is active (no exitDate or exitDate > today)
            - **canLogin** (boolean): Whether employee can login to the system
            - **roleId** (string): User role ID
            - **teamIds** (array): Team IDs (array)
            - **createdAt** (date): Creation timestamp

            **Filter Structure:**
            ```json
            [
              {
                "type": "string|number|date|set|boolean|array|task_status|task_type",
                "fieldName": "field_name",
                "value": {
                  "comparison": "comparison_type",
                  "value": "filter_value"
                }
              }
            ]
            ```

            **Group Filters (AND/OR Combinations):**
            ```json
            [
              {
                "type": "group",
                "fieldName": "group0.18807479823070028",
                "value": {
                  "type": "or",
                  "filters": [
                    {
                      "type": "string",
                      "fieldName": "fileName",
                      "value": {
                        "comparison": "contain",
                        "value": "aa"
                      }
                    },
                    {
                      "type": "number",
                      "fieldName": "rowCount",
                      "value": {
                        "comparison": ">=",
                        "value": 33
                      }
                    }
                  ]
                }
              }
            ]
            ```

            **Available Comparison Operators:**
            - **id** (string): Employee ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **firstName** (string): First name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **lastName** (string): Last name (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **email** (string): Email address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **phone** (string): Phone number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **position** (string): Job position (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **title** (string): Title (e.g., Dr., Prof.) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **degree** (string): Academic degree (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressStreet** (string): Street address (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressZip** (string): ZIP/Postal code (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCity** (string): City (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressCountry** (string): Country (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **addressHouseNumber** (string): House number (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **employmentMode** (string): Employment mode (FullTime, Contract, Temporary, Freelance) (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **entryDate** (date): Entry date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **exitDate** (date): Exit date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **incomeTaxClass** (string): Income tax class (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **birthDate** (date): Birth date (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty
            - **avatarId** (string): Avatar file ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **isActive** (boolean): Whether employee is active (no exitDate or exitDate > today) (boolean filter). Available comparisons: equal, not_equal
            - **canLogin** (boolean): Whether employee can login to the system (boolean filter). Available comparisons: equal, not_equal
            - **roleId** (string): User role ID (string filter). Available comparisons: contain, not_contain, equal, starts_with, ends_with, is_empty, is_not_empty
            - **teamIds** (array): Team IDs (array) (array filter). Available comparisons: in, not_in, is_empty, is_not_empty
            - **createdAt** (date): Creation timestamp (date filter). Available comparisons: equal, not_equal, >, <, >=, <=, between, lastMonth, thisMonth, is_empty, is_not_empty

            **Note:** Use `quickSearch` parameter for simple text search instead of adding it to filters.
          schema:
            example: '[]'
            type: string
        - name: sort
          required: false
          in: query
          description: |-
            JSON array of sort objects.

            **Sortable Fields:**
            - **id**: Employee ID
            - **firstName**: First name
            - **lastName**: Last name
            - **name**: Full name (firstName + lastName, virtual field)
            - **position**: Job position
            - **addressCity**: City
            - **addressCountry**: Country
            - **employmentMode**: Employment mode (FullTime, Contract, Temporary, Freelance)
            - **entryDate**: Entry date
            - **exitDate**: Exit date
            - **birthDate**: Birth date
            - **isActive**: Whether employee is active (no exitDate or exitDate > today)
            - **canLogin**: Whether employee can login to the system
            - **createdAt**: Creation timestamp

            **Sort Structure:**
            ```json
            [
              {
                "field": "field_name",
                "direction": "asc|desc"
              }
            ]
            ```

            **Default Sort:** firstName (asc)
          schema:
            example: '[{"field":"firstName","direction":"asc"}]'
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: |-
            Comma-separated list of field names to include in response items. If not provided, all fields are returned.

            **Available Fields:** id, firstName, lastName, name, email, phone, position, title, degree, addressStreet, addressZip, addressCity, addressCountry, addressHouseNumber, employmentMode, entryDate, exitDate, incomeTaxClass, birthDate, avatarId, isActive, canLogin, roleId, teamIds, createdAt
          schema:
            example: id,firstName
            type: array
            items: {}
      responses:
        '200':
          description: Successfully retrieved employees grid
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get employees grid
      tags:
        - employees
      x-required-permissions:
        - employees.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /employees/{id}/calendar-feed:
    get:
      description: Returns the HTTPS URL for the employee’s Leadtime calendar feed (.ics). Requires a linked user account on the employee.
      operationId: EmployeesController_getEmployeeCalendarFeed
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CalendarFeedConfigResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get employee calendar export (ICS subscription URL)
      tags:
        - employees
      x-required-permissions:
        - employees.manageCalendarSync
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
  /employees/{id}/calendar-feed/regenerate:
    post:
      description: Issues a new ICS subscription token; previous URLs stop working.
      operationId: EmployeesController_regenerateEmployeeCalendarFeed
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: url
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CalendarFeedConfigResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Regenerate employee calendar export URL
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - url
        allowedFields:
          - url
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.manageCalendarSync
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/external-calendar-feeds:
    get:
      description: Returns configured HTTPS ICS feeds that sync busy blocks into Leadtime for this employee.
      operationId: EmployeesController_listEmployeeExternalCalendarFeeds
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExternalCalendarFeedResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: List employee external calendar imports
      tags:
        - employees
      x-required-permissions:
        - employees.manageCalendarSync
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    post:
      operationId: EmployeesController_createEmployeeExternalCalendarFeed
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,label,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEmployeeExternalCalendarFeedDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalCalendarFeedResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Add external calendar feed for employee
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - label
          - enabled
        allowedFields:
          - id
          - label
          - sourceUrl
          - enabled
          - lastFetchedAt
          - lastError
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.manageCalendarSync
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/external-calendar-feeds/{feedId}:
    patch:
      operationId: EmployeesController_patchEmployeeExternalCalendarFeed
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: feedId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,label,enabled
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchEmployeeExternalCalendarFeedDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalCalendarFeedResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update employee external calendar feed
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - label
          - enabled
        allowedFields:
          - id
          - label
          - sourceUrl
          - enabled
          - lastFetchedAt
          - lastError
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.manageCalendarSync
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      operationId: EmployeesController_deleteEmployeeExternalCalendarFeed
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: feedId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Remove employee external calendar feed
      tags:
        - employees
      x-required-permissions:
        - employees.manageCalendarSync
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/external-calendar-feeds/{feedId}/sync:
    post:
      operationId: EmployeesController_syncEmployeeExternalCalendarFeed
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: feedId
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,label,enabled,lastFetchedAt,lastError
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalCalendarFeedResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Sync employee external calendar feed now
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - label
          - enabled
          - lastFetchedAt
          - lastError
        allowedFields:
          - id
          - label
          - sourceUrl
          - enabled
          - lastFetchedAt
          - lastError
          - createdAt
          - editedAt
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.manageCalendarSync
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}:
    get:
      description: |-
        Returns complete details for a specific employee, including all personal information, employment data, user account details, team assignments, and working conditions.

        **What is returned:**
        - All employee personal information (name, email, phone, address, birth date)
        - Professional details (position, title, degree)
        - Employment information (employment mode, entry date, exit date, income tax class)
        - User account details (userId, roleId, canLogin status)
        - Team assignments (array of team IDs)
        - Avatar URL (if avatar is set)
        - Status flags (isActive calculated from exitDate)
        - Working conditions (chargeableHoursDayGoal)
        - Weekly working time entries (array with dateFrom, workingDays, weeklyWorkingHours)
        - Salary entries (array with dateFrom, salary)
        - Vacation days entries (array with dateFrom, days)
        - Timestamps (createdAt, updatedAt)

        **Note:** Returns 404 if the employee does not exist or has been deleted.
      operationId: EmployeesController_getEmployeeDetails
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:read
        - bearer: []
      summary: Get employee details
      tags:
        - employees
      x-required-permissions:
        - employees.manage
      x-required-permissions-mode: all
      x-required-scopes:
        - api:read
    patch:
      description: |-
        Partially updates an existing employee. Only the fields you provide will be updated; all other fields remain unchanged.

        **How updates work:**
        - **Personal information** (firstName, lastName, email, phone, address, birth date, title, degree, position):
          - Updates the employee record
          - If email, firstName, or lastName changes, the associated user account is automatically synced
        - **Access settings** (canLogin, roleId):
          - Updates the user account permissions
          - If `canLogin` changes from `false` to `true`:
            - User account is created if it doesn't exist
            - User status becomes Active
            - In ready workspaces, an invitation email is sent automatically
            - During initial workspace setup, invitation email delivery is deferred until setup is finished
          - If email is changed and a HelpdeskUser with that email exists, it will be upgraded to Employee type
        - **Team assignments** (teams):
          - Replaces the entire team assignment list
          - Provide an empty array to remove all team assignments
          - Omit `teams` to leave team assignments unchanged
        - **Avatar** (avatarId):
          - Set to a file ID from POST /workspace/upload to update the avatar
          - Set to `null` to remove the avatar
        - **Working conditions** (chargeableHoursDayGoal):
          - Updates the chargeable (billable) hours per day goal
          - Used for time tracking targets and notifications
          - Set to `null` to remove the goal

        **Custom fields:**
        Employees do not support custom fields through this API.

        **Update process:**
        1. Verify the employee exists (returns 404 if not found)
        2. Update personal information if provided
        3. Update access settings if provided
        4. Update team assignments if provided
        5. Update working conditions if provided
        6. Return the updated employee with all current data

        **Note:** All updates are validated and will fail if validation rules are violated (e.g., email must be unique). The response includes the complete updated employee record.
      operationId: EmployeesController_updateEmployee
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,email
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchEmployeeDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Update employee
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - email
        allowedFields:
          - id
          - userId
          - email
          - firstName
          - lastName
          - position
          - phone
          - canLogin
          - isActive
          - roleId
          - teams
          - employmentMode
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
    delete:
      description: |-
        Soft deletes an employee and the associated user account. The employee is not permanently removed from the database, but marked as deleted with anonymized data.

        **What happens when deleting an employee:**
        1. Employee record is marked as deleted
        2. Associated user account is also marked as deleted
        3. Personal data is anonymized for privacy:
           - firstName and lastName are set to "Deleted User"
           - Email is anonymized
           - Other personal information is cleared
        4. Billing is automatically recalculated to reflect the removal
        5. The employee will no longer appear in employee lists or grids

        **Note:** This is a soft delete operation. The employee data remains in the database but is anonymized and hidden. This operation cannot be undone. Returns 404 if the employee does not exist.
      operationId: EmployeesController_deleteEmployee
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete employee
      tags:
        - employees
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees:
    post:
      description: |-
        Creates a new employee in the workspace with automatic user account creation and team assignments.

        **How employee creation works:**
        1. A new employee record is created with the provided information
        2. If `canLogin` is `true`, a user account is automatically created:
           - User status is set to Active
           - In ready workspaces, an invitation email is sent to the employee
           - During initial workspace setup, invitation email delivery is deferred until setup is finished
           - The user is assigned the specified role
        3. If an existing HelpdeskUser with the same email exists, it will be upgraded to Employee type
        4. Teams are assigned if provided in the `teams` array
        5. Employee data is automatically synced with the user account

        **Required fields:**
        - `firstName`: Employee first name
        - `lastName`: Employee last name
        - `email`: Unique email address (must not exist in the workspace)
        - `roleId`: Role ID to assign to the user account

        **Optional fields:**
        - `position`: Job title/position
        - `canLogin`: Whether employee can login (default: false)
        - `teams`: Array of team IDs to assign the employee to
        - `avatarId`: File ID from POST /workspace/upload endpoint

        **Custom fields:**
        Employees do not support custom fields through this API.

        **Avatar upload process:**
        1. First, upload the avatar file using POST /workspace/upload
        2. Get the file ID from the upload response
        3. Use that file ID in the `avatarId` field when creating the employee

        **Note:** The email address must be unique within the workspace. If `canLogin` is false, no user account is created initially, but one can be created later by updating the employee. To list or verify employees after creation, use GET /employees/grid if you have Employees.manage permission, or GET /workspace/users for the user list. There is no GET /employees list endpoint.
      operationId: EmployeesController_createEmployee
      parameters:
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,email
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEmployeeDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '400':
          description: Validation errors
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: number
                    example: 400
                  message:
                    type: string
                    example: Bad Request
                  errors:
                    type: object
                    example:
                      firstName:
                        - First name is required
                      lastName:
                        - Last name is required
                      email:
                        - Email is required
                        - Email must be valid
                      roleId:
                        - Role ID is required
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create employee
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - email
        allowedFields:
          - id
          - userId
          - email
          - firstName
          - lastName
          - position
          - phone
          - canLogin
          - isActive
          - roleId
          - teams
          - employmentMode
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.create
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/weekly-working-time:
    post:
      description: |-
        Creates a new weekly working time entry or updates an existing one for an employee.

        **How it works:**
        - If `id` is provided in the request body, updates the existing entry with that ID
        - If `id` is omitted, creates a new entry

        **What is weekly working time?**
        Weekly working time defines the employee's work schedule:
        - `workingDays`: Array of 7 numbers (Monday=0, Sunday=6), where 1 means the employee works that day, 0 means they don't
        - `weeklyWorkingHours`: Total hours per week (maximum 168)
        - `dateFrom`: Date when this configuration starts applying

        **Active entry selection:**
        - The active entry for a given date is the one with the most recent `dateFrom` that is still on or before that date
        - If no entry exists for a date, no entry is considered active

        **Returns:** The updated employee object with all weekly working time entries.
      operationId: EmployeesController_createOrUpdateWeeklyWorkingTime
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,email
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrUpdateWeeklyWorkingTimeDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '400':
          description: Validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update weekly working time entry
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - email
        allowedFields:
          - id
          - userId
          - email
          - firstName
          - lastName
          - position
          - phone
          - canLogin
          - isActive
          - roleId
          - teams
          - employmentMode
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/weekly-working-time/{entryId}:
    delete:
      description: |-
        Deletes a weekly working time entry for an employee.

        **Returns:** The updated employee object with remaining weekly working time entries.
      operationId: EmployeesController_deleteWeeklyWorkingTime
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: entryId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete weekly working time entry
      tags:
        - employees
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/salaries:
    post:
      description: |-
        Creates a new salary entry or updates an existing one for an employee.

        **How it works:**
        - If `id` is provided in the request body, updates the existing entry with that ID
        - If `id` is omitted, creates a new entry
        - Each entry defines a monthly salary amount that applies from a specific date

        **Returns:** The updated employee object with all salary entries.
      operationId: EmployeesController_createOrUpdateSalary
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,email
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrUpdateSalaryDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '400':
          description: Validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update salary entry
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - email
        allowedFields:
          - id
          - userId
          - email
          - firstName
          - lastName
          - position
          - phone
          - canLogin
          - isActive
          - roleId
          - teams
          - employmentMode
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/salaries/{entryId}:
    delete:
      description: |-
        Deletes a salary entry for an employee.

        **Returns:** The updated employee object with remaining salary entries.
      operationId: EmployeesController_deleteSalary
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: entryId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete salary entry
      tags:
        - employees
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/vacation-days:
    post:
      description: |-
        Creates a new vacation days entry or updates an existing one for an employee.

        **How it works:**
        - If `id` is provided in the request body, updates the existing entry with that ID
        - If `id` is omitted, creates a new entry
        - Each entry defines vacation days per year that apply from a specific date

        **Returns:** The updated employee object with all vacation days entries.
      operationId: EmployeesController_createOrUpdateVacationDays
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: fieldsToReturn
          required: false
          in: query
          description: Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
          schema:
            example: id,userId,email
            type: string
        - name: responseShape
          required: false
          in: query
          description: Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          schema:
            enum:
              - compact
              - full
            type: string
        - name: LT-Response-Shape
          in: header
          description: Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
          required: false
          schema:
            type: string
            enum:
              - full
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrUpdateVacationDaysDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '400':
          description: Validation errors
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Create or update vacation days entry
      tags:
        - employees
      x-lt-response-projection:
        defaultShape: compact
        defaultFields:
          - id
          - userId
          - email
        allowedFields:
          - id
          - userId
          - email
          - firstName
          - lastName
          - position
          - phone
          - canLogin
          - isActive
          - roleId
          - teams
          - employmentMode
        fieldSelectionQueryParam: fieldsToReturn
        fullResponseQuery: responseShape=full
        fullResponseHeader: 'LT-Response-Shape: full'
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /employees/{id}/vacation-days/{entryId}:
    delete:
      description: |-
        Deletes a vacation days entry for an employee.

        **Returns:** The updated employee object with remaining vacation days entries.
      operationId: EmployeesController_deleteVacationDays
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: entryId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponseDto'
        '401':
          description: Unauthorized - Invalid or missing authentication token
        '403':
          description: Forbidden - Insufficient API scopes or permissions
      security:
        - OAuth2:
            - api:write
        - bearer: []
      summary: Delete vacation days entry
      tags:
        - employees
      x-required-permissions:
        - employees.edit
      x-required-permissions-mode: all
      x-required-scopes:
        - api:write
  /automations/webhooks/{secret}:
    post:
      summary: Trigger automation via webhook
      description: 'Unauthenticated inbound webhook for automation triggers. No Bearer token required. The secret is webhookSecret from a webhook trigger (returned in automation triggers after create/update). POST with Content-Type: application/json; request body is passed to the agent as context. Returns 202 with { "accepted": true } when the secret matches an enabled webhook trigger and the automation executor is eligible. Returns 202 with { "accepted": false } when the secret is valid but the executor is inactive or no longer in the workspace (no run is enqueued).'
      operationId: AutomationWebhooks_handleWebhook
      tags:
        - automations
      parameters:
        - name: secret
          in: path
          required: true
          schema:
            type: string
          description: webhookSecret from the automation trigger
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              description: Arbitrary JSON; passed to the agent as webhook payload
      responses:
        '202':
          description: 'Request processed: accepted true if a run was enqueued; accepted false if the executor cannot run automations'
          content:
            application/json:
              schema:
                type: object
                properties:
                  accepted:
                    type: boolean
                    description: True when the automation run was queued; false when the executor is ineligible
        '404':
          description: Webhook not found (invalid or disabled secret)
      servers:
        - url: https://leadtime.app/api
          description: API base for webhook endpoints
      security: []
info:
  title: Leadtime Public API
  description: |-
    Versioned public API.

    Authentication:
    - Personal Access Tokens (PAT): Create/manage in your Profile → API Tokens. Use the raw token once returned to you as Bearer: `Authorization: Bearer <pat>`.
    - OAuth 2.0 (Authorization Code + PKCE): Workspace admin creates an OAuth client in Workspace Settings → API & Integrations, then obtain tokens via the standard OAuth flow. Use the access token as Bearer. Refresh tokens are supported at `/api/oauth/token`.

    Dynamic Client Registration (RFC 7591):
    The API supports RFC 7591 compliant dynamic client registration, allowing applications to register OAuth clients programmatically without requiring workspace admin setup.

    **Registration Endpoint**: `POST /api/oauth/register`

    **Features**:
    - Automatically creates OAuth clients with generated `client_id` and optional `client_secret`
    - Clients are auto-installed in workspaces when the API feature is enabled
    - Supports multiple authentication methods: `none` (public clients), `client_secret_post`, `client_secret_basic`
    - Idempotent registration: If a client with the same name and redirect URIs already exists, the existing client is updated instead of creating a duplicate
    - Returns RFC 7591 compliant registration response with client credentials and metadata

    **Request Body**:
    - `redirect_uris` (required): Array of redirect URIs for the client
    - `client_name` (optional): Display name for the client (defaults to "Dynamically Registered Client")
    - `client_uri` (optional): Homepage URL of the client application
    - `logo_uri` (optional): Logo URL for the client
    - `token_endpoint_auth_method` (optional): Authentication method (`none`, `client_secret_post`, or `client_secret_basic`, defaults to `none`)

    **Response**:
    - `client_id`: Unique client identifier (format: `dcr_<uuid>`)
    - `client_secret`: Client secret (only returned for confidential clients, not for `none` auth method)
    - `client_name`: Client display name
    - `redirect_uris`: Array of allowed redirect URIs
    - `token_endpoint_auth_method`: Authentication method used
    - `grant_types`: Supported grant types (`authorization_code`, `refresh_token`)
    - `response_types`: Supported response types (`code`)
    - `client_id_issued_at`: Unix timestamp when client was created
    - `client_secret_expires_at`: Expiration timestamp (0 means no expiration)
    - `registration_client_uri`: URI for client registration management

    **Discovery**:
    The registration endpoint URL is available via OAuth 2.0 Authorization Server Metadata at `/api/.well-known/oauth-authorization-server` (see `registration_endpoint` field).

    Imposter Mode (Testing/Debugging):
    ⚠️ **Permission Required**: `PublicApi.allowImposterMode`

    Optional headers for testing and debugging (case-insensitive):
    - `LT_IMPOSTER_USER_ID` / `lt_imposter_user_id`: Override authenticated user with specified user ID. User must exist and belong to the same workspace. Original token permissions are preserved.
    - `LT_CUSTOM_NOW` / `lt_custom_now`: Override current time with custom date (ISO 8601 format). Useful for testing time-sensitive operations.

    Mutation response projection:
    - Create/update endpoints may declare a compact default response, usually identifiers and the primary display field.
    - Use `fieldsToReturn=id,name,...` to request exact top-level response fields when the endpoint documents `x-lt-response-projection`.
    - Use `responseShape=full` or header `LT-Response-Shape: full` to opt into the complete response object.
    - Validation and error responses are never projected.

    OpenAPI specification downloads:
    - [JSON](https://leadtime.app/api/public/docs/json)
    - [YAML](https://leadtime.app/api/public/docs/yaml)
    - Load either specification into Scalar, Postman, Insomnia, or another OpenAPI client for interactive requests.

    Example:
    ```
    curl -X GET "https://api.example.com/api/public/account/info" \
      -H "Authorization: Bearer <token>" \
      -H "LT_IMPOSTER_USER_ID: user-123" \
      -H "LT_CUSTOM_NOW: 2024-01-15T10:00:00Z"
    ```

    Base URL: https://leadtime.app/api/public
  version: '1.0'
  contact: {}
tags: []
servers:
  - url: https://leadtime.app/api/public
    description: Leadtime Public API Server
components:
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: JWT
      type: http
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://leadtime.app/api/oauth/authorize
          tokenUrl: https://leadtime.app/api/oauth/token
          refreshUrl: https://leadtime.app/api/oauth/token
          scopes:
            api:read: api:read
            api:write: api:write
  schemas:
    AccountInfoResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
          description: Operation success flag
        userId:
          type: string
          example: user_123
          description: Current user ID
        workspaceId:
          type: string
          example: workspace_456
          description: Current workspace ID
        email:
          type: string
          example: jane.doe@example.com
        firstName:
          type: string
          example: Jane
          nullable: true
        lastName:
          type: string
          example: Doe
          nullable: true
        name:
          type: string
          example: Jane Doe
          description: Convenience full name
        type:
          type: string
          enum:
            - Employee
            - OrganizationMember
            - LeadtimeSupport
            - HelpdeskUser
            - Agent
          description: User type
        roleId:
          type: string
          example: role_abc
        roleName:
          type: string
          example: Admin
        permissions:
          description: Granted permission keys
          type: array
          items:
            type: string
        avatarUrl:
          type: string
          example: https://app.example.com/api/avatars/public/avatar_123.svg
          nullable: true
          description: Public URL for the generated user avatar image
        employeeId:
          type: string
          example: emp_789
          nullable: true
          description: Employee ID if user is Employee
        memberId:
          type: string
          example: null
          nullable: true
          description: Organization member ID if user is OrganizationMember
        source:
          type: string
          enum:
            - webapp
            - api_pat
            - api_oauth
            - mcp
            - agent
          description: Authentication source
        language:
          type: string
          example: en
          description: User preferred language
        emailNotifications:
          type: boolean
          example: true
          description: Email notifications enabled
        userWorkspaceColor:
          type: string
          enum:
            - INHERIT
            - DEFAULT
            - BLACK
            - RED
            - ROSE
            - MAGENTA
            - GREEN
            - BLUE
            - YELLOW
            - VIOLET
          description: User workspace color preference
      required:
        - ok
        - userId
        - workspaceId
        - email
        - firstName
        - lastName
        - name
        - type
        - roleId
        - roleName
        - permissions
        - avatarUrl
        - employeeId
        - memberId
        - source
        - language
        - emailNotifications
        - userWorkspaceColor
    EmployeeDataResponse:
      type: object
      properties:
        id:
          type: string
          example: emp_123
          description: Employee ID
        firstName:
          type: string
          example: John
          nullable: true
          description: First name
        lastName:
          type: string
          example: Doe
          nullable: true
          description: Last name
        email:
          type: string
          example: john.doe@example.com
          nullable: true
          description: Email address
        title:
          type: string
          example: Software Engineer
          nullable: true
          description: Job title
        degree:
          type: string
          example: Bachelor
          nullable: true
          description: Degree
        addressStreet:
          type: string
          example: Main Street
          nullable: true
          description: Street address
        addressZip:
          type: string
          example: '12345'
          nullable: true
          description: ZIP code
        addressCity:
          type: string
          example: Berlin
          nullable: true
          description: City
        addressCountry:
          type: string
          example: Germany
          nullable: true
          description: Country
        addressHouseNumber:
          type: string
          example: '12'
          nullable: true
          description: House number
        phone:
          type: string
          example: +49 123 456789
          nullable: true
          description: Phone number
        birthDate:
          type: string
          format: date-time
          example: '1990-01-01T00:00:00.000Z'
          nullable: true
          description: Birth date
        signatureUrl:
          type: string
          example: https://app.example.com/api/public/workspace/files/signature_123
          nullable: true
          description: Workspace-authenticated signature file URL. Send the Public API Bearer token when downloading it.
      required:
        - id
        - firstName
        - lastName
        - email
        - title
        - degree
        - addressStreet
        - addressZip
        - addressCity
        - addressCountry
        - addressHouseNumber
        - phone
        - birthDate
        - signatureUrl
    UpdateWorkspaceColorRequest:
      type: object
      properties:
        color:
          type: string
          enum:
            - INHERIT
            - DEFAULT
            - BLACK
            - RED
            - ROSE
            - MAGENTA
            - GREEN
            - BLUE
            - YELLOW
            - VIOLET
          description: New workspace color
      required:
        - color
    SuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
          description: Operation success flag
      required:
        - success
    UpdateLanguageRequest:
      type: object
      properties:
        language:
          type: string
          example: en
          description: Preferred language code
          enum:
            - en
            - de
      required:
        - language
    UpdateEmailNotificationsRequest:
      type: object
      properties:
        enabled:
          type: boolean
          example: true
          description: Enable email notifications
      required:
        - enabled
    UpdateEmployeeDataRequest:
      type: object
      properties:
        email:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        title:
          type: string
        degree:
          type: string
        addressStreet:
          type: string
        addressZip:
          type: string
        addressCity:
          type: string
        addressCountry:
          type: string
          example: US
          description: ISO 3166-1 alpha-2 country code (2 uppercase letters)
        addressHouseNumber:
          type: string
        phone:
          type: string
        birthDate:
          type: string
          description: ISO8601 date string
        signatureFileId:
          type: string
          description: File ID for signature (upload via POST /api/public/workspace/upload first)
        avatarId:
          type: object
          description: File ID for avatar (upload via POST /api/public/workspace/upload first). Set to null to remove avatar.
          nullable: true
    BankDataResponse:
      type: object
      properties:
        id:
          type: string
          example: emp_123
          description: Employee ID
        bankAccountBIC:
          type: string
          example: DEUTDEFF
          nullable: true
          description: Bank BIC code
        bankAccountIBAN:
          type: string
          example: DE89370400440532013000
          nullable: true
          description: Bank IBAN
        bankAccountName:
          type: string
          example: Deutsche Bank
          nullable: true
          description: Bank account name
        bankAccountOwner:
          type: string
          example: John Doe
          nullable: true
          description: Account owner name
      required:
        - id
        - bankAccountBIC
        - bankAccountIBAN
        - bankAccountName
        - bankAccountOwner
    UpdateBankDataRequest:
      type: object
      properties:
        bankAccountBIC:
          type: object
          example: DEUTDEFF
          description: Bank BIC code
        bankAccountIBAN:
          type: object
          example: DE89370400440532013000
          description: Bank IBAN
        bankAccountOwner:
          type: object
          example: John Doe
          description: Account owner name
        bankAccountName:
          type: object
          example: Deutsche Bank
          description: Bank name
    OrgMemberDataResponse:
      type: object
      properties:
        id:
          type: string
          example: member_123
          description: Organization member ID
        firstName:
          type: string
          example: John
          nullable: true
          description: First name
        lastName:
          type: string
          example: Doe
          nullable: true
          description: Last name
        email:
          type: string
          example: john.doe@example.com
          nullable: true
          description: Email address
        title:
          type: string
          example: Software Engineer
          nullable: true
          description: Job title
        degree:
          type: string
          example: Bachelor
          nullable: true
          description: Degree
        position:
          type: string
          example: Senior Developer
          nullable: true
          description: Position
        addressStreet:
          type: string
          example: Main Street
          nullable: true
          description: Street address
        addressZip:
          type: string
          example: '12345'
          nullable: true
          description: ZIP code
        addressCity:
          type: string
          example: Berlin
          nullable: true
          description: City
        addressCountry:
          type: string
          example: Germany
          nullable: true
          description: Country
        addressHouseNumber:
          type: string
          example: '12'
          nullable: true
          description: House number
        phone:
          type: string
          example: +49 123 456789
          nullable: true
          description: Phone number
        birthDate:
          type: string
          format: date-time
          example: '1990-01-01T00:00:00.000Z'
          nullable: true
          description: Birth date
      required:
        - id
        - firstName
        - lastName
        - email
        - title
        - degree
        - position
        - addressStreet
        - addressZip
        - addressCity
        - addressCountry
        - addressHouseNumber
        - phone
        - birthDate
    UpdateOrgMemberDataRequest:
      type: object
      properties:
        email:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        title:
          type: string
        degree:
          type: string
        position:
          type: string
        addressStreet:
          type: string
        addressZip:
          type: string
        addressCity:
          type: string
        addressCountry:
          type: string
          example: DE
          description: ISO 3166-1 alpha-2 country code (2 uppercase letters)
        addressHouseNumber:
          type: string
        phone:
          type: string
        birthDate:
          type: string
          description: ISO8601 date string
        avatarId:
          type: object
          description: File ID for avatar (upload via POST /api/public/workspace/upload first). Set to null to remove avatar.
          nullable: true
    FutureVacationResponse:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
        dateFrom:
          type: string
          example: '2024-06-01'
        dateTo:
          type: string
          example: '2024-06-05'
        status:
          type: string
          example: Approved
          enum:
            - Pending
            - Approved
            - Rejected
      required:
        - id
        - dateFrom
        - dateTo
        - status
    VacationDaysPeriodDto:
      type: object
      properties:
        from:
          type: string
          format: date-time
          example: '2024-01-01T00:00:00.000Z'
        to:
          type: string
          format: date-time
          example: '2024-12-31T23:59:59.999Z'
        days:
          type: number
          example: 25
      required:
        - from
        - to
        - days
    VacationStatsResponse:
      type: object
      properties:
        year:
          type: number
          example: 2024
        byContract:
          type: number
          example: 25
        daysPeriods:
          example:
            - from: '2024-01-01T00:00:00.000Z'
              to: '2024-12-31T23:59:59.999Z'
              days: 25
          type: array
          items:
            $ref: '#/components/schemas/VacationDaysPeriodDto'
        pendingVacationDays:
          type: number
          example: 5
        approvedFutureVacationDays:
          type: number
          example: 10
        approvedPastVacationDays:
          type: number
          example: 8
        fromOvertime:
          type: number
          example: 2
          nullable: true
        added:
          type: number
          example: 0
          nullable: true
        deducted:
          type: number
          example: 0
          nullable: true
        totalVacationDays:
          type: number
          example: 27
        totalUsedVacationDays:
          type: number
          example: 8
        totalRemainingVacationDays:
          type: number
          example: 19
        potentialVacationDays:
          type: number
          example: 3
      required:
        - year
        - byContract
        - daysPeriods
        - pendingVacationDays
        - approvedFutureVacationDays
        - approvedPastVacationDays
        - fromOvertime
        - added
        - deducted
        - totalVacationDays
        - totalUsedVacationDays
        - totalRemainingVacationDays
    VacationHistoryResponse:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
        dateFrom:
          type: string
          example: '2024-06-01T00:00:00.000Z'
        dateTo:
          type: string
          example: '2024-06-05T00:00:00.000Z'
        status:
          type: string
          example: Approved
          enum:
            - Pending
            - Approved
            - Rejected
        employeeId:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174001
        statusChangedAt:
          type: string
          example: '2024-06-02T10:30:00.000Z'
          nullable: true
        statusChangedBy:
          type: string
          example: John Doe
          nullable: true
        reason:
          type: string
          example: Family vacation
          nullable: true
      required:
        - id
        - dateFrom
        - dateTo
        - status
        - employeeId
        - statusChangedAt
        - statusChangedBy
        - reason
    VacationHistoryGridResponse:
      type: object
      properties:
        total:
          type: number
          example: 50
        page:
          type: number
          example: 1
        pageSize:
          type: number
          example: 10
        items:
          type: array
          items:
            $ref: '#/components/schemas/VacationHistoryResponse'
      required:
        - total
        - page
        - pageSize
        - items
    VacationCompensationResponse:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
        employeeId:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174001
        type:
          type: string
          example: Overtime
          enum:
            - Overtime
            - Unused
            - Bonus
        year:
          type: number
          example: 2024
        toYear:
          type: number
          example: 2025
        days:
          type: number
          example: 5
        createdAt:
          type: string
          example: '2024-06-01T10:30:00.000Z'
      required:
        - id
        - employeeId
        - type
        - year
        - toYear
        - days
        - createdAt
    VacationCompensationGridResponse:
      type: object
      properties:
        total:
          type: number
          example: 25
        page:
          type: number
          example: 1
        pageSize:
          type: number
          example: 10
        items:
          type: array
          items:
            $ref: '#/components/schemas/VacationCompensationResponse'
      required:
        - total
        - page
        - pageSize
        - items
    RequestVacationDto:
      type: object
      properties:
        dateFrom:
          format: date-time
          type: string
          example: '2024-06-01T00:00:00.000Z'
          description: Start date of vacation
        dateTo:
          format: date-time
          type: string
          example: '2024-06-05T00:00:00.000Z'
          description: End date of vacation
      required:
        - dateFrom
        - dateTo
    SicknessRowResponse:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
        dateFrom:
          type: string
          example: '2024-06-01T00:00:00.000Z'
        dateTo:
          type: string
          example: '2024-06-03T00:00:00.000Z'
        employeeId:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174001
        comment:
          type: string
          example: Flu symptoms
          nullable: true
        attachedFilesIds:
          example:
            - file1.pdf
            - file2.jpg
          type: array
          items:
            type: string
      required:
        - id
        - dateFrom
        - dateTo
        - employeeId
        - comment
        - attachedFilesIds
    SicknessGridResponse:
      type: object
      properties:
        total:
          type: number
          example: 15
        page:
          type: number
          example: 1
        pageSize:
          type: number
          example: 10
        items:
          type: array
          items:
            $ref: '#/components/schemas/SicknessRowResponse'
      required:
        - total
        - page
        - pageSize
        - items
    RequestSicknessDto:
      type: object
      properties:
        dateFrom:
          type: string
          example: '2024-06-01T00:00:00.000Z'
          description: Start date of sickness
        dateTo:
          type: string
          example: '2024-06-03T00:00:00.000Z'
          description: End date of sickness
        comment:
          type: string
          example: Flu symptoms
          description: Optional comment about the sickness
        attachedFilesIds:
          description: Array of file IDs for medical certificates (upload via POST /api/public/workspace/upload first)
          type: array
          items:
            type: string
      required:
        - dateFrom
        - dateTo
    JournalEntryResponse:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
        createdAt:
          type: string
          example: '2024-06-01T10:30:00.000Z'
        createdBy:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174001
          description: ID of the user who created the journal entry
        mood:
          type: object
          example: Happy
          description: Mood of the journal entry
        reminder:
          type: object
          example: '2024-06-15T09:00:00.000Z'
          description: Reminder date
        body:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
      required:
        - id
        - createdAt
        - createdBy
        - body
    JournalGridResponse:
      type: object
      properties:
        items:
          description: Array of journal entries
          type: array
          items:
            $ref: '#/components/schemas/JournalEntryResponse'
        total:
          type: number
          example: 150
          description: Total number of journal entries
        page:
          type: number
          example: 1
          description: Current page number
        pageSize:
          type: number
          example: 20
          description: Number of items per page
        viewId:
          type: string
          example: default
          description: View ID for the grid
      required:
        - items
        - total
        - page
        - pageSize
    CreateJournalDto:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
          description: Journal entry ID for updates
        mood:
          type: string
          example: Happy
          enum:
            - Sad
            - Neutral
            - Happy
          description: Mood of the journal entry
        reminder:
          type: string
          example: '2024-06-15T09:00:00.000Z'
          description: Reminder date
        body:
          type: string
          example: <p>Today was a great day!</p>
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
      required:
        - body
    PatchJournalDto:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
          description: Journal entry ID for updates
        mood:
          type: string
          example: Happy
          enum:
            - Sad
            - Neutral
            - Happy
          description: Mood of the journal entry
        reminder:
          type: string
          example: '2024-06-15T09:00:00.000Z'
          description: Reminder date
        body:
          type: string
          example: <p>Today was a great day!</p>
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
    UpdateVacationRequest:
      type: object
      properties:
        dateFrom:
          type: string
          example: '2024-06-01T00:00:00.000Z'
          description: Vacation start date
        dateTo:
          type: string
          example: '2024-06-05T00:00:00.000Z'
          description: Vacation end date
      required:
        - dateFrom
        - dateTo
    UpdateSicknessRequest:
      type: object
      properties:
        dateFrom:
          type: string
          example: '2024-06-01T00:00:00.000Z'
          description: Sickness start date
        dateTo:
          type: string
          example: '2024-06-05T00:00:00.000Z'
          description: Sickness end date
        comment:
          type: string
          example: Flu symptoms
          description: Comment about the sickness
        attachedFilesIds:
          example:
            - file-id-1
            - file-id-2
          description: Array of attached file IDs
          type: array
          items:
            type: string
      required:
        - dateFrom
        - dateTo
        - attachedFilesIds
    OvertimeStatsResponse:
      type: object
      properties:
        startDate:
          format: date-time
          type: string
          example: '2023-01-01T00:00:00.000Z'
          description: Start date of employment
        workingSince:
          format: date-time
          type: string
          example: '2023-01-15T00:00:00.000Z'
          description: Date when employee started working
        totalHoursLogged:
          type: number
          example: 1750.5
          description: Total hours logged
        totalCompensatedHours:
          type: number
          example: 50
          description: Total compensated hours
        workingHours:
          type: number
          example: 1600
          description: Expected working hours
        workingDays:
          type: number
          example: 200
          description: Total working days
        vacationThisYearHours:
          type: number
          example: 80
          description: Vacation hours taken this year
        paidThisYearHours:
          type: number
          example: 40
          description: Paid vacation hours this year
        unpaidThisYearHours:
          type: number
          example: 40
          description: Unpaid vacation hours this year
        vacationDaysThisYear:
          type: number
          example: 10
          description: Vacation days taken this year
        balance:
          type: number
          example: 100.5
          description: Current overtime balance in hours
      required:
        - startDate
        - workingSince
        - totalHoursLogged
        - totalCompensatedHours
        - workingHours
        - workingDays
        - vacationThisYearHours
        - paidThisYearHours
        - unpaidThisYearHours
        - vacationDaysThisYear
        - balance
    OvertimeCompensationResponse:
      type: object
      properties:
        id:
          type: string
          example: 123e4567-e89b-12d3-a456-426614174000
          description: Compensation record ID
        employeeId:
          type: string
          example: emp_789
          description: Employee ID
        type:
          type: string
          example: PAID
          description: Compensation type
        hours:
          type: number
          example: 8
          description: Number of hours compensated
        vacationYear:
          type: number
          example: 2024
          nullable: true
          description: Vacation year if applicable
        vacationDays:
          type: number
          example: 1
          nullable: true
          description: Number of vacation days if applicable
        createdAt:
          format: date-time
          type: string
          example: '2024-03-15T10:30:00.000Z'
          description: Compensation creation date
      required:
        - id
        - employeeId
        - type
        - hours
        - vacationYear
        - vacationDays
        - createdAt
    OvertimeCompensationGridResponse:
      type: object
      properties:
        total:
          type: number
          example: 25
        page:
          type: number
          example: 1
        pageSize:
          type: number
          example: 10
        items:
          type: array
          items:
            $ref: '#/components/schemas/OvertimeCompensationResponse'
      required:
        - total
        - page
        - pageSize
        - items
    AgentChatAgentDto:
      type: object
      properties:
        type:
          type: string
        label:
          type: string
        botUserId:
          type: object
        provider:
          type: object
      required:
        - type
    AgentChatListItemDto:
      type: object
      properties:
        id:
          type: string
        runId:
          type: string
        chatTitle:
          type: object
          nullable: true
        agent:
          $ref: '#/components/schemas/AgentChatAgentDto'
        createdAt:
          type: string
        lastMessageAt:
          type: string
        isDraft:
          type: boolean
        isEmptyDraft:
          type: boolean
        status:
          type: string
          enum:
            - queued
            - running
            - waitingInput
            - waitingApproval
            - done
            - failed
            - canceled
        pinnedAt:
          type: object
          nullable: true
      required:
        - id
        - runId
        - createdAt
        - lastMessageAt
        - isDraft
        - isEmptyDraft
        - status
    AgentChatListPageDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/AgentChatListItemDto'
        page:
          type: number
        pageSize:
          type: number
        hasMore:
          type: boolean
      required:
        - items
        - page
        - pageSize
        - hasMore
    PinAgentChatDto:
      type: object
      properties:
        pinned:
          type: boolean
          description: Whether the chat should be pinned
      required:
        - pinned
    AgentChatPinResponseDto:
      type: object
      properties:
        success:
          type: boolean
        pinnedAt:
          type: object
          nullable: true
      required:
        - success
    RenameAgentChatDto:
      type: object
      properties:
        title:
          type: string
          description: New chat title
      required:
        - title
    AgentChatSuccessResponseDto:
      type: object
      properties:
        success:
          type: boolean
      required:
        - success
    ClaimAgentConnectorSetupDto:
      type: object
      properties:
        setupToken:
          type: string
          description: One-time setup token generated from the agent settings page.
          example: lt_conn_abc123
        gatewayPublicUrl:
          type: string
          description: Public OpenClaw/Hermes gateway URL reachable by Leadtime for webhook delivery.
          example: https://agent.example.com
        agentId:
          type: string
          description: Runtime-specific agent id to attach this Leadtime agent to.
          example: main
        runtimeVersion:
          type: string
          description: Connector/runtime version reported by the installer.
          example: openclaw-leadtime-plugin@0.1.0
      required:
        - setupToken
        - gatewayPublicUrl
    AppendAgentSessionActivityDto:
      type: object
      properties:
        activityType:
          type: string
          enum:
            - thought
            - action
            - elicitation
            - response
            - error
            - prompt
            - log
            - modelRequest
            - modelResponse
            - toolCall
            - toolResult
        body:
          type: string
        action:
          type: string
        parameter:
          type: string
        result:
          type: string
        providerEventId:
          type: string
        providerEventType:
          type: string
        raw:
          type: object
        idempotencyKey:
          type: string
      required:
        - activityType
        - body
    UpdateAgentSessionStatusDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - queued
            - running
            - waitingInput
            - waitingApproval
            - done
            - failed
            - canceled
        message:
          type: string
        idempotencyKey:
          type: string
      required:
        - status
    ToggleFavoriteRequestDto:
      type: object
      properties:
        entityId:
          type: string
          description: The unique identifier (UUID) of the entity to favorite or unfavorite. This is the ID of the task, project, organization, or command you want to toggle.
          example: 550e8400-e29b-41d4-a716-446655440000
        entityType:
          type: string
          description: 'The type of entity being favorited. Determines which entity table to check and what metadata to return. Valid values: Project, Task, Organization, Command.'
          enum:
            - Project
            - Task
            - Organization
            - Command
          example: Task
      required:
        - entityId
        - entityType
    ToggleFavoriteResponseDto:
      type: object
      properties:
        isFavorite:
          type: boolean
          description: Whether the entity is now favorited after the toggle operation. True means the entity was added to favorites, false means it was removed.
          example: true
      required:
        - isFavorite
    FavoritesListResponseDto:
      type: object
      properties:
        favorites:
          description: Array of all favorites for the authenticated user, sorted by custom sort order (if set) or creation date. Each favorite includes entity-specific metadata. Deleted entities are automatically excluded from the results.
          items:
            type: array
            items:
              type: object
          type: array
      required:
        - favorites
    FormCategoryTranslationResponseDto:
      type: object
      properties:
        language:
          type: string
        name:
          type: object
          nullable: true
        description:
          type: object
          nullable: true
      required:
        - language
    FormCategoryResponseDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        icon:
          type: string
        description:
          type: object
          nullable: true
        sort:
          type: number
        defaultKey:
          type: object
          nullable: true
        translations:
          type: array
          items:
            $ref: '#/components/schemas/FormCategoryTranslationResponseDto'
      required:
        - id
        - name
        - icon
        - sort
        - translations
    FormCategoryTranslationRequestDto:
      type: object
      properties:
        language:
          type: string
          example: en
        name:
          type: object
          nullable: true
        description:
          type: object
          nullable: true
      required:
        - language
    CreateFormCategoryDto:
      type: object
      properties:
        name:
          type: string
        icon:
          type: string
          example: ':clipboard:'
        description:
          type: object
          nullable: true
        translations:
          type: array
          items:
            $ref: '#/components/schemas/FormCategoryTranslationRequestDto'
      required:
        - name
        - icon
        - translations
    UpdateFormCategoryDto:
      type: object
      properties:
        name:
          type: string
        icon:
          type: string
        description:
          type: object
          nullable: true
        translations:
          type: array
          items:
            $ref: '#/components/schemas/FormCategoryTranslationRequestDto'
    SortFormCategoriesDto:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
      required:
        - ids
    SortFormTemplatesDto:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
      required:
        - ids
    FormTemplateFieldResponseDto:
      type: object
      properties:
        id:
          type: string
        templateId:
          type: string
        sort:
          type: number
        key:
          type: object
          nullable: true
          description: When set, this string is the `valueKey` for PATCH `/tasks/{identifier}/form-instances/{formInstanceId}` payloads; when null, use the field `id` instead.
        label:
          type: string
        description:
          type: string
        type:
          type: string
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
            - SectionHeading
            - FileUpload
            - Signature
            - Radio
        placeholder:
          type: object
          nullable: true
        helpText:
          type: object
          nullable: true
        required:
          type: boolean
        settings:
          type: object
          description: Type-specific settings JSON
        selectOptions:
          type: array
          items:
            type: object
          description: Options for Select, MultiSelect, Radio
        createdAt:
          format: date-time
          type: string
        updatedAt:
          format: date-time
          type: string
      required:
        - id
        - templateId
        - sort
        - label
        - description
        - type
        - required
        - settings
        - selectOptions
        - createdAt
        - updatedAt
    SortFormTemplateFieldsDto:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
      required:
        - ids
    CreateFormTemplateFieldDto:
      type: object
      properties:
        sort:
          type: number
        key:
          type: object
          nullable: true
        label:
          type: string
        description:
          type: string
        type:
          type: string
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
            - SectionHeading
            - FileUpload
            - Signature
            - Radio
        placeholder:
          type: object
          nullable: true
        helpText:
          type: object
          nullable: true
        required:
          type: boolean
        settings:
          type: object
        selectOptions:
          type: array
          items:
            type: object
      required:
        - label
        - type
    UpdateFormTemplateFieldDto:
      type: object
      properties:
        sort:
          type: number
        key:
          type: object
          nullable: true
        label:
          type: string
        description:
          type: string
        type:
          type: string
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
            - SectionHeading
            - FileUpload
            - Signature
            - Radio
        placeholder:
          type: object
          nullable: true
        helpText:
          type: object
          nullable: true
        required:
          type: boolean
        settings:
          type: object
        selectOptions:
          type: array
          items:
            type: object
    FormTemplateListItemResponseDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        categoryId:
          type: string
        tags:
          description: Assigned form-template tag IDs (UUIDs).
          type: array
          items:
            type: string
        icon:
          type: object
          nullable: true
        sort:
          type: number
        fieldsCount:
          type: number
          description: Number of non-deleted fields on this template.
        createdAt:
          format: date-time
          type: string
        updatedAt:
          format: date-time
          type: string
        createdBy:
          type: string
        updatedBy:
          type: object
          nullable: true
        category:
          type: object
      required:
        - id
        - name
        - description
        - categoryId
        - tags
        - sort
        - fieldsCount
        - createdAt
        - updatedAt
        - createdBy
        - category
    FormTemplateDetailResponseDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        categoryId:
          type: string
        tags:
          description: Assigned form-template tag IDs (UUIDs).
          type: array
          items:
            type: string
        icon:
          type: object
          nullable: true
        sort:
          type: number
        fieldsCount:
          type: number
          description: Number of non-deleted fields on this template.
        createdAt:
          format: date-time
          type: string
        updatedAt:
          format: date-time
          type: string
        createdBy:
          type: string
        updatedBy:
          type: object
          nullable: true
        category:
          type: object
        fields:
          type: array
          items:
            $ref: '#/components/schemas/FormTemplateFieldResponseDto'
      required:
        - id
        - name
        - description
        - categoryId
        - tags
        - sort
        - fieldsCount
        - createdAt
        - updatedAt
        - createdBy
        - category
        - fields
    CreateFormTemplateDto:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        categoryId:
          type: string
        tags:
          description: Tag IDs (UUIDs) of type form-template. List or create tags via GET/POST `tags/form-template/list` and `tags/form-template/create`.
          type: array
          items:
            type: string
        icon:
          type: object
          nullable: true
      required:
        - name
        - categoryId
    UpdateFormTemplateDto:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        categoryId:
          type: string
        tags:
          description: Tag IDs (UUIDs) of type form-template. List tags via GET `tags/form-template/list`.
          type: array
          items:
            type: string
        icon:
          type: object
          nullable: true
    AttendanceRequestDto:
      type: object
      properties:
        date:
          type: string
          description: Date of the workday in ISO 8601 format (YYYY-MM-DD). This is the date for which attendance is being recorded.
          example: '2025-01-15'
        timeFrom:
          type: string
          description: 'Clock-in time (start of work) in HH:mm format (24-hour format). This is when the employee started working. Example: "09:00" for 9 AM, "13:30" for 1:30 PM.'
          example: '09:00'
          pattern: ^([0-1][0-9]|2[0-3]):[0-5][0-9]$
        timeTo:
          type: string
          description: 'Clock-out time (end of work) in HH:mm format (24-hour format). Optional - if not provided, you can clock out later. Must be after timeFrom if provided. Example: "17:30" for 5:30 PM.'
          example: '17:30'
          pattern: ^([0-1][0-9]|2[0-3]):[0-5][0-9]$
        mood:
          type: string
          description: 'Employee mood for the workday. Indicates how the employee felt during the day: Good (positive), Neutral (normal), or Bad (challenging).'
          enum:
            - Good
            - Neutral
            - Bad
          example: Good
        comment:
          type: string
          description: Optional notes or comments about the workday. Can include information about the work performed, special circumstances, or any relevant details.
          example: Regular workday
      required:
        - date
        - timeFrom
        - mood
    AttendanceResponseDto:
      type: object
      properties:
        date:
          type: string
          description: Date of the attendance record
          example: '2025-01-15T00:00:00.000Z'
        timeFrom:
          type: string
          description: Clock-in time (when work started). ISO 8601 datetime format. Null if not yet clocked in.
          example: '1970-01-01T09:00:00.000Z'
          nullable: true
        timeTo:
          type: string
          description: Clock-out time (when work ended). ISO 8601 datetime format. Null if not yet clocked out.
          example: '1970-01-01T17:30:00.000Z'
          nullable: true
        totalHours:
          type: number
          description: Total hours worked, automatically calculated from clock-in and clock-out times. Null if end time is not yet recorded.
          example: 8.5
          nullable: true
        mood:
          type: string
          description: Employee mood for the workday (Good, Neutral, or Bad). Indicates how the employee felt during the day.
          enum:
            - Good
            - Neutral
            - Bad
          example: Good
          nullable: true
        comment:
          type: string
          description: Optional notes or comments about the workday. Can include information about work performed or special circumstances.
          example: Regular workday
          nullable: true
        type:
          type: string
          description: Type of attendance record. Indicates the nature of the workday (e.g., Workday, Holiday, etc.).
          enum:
            - Workday
            - Vacation
            - VacationPending
            - Sick
            - Away
          example: Workday
        deletedAt:
          type: string
          description: Deletion timestamp (null if not deleted)
          example: null
          nullable: true
      required:
        - date
        - timeFrom
        - timeTo
        - totalHours
        - mood
        - comment
        - type
        - deletedAt
    DeleteAttendanceRequestDto:
      type: object
      properties:
        date:
          type: string
          description: Date of the attendance record to delete in ISO 8601 format (YYYY-MM-DD). The attendance record for this date will be permanently deleted.
          example: '2025-01-15'
      required:
        - date
    CreateWorkspaceProviderApiKeyDto:
      type: object
      properties:
        provider:
          type: string
          enum:
            - cursorCloud
            - claudeManaged
        apiKey:
          type: string
          description: Raw provider API key to save securely.
        label:
          type: string
          nullable: true
      required:
        - provider
        - apiKey
    CreateWorkspaceAgentSkillDto:
      type: object
      properties:
        name:
          type: string
          description: Shared workspace agent skill slug used in SKILL.md frontmatter. Lowercase letters, numbers, and hyphens only.
          example: invoice-analysis
        displayTitle:
          type: string
          description: Human-readable shared workspace agent skill title shown in Leadtime.
        description:
          type: string
          description: Short description used for native and hosted agent skill discovery. Maximum 1024 characters.
        body:
          type: string
          description: Markdown instructions loaded when the skill is activated by a native custom agent or Leadtime-managed hosted agent.
      required:
        - name
        - displayTitle
        - description
        - body
    CreateAgentDto:
      type: object
      properties:
        name:
          type: string
          description: Agent display name.
          example: Company Manager
        roleId:
          type: string
          description: Role id to assign. Defaults to the workspace Agent role.
        connectionProvider:
          type: string
          enum:
            - selfHosted
            - claudeManaged
            - cursorCloud
            - geminiManaged
            - geminiAgentRuntime
            - native
          description: Optional agent connection provider to initialize. Use native for normal user-created custom agents running on Leadtime internal agent engine. Use selfHosted for external wrappers/webhooks, cursorCloud for Cursor Cloud, and claudeManaged only for explicit Anthropic/Claude Managed requests.
        nativeConfig:
          type: object
          description: Optional native custom-agent config for connectionProvider=native. Instructions and selected workspace skills can also be configured later through /agents/{id}/native.
        avatarConfig:
          type: object
          description: Generated avatar configuration JSON.
      required:
        - name
    UpdateAgentBodyDto:
      type: object
      properties:
        name:
          type: string
          description: Agent display name. Omit to keep the current name.
          example: Company Manager
        roleId:
          type: string
          description: Role id to assign. Omit to keep the current role.
        connectionProvider:
          type: string
          enum:
            - selfHosted
            - claudeManaged
            - cursorCloud
            - geminiManaged
            - geminiAgentRuntime
            - native
          description: Optional agent connection provider. Keep or set native for normal custom agents running on Leadtime internal agent engine. Use selfHosted for external wrappers/webhooks, cursorCloud for Cursor Cloud, and claudeManaged only for explicit Anthropic/Claude Managed requests.
        nativeConfig:
          type: object
          description: Optional native custom-agent config for connectionProvider=native. Instructions and selected workspace skills can also be configured later through /agents/{id}/native.
        avatarConfig:
          type: object
          description: Generated avatar configuration JSON.
        removeAvatar:
          type: boolean
          description: When true, removes the agent avatar image.
    SetPublicAgentStatusDto:
      type: object
      properties: {}
    SetPublicAgentConnectionProviderDto:
      type: object
      properties: {}
    NativeCustomAgentConfigDto:
      type: object
      properties:
        instructions:
          type: object
          description: Required custom instructions for a native custom agent. Accepts Markdown, HTML, or ProseMirror IDoc JSON. These instructions are appended to the default Leadtime native agent prompt.
        workspaceSkillIds:
          description: Shared workspace agent skill ids selected for this native custom agent. Get or create skills through the workspace agent skills API, then save their UUIDs here.
          type: array
          items:
            type: string
        toolMode:
          type: string
          description: Leadtime tool exposure mode. Full mode gives the native Leadtime agent its normal Leadtime tools. Basic mode restricts Leadtime tools to task read/comment/status/assignee actions and task-bound time booking while keeping custom instructions, workspace skills, and custom MCP tools.
          enum:
            - basic
            - full
        defaultModelId:
          type: string
          description: Default native runtime model alias. Use auto unless the agent should prefer a specific catalog model.
          enum:
            - auto
            - gemini-3-flash
            - claude-haiku-4-5
            - claude-sonnet-4-6
            - claude-opus-4-8
            - gemini-3-1-pro
            - gemini-3-1-flash-lite
            - gemini-2-5-flash
            - gpt-5-mini
            - gpt-5-5
            - grok-4-3
            - kimi-k2-6
            - deepseek-v4-flash
            - deepseek-v4-pro
            - glm-5-2
      required:
        - instructions
    SavePublicNativeCustomAgentSettingsDto:
      type: object
      properties:
        config:
          $ref: '#/components/schemas/NativeCustomAgentConfigDto'
        enabled:
          type: boolean
          description: Deprecated compatibility field. Pause/resume agents with the normal agent status endpoint instead of a separate native-agent enabled flag.
    SavePublicNativeCustomAgentMcpConnectionDto:
      type: object
      properties:
        id:
          type: object
          description: Existing MCP connection id to update.
        name:
          type: string
          description: MCP server name.
        url:
          type: string
          description: MCP server URL.
        authMode:
          type: string
          description: 'Auth mode: none, bearer, or oauth.'
        bearerToken:
          type: object
          description: Bearer token for bearer auth. Write-only.
        oauthClientId:
          type: object
          description: OAuth client id for advanced OAuth setup.
        oauthClientSecret:
          type: object
          description: OAuth client secret for advanced OAuth setup. Write-only.
    PublicNativeCustomAgentMcpOAuthConnectUrlDto:
      type: object
      properties:
        oauthClientId:
          type: object
          description: OAuth client id for advanced OAuth setup.
        oauthClientSecret:
          type: object
          description: OAuth client secret for advanced OAuth setup. Write-only.
        returnUrl:
          type: object
          description: Browser URL to return to after OAuth completes.
    UpdatePublicAgentSettingsDto:
      type: object
      properties: {}
    SetPublicAgentConnectionEnabledDto:
      type: object
      properties: {}
    SavePublicCursorCloudAgentSettingsDto:
      type: object
      properties: {}
    ValidatePublicCursorCloudConnectionDto:
      type: object
      properties: {}
    ClaudeManagedMcpOAuthCredentialDto:
      type: object
      properties:
        accessToken:
          type: string
          nullable: true
        refreshToken:
          type: string
          nullable: true
        expiresAt:
          type: string
          nullable: true
        tokenEndpoint:
          type: string
          nullable: true
        tokenEndpointAuthType:
          type: string
          enum:
            - none
            - client_secret_basic
            - client_secret_post
          nullable: true
        scope:
          type: string
          nullable: true
        clientId:
          type: string
          nullable: true
        clientSecret:
          type: string
          nullable: true
    ClaudeManagedMcpServerDto:
      type: object
      properties:
        id:
          type: string
          description: Stable local id for an existing MCP row.
        name:
          type: string
          description: Display name for the MCP server. If omitted, Leadtime derives one from the URL.
        url:
          type: string
          description: Hosted MCP server URL.
        authMode:
          type: string
          enum:
            - none
            - bearer
            - oauth
          description: Authentication mode for this MCP server.
        bearerToken:
          type: string
          nullable: true
          description: Bearer token to store for bearer-token MCP servers.
        credentialConfigured:
          type: boolean
          description: Whether Leadtime already has a stored credential for this MCP server.
        oauthCredential:
          nullable: true
          description: OAuth credential details for an OAuth MCP server.
          type: object
          allOf:
            - $ref: '#/components/schemas/ClaudeManagedMcpOAuthCredentialDto'
      required:
        - url
    ClaudeManagedSkillRefDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - anthropic
            - custom
        skillId:
          type: string
          description: Anthropic built-in skill id (for example xlsx) or Leadtime workspace skill id for custom skills.
        version:
          type: string
          nullable: true
          description: Optional Anthropic skill version. Defaults to latest when omitted.
      required:
        - type
        - skillId
    ClaudeManagedLeadtimeManagedAgentDto:
      type: object
      properties:
        instructions:
          oneOf:
            - type: string
            - type: object
          description: |
            Standard Leadtime editor field for managed Claude agent instructions. Accepts Markdown, HTML, or ProseMirror IDoc JSON (object or JSON string); Leadtime converts it to internal IDoc before storing and syncing the rendered HTML to Claude.

            Use only the nodes and marks documented below for this field.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
        modelId:
          type: string
          example: claude-sonnet-4-6
          description: Claude model id from the agent settings model cache/resources refresh, for example claude-sonnet-4-6 or claude-opus-4-8.
        mcpServers:
          description: Custom hosted MCP servers to attach to this Claude agent.
          type: array
          items:
            $ref: '#/components/schemas/ClaudeManagedMcpServerDto'
        skills:
          description: Skills attached to this Leadtime-managed Claude agent. Maximum 20. Only supported when agentSource is leadtimeManaged.
          type: array
          items:
            $ref: '#/components/schemas/ClaudeManagedSkillRefDto'
    ClaudeManagedGithubRepositoryDto:
      type: object
      properties:
        id:
          type: number
          description: GitHub repository id.
        name:
          type: string
          description: Repository short name.
        fullName:
          type: string
          description: Repository full name, for example owner/repo.
        url:
          type: string
          description: GitHub web URL.
        cloneUrl:
          type: string
          nullable: true
          description: Repository clone URL.
        private:
          type: boolean
          description: Whether the GitHub repository is private.
      required:
        - id
        - name
        - fullName
        - url
        - cloneUrl
        - private
    ClaudeManagedAgentConfigDto:
      type: object
      properties:
        agentSource:
          type: string
          enum:
            - leadtimeManaged
            - existing
          description: Use leadtimeManaged when Leadtime should create and repair the Claude agent, environment, and vault.
        agentId:
          type: string
          nullable: true
          description: Existing Anthropic agent id when agentSource is existing.
        environmentId:
          type: string
          nullable: true
          description: Existing Anthropic environment id when agentSource is existing.
        userVaultIds:
          description: Additional user-managed Claude vault ids to attach to sessions.
          type: array
          items:
            type: string
        toolMode:
          type: string
          enum:
            - basic
            - full
          description: Leadtime MCP tool set exposed to Claude task sessions.
        exposeRawApiCredentialToAgent:
          type: boolean
          description: Expose a raw Leadtime API credential to the agent for trusted automation.
        allowNetworkAccess:
          type: boolean
          description: Allow unrestricted outbound network access from the Claude cloud sandbox. When disabled, Leadtime still allows its own app/API/MCP hosts.
        customGuidance:
          type: string
          nullable: true
          description: Extra guidance appended to Leadtime task-session instructions.
        managedAgent:
          description: Leadtime-managed Claude agent configuration. Put modelId, instructions, and mcpServers here.
          allOf:
            - $ref: '#/components/schemas/ClaudeManagedLeadtimeManagedAgentDto'
        githubRepositoryDefaults:
          description: Default GitHub repositories mounted into Claude coding sessions for this agent.
          type: array
          items:
            $ref: '#/components/schemas/ClaudeManagedGithubRepositoryDto'
    SavePublicClaudeManagedAgentSettingsDto:
      type: object
      properties:
        enabled:
          type: boolean
          description: Enable or disable this agent connection.
        providerApiKeyId:
          type: string
          nullable: true
          description: Saved Anthropic provider API key id to use for this agent.
        anthropicApiKey:
          type: string
          nullable: true
          description: Raw Anthropic API key. Prefer providerApiKeyId when a saved key exists.
        config:
          description: Claude Managed configuration. Put model changes under config.managedAgent.modelId and GitHub defaults under config.githubRepositoryDefaults.
          allOf:
            - $ref: '#/components/schemas/ClaudeManagedAgentConfigDto'
    ValidatePublicClaudeManagedConnectionDto:
      type: object
      properties:
        providerApiKeyId:
          type: string
          nullable: true
          description: Saved Anthropic provider API key id to validate.
        anthropicApiKey:
          type: string
          nullable: true
          description: Raw Anthropic API key to validate.
    PublicClaudeManagedMcpOAuthConnectUrlDto:
      type: object
      properties:
        server:
          $ref: '#/components/schemas/ClaudeManagedMcpServerDto'
      required:
        - server
    SaveProjectAgentProviderSettingsDto:
      type: object
      properties: {}
    ValidateProjectAgentProviderSettingsDto:
      type: object
      properties: {}
    ManualPositionCategoryResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique category identifier. A UUID that uniquely identifies this category within the workspace. Use this ID when assigning the category to manual positions or when updating/deleting the category.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Category name. The display name of the category as it appears in the application. This is the name that will be shown when the category is assigned to manual positions in offers and invoices.
          example: Development
      required:
        - id
        - name
    CreateOrUpdateManualPositionCategoryDto:
      type: object
      properties:
        id:
          type: string
          description: Category identifier (UUID). When provided, updates the existing category with this ID. When omitted or set to `null`, creates a new category. Must be a valid UUID format if provided.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: 'Category name. A descriptive label for organizing manual positions. Examples: "Development", "Consulting", "Travel Expenses", "External Services", "Licenses". Must be a non-empty string. Category names are case-sensitive and can be used across all projects in the workspace.'
          example: Development
      required:
        - name
    WorkspaceDetailsResponse:
      type: object
      properties:
        id:
          type: string
          example: workspace_456
          description: Unique identifier for the workspace. Used to reference the workspace in other API endpoints.
        domain:
          type: string
          example: company-domain
          description: Workspace domain/subdomain identifier. Used in workspace URLs and API routing.
        companyName:
          type: string
          example: Acme Corp
          description: Company or organization name associated with the workspace. This is the display name shown throughout the application.
        isBillingLocked:
          type: boolean
          example: false
          description: Indicates whether the workspace is locked due to past due billing status. When true, certain workspace operations may be restricted until billing issues are resolved.
        attendanceEnabled:
          type: boolean
          example: false
          description: Indicates whether attendance tracking (clock in/out) is enabled for the workspace AND the current user is an Employee type. This field is false if attendance is disabled or if the current user is not an Employee.
        isHelpdeskEnabled:
          type: boolean
          example: true
          description: Indicates whether the helpdesk feature is enabled for the workspace. When enabled, the workspace can use ticket-based support workflows.
        features:
          type: array
          description: Array of all enabled workspace features. Includes both currently active features and features that are still available but not yet activated. Features define what functionality is available in the workspace.
          items:
            type: string
            enum:
              - HELPDESK
              - API
              - AGENT
        workspaceLanguage:
          type: string
          example: en
          description: Default language code for the workspace (e.g., "en" for English, "de" for German). This setting applies to new users; individual users can override this in their profile settings.
        sprintStartDay:
          type: number
          example: 1
          description: Day of the week when sprints/pipeline periods start. 1 = Monday, 2 = Tuesday, ..., 7 = Sunday. Used for capacity planning and pipeline views.
        sprintPeriodDays:
          type: number
          example: 14
          description: Duration of sprint/pipeline periods in days. Typically 7 (one week) or 14 (two weeks). Defines the time span for capacity planning in the pipeline view.
        mainWorkspaceColor:
          type: string
          enum:
            - DEFAULT
            - BLACK
            - RED
            - ROSE
            - MAGENTA
            - GREEN
            - BLUE
            - YELLOW
            - VIOLET
          description: Main accent color theme for the workspace interface. Options include Default, Black, Red, Pink, Magenta, Green, Blue, Yellow, Purple. This color affects navigation elements, buttons, and active states throughout the application.
        logoUrl:
          type: object
          example: https://app.example.com/api/files/public/logo_123
          nullable: true
          description: 'Public URL for the workspace logo image used in light mode. This logo appears in the top left corner of the workspace above the navigation. Null if no logo has been uploaded. Recommended format: PNG with transparent background, ideal size 1000x260 pixels.'
        logoDarkThemeUrl:
          type: object
          example: https://app.example.com/api/files/public/logo_dark_456
          nullable: true
          description: 'Public URL for the workspace logo image used in dark mode. This logo appears when users have dark theme enabled. Null if no dark theme logo has been uploaded. Recommended format: PNG with transparent background, ideal size 1000x260 pixels.'
        branding:
          type: object
          description: Workspace branding settings including custom brand color. The brandColor field (if set) is a HEX color code that matches the company brand and is used for internal projects in various views of the application.
        customIcons:
          description: Array of custom icons available in the workspace. Custom icons are user-uploaded images (PNG or SVG) that can be used to visually tag tickets, projects, documents, or other entities. Each icon has a unique name (e.g., ":team_sales:") and an associated image file ID.
          type: array
          items:
            type: string
      required:
        - id
        - domain
        - companyName
        - isBillingLocked
        - attendanceEnabled
        - isHelpdeskEnabled
        - features
        - workspaceLanguage
        - sprintStartDay
        - sprintPeriodDays
        - mainWorkspaceColor
        - logoUrl
        - logoDarkThemeUrl
        - branding
        - customIcons
    ObjectStatusDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        color:
          type: string
        sort:
          type: number
        createdAt:
          type: string
      required:
        - id
        - name
        - color
        - sort
        - createdAt
    ObjectCustomFieldInTypeDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        type:
          type: string
        sort:
          type: number
        objectTypeId:
          type: string
        showInList:
          type: boolean
        showInHeader:
          type: boolean
        selectOptions:
          type: array
        translations:
          type: array
      required:
        - id
        - name
        - description
        - type
        - sort
        - objectTypeId
        - showInList
        - showInHeader
        - selectOptions
        - translations
    ObjectTypeDetailResponseDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        slug:
          type: string
        icon:
          type: object
          nullable: true
        sort:
          type: number
        createdAt:
          type: string
        statuses:
          type: array
          items:
            $ref: '#/components/schemas/ObjectStatusDto'
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/ObjectCustomFieldInTypeDto'
      required:
        - id
        - name
        - slug
        - icon
        - sort
        - createdAt
        - statuses
        - customFields
    WorkspaceObjectSettingsResponseDto:
      type: object
      properties:
        objectTypes:
          description: All non-deleted object types with nested statuses and per-type custom field definitions (read-only bundle for forms and integrations).
          type: array
          items:
            $ref: '#/components/schemas/ObjectTypeDetailResponseDto'
      required:
        - objectTypes
    FileUploadResponse:
      type: object
      properties:
        id:
          type: string
          example: file_123
          description: Unique file identifier. Use this ID to reference the file in other API endpoints that accept file references (e.g., when setting workspace logos, user avatars, or custom icons).
        filename:
          type: string
          example: logo.png
          description: Original filename of the uploaded file as provided by the client. This is preserved for reference but may differ from the stored filename.
        mimetype:
          type: string
          example: image/png
          description: MIME type of the uploaded file (e.g., "image/png", "image/jpeg", "image/svg+xml"). Indicates the file format and how it should be processed or displayed.
        size:
          type: number
          example: 1024
          description: File size in bytes. Use this to validate file size limits or display file size information to users.
        url:
          type: string
          example: https://app.example.com/api/public/workspace/files/file_123
          description: Workspace-authenticated download URL. Public API clients must send the same Bearer token used for the upload request.
      required:
        - id
        - filename
        - mimetype
        - size
        - url
    WorkspaceUserDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the user. Use this ID to reference the user in other API endpoints or when assigning tasks, projects, or other entities to users.
        name:
          type: string
          description: Full display name of the user, constructed from firstName and lastName. This is the name shown in user lists, dropdowns, and throughout the application interface.
        avatarId:
          type: object
          description: File ID of the user avatar image. Use this ID with the file API to retrieve the avatar image URL. Null if no avatar has been uploaded for this user.
        position:
          type: string
          description: Job title or position of the user within the workspace or organization. This field may be empty for users who do not have a position set.
        employeeId:
          type: object
          description: Employee ID associated with this user, if the user is an Employee type. This links the user to their employee record which contains additional employee-specific information. Null if the user is not an Employee or does not have an associated employee record.
        userType:
          type: string
          enum:
            - Employee
            - OrganizationMember
            - LeadtimeSupport
            - HelpdeskUser
            - Agent
          description: 'Type of user account. Options: Employee (internal workspace employee), OrganizationMember (external organization member), LeadtimeSupport (Leadtime support staff), Agent (workspace automation/service account). The user type determines what features and data the user can access.'
        canLogin:
          type: boolean
          description: Indicates whether the user account is active and can log in to the workspace. False if the user account is inactive, suspended, or otherwise unable to authenticate.
        isDeleted:
          type: boolean
          description: Indicates whether the user account has been soft-deleted. Deleted users are typically hidden from active user lists but may still be included in responses depending on the includeDeleted query parameter.
        agentSystemKey:
          type: object
          description: System agent provider key for agent users, such as cursorCloud or claudeManaged. Null for human users and generic self-hosted agents.
          nullable: true
      required:
        - id
        - name
        - position
        - userType
        - canLogin
        - isDeleted
    QuickSearchResultDto:
      type: object
      properties:
        title:
          type: string
          description: Display title or name of the entity. This is the primary text shown in search results and is what the search term matched against.
          example: Project Alpha
        type:
          type: string
          description: Type of entity that was found. Indicates which kind of workspace resource this result represents. Use this to determine how to handle or display the result in your application.
          enum:
            - employee
            - organization
            - organizationMember
            - workspace
            - project
            - task
            - team
            - contact
          example: project
        meta:
          type: object
          description: Entity metadata object containing the entity ID and additional type-specific fields. The id field is always present and can be used to fetch full entity details. Other fields vary by entity type (e.g., projectId for tasks, organizationId for projects).
          example:
            id: uuid
            projectId: uuid
            organizationId: uuid
        color:
          type: string
          description: Color associated with the entity, typically in HEX format (e.g., "#ff0000"). Used for visual identification in lists, kanban boards, and other UI components. May be null if the entity type does not support colors.
          example: '#ff0000'
        icon:
          type: object
          description: Icon identifier for the entity, typically a FontAwesome icon class name (e.g., "faSquare", "faUser"). Used for visual representation in UI components. May be null if no icon is set for the entity.
          example: faSquare
          nullable: true
        shortName:
          type: object
          description: Short name or code for the entity, often used for quick identification (e.g., "ORG-001", "PRJ-123"). This is a human-readable identifier that may be displayed alongside the title. May be null if the entity type does not use short names.
          example: ORG-001
          nullable: true
      required:
        - title
        - type
        - meta
    ProductListItemDto:
      type: object
      properties:
        id:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for the product. Use this ID to reference the product in other API endpoints or when adding the product to projects or tasks.
        name:
          type: string
          example: Premium Software License
          description: Display name of the product. This is the name shown in product lists, dropdowns, and when the product is added to projects or tasks.
        categoryId:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440001
          description: Identifier of the product category this product belongs to. Categories help organize products in the catalog and can be used for filtering or grouping.
        logoId:
          type: object
          example: 550e8400-e29b-41d4-a716-446655440002
          nullable: true
          description: File ID of the product logo image. Use this ID with the file API to retrieve the logo image URL. Null if no logo has been uploaded for this product.
        priceFixed:
          type: object
          example: 5000
          nullable: true
          description: Fixed one-time price for the product, typically in the smallest currency unit (e.g., cents for USD). Null if this product does not have a fixed price. Products can have multiple pricing types (fixed, subscription, per-unit) or just one.
        priceSubscription:
          type: object
          example: 25
          nullable: true
          description: Recurring subscription price for the product, typically in the smallest currency unit per billing period (e.g., cents for USD). Null if this product does not have subscription pricing. Used for products that are billed on a recurring basis.
        pricePerUnit:
          type: object
          example: 150
          nullable: true
          description: Per-unit price for the product, typically in the smallest currency unit (e.g., cents for USD). Null if this product does not have per-unit pricing. Used when the product price depends on quantity.
      required:
        - id
        - name
        - categoryId
        - logoId
        - priceFixed
        - priceSubscription
        - pricePerUnit
    WorkspaceFormTemplatesResponseDto:
      type: object
      properties:
        categories:
          type: array
          items:
            $ref: '#/components/schemas/FormCategoryResponseDto'
        templates:
          type: array
          items:
            $ref: '#/components/schemas/FormTemplateListItemResponseDto'
      required:
        - categories
        - templates
    DocumentTemplateApiResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the document template (UUID)
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: The name/title of the document template
          example: Project Contract Template
        description:
          type: string
          description: A brief description explaining the purpose or use case of this template
          example: Standard contract template for project agreements
        language:
          type: string
          description: ISO 639-1 language code for the template (e.g., "en", "de")
          example: en
        type:
          type: string
          description: The type of document this template generates
          enum:
            - ProjectDocument
          example: ProjectDocument
        content:
          type: string
          description: 'Template content as HTML. The content includes static text, formatting, and variable placeholders. Variables are represented as HTML spans: `<span data-type="variable" data-id="project.name">...</span>`. The content may also include conditional sections and page breaks.'
          example: '<h1>Project Contract</h1><p>Project: <span data-type="variable" data-id="project.name">Website Redesign</span></p>'
        workspaceId:
          type: string
          description: The workspace ID this template belongs to
          example: 123e4567-e89b-12d3-a456-426614174000
        createdAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp when the template was created
          example: '2024-01-15T10:30:00Z'
        updatedAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp when the template was last updated
          example: '2024-01-20T14:45:00Z'
        createdBy:
          type: string
          description: User ID of the user who created this template
          example: 123e4567-e89b-12d3-a456-426614174000
        updatedBy:
          type: object
          description: User ID of the user who last updated this template (null if never updated)
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        documentCustomVariables:
          description: Array of custom variables defined for this template. Custom variables extend beyond standard system variables and allow template-specific placeholders. Each variable includes its type, default value (if any), options (for Select/MultiSelect), and whether it is required when generating documents from this template.
          example:
            - id: var-123
              name: client_name
              description: Client company name
              type: Text
              value: Acme Corp
              options: []
              required: true
          type: array
          items:
            type: object
      required:
        - id
        - name
        - description
        - language
        - type
        - content
        - workspaceId
        - createdAt
        - updatedAt
        - createdBy
        - updatedBy
        - documentCustomVariables
    DocumentCustomVariableDto:
      type: object
      properties:
        id:
          type: string
          description: Variable ID (UUID). Only required when updating an existing variable. Omit when creating a new variable.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: 'Variable name/key used to reference this variable in the template content. Use lowercase with underscores (e.g., "client_name", "project_budget"). This name is used in the variable reference: custom.variable_name.'
          example: client_name
        description:
          type: string
          description: Human-readable label/description for this variable. This is displayed to users when they are prompted to fill in the variable value when generating a document.
          example: Client Company Name
        type:
          type: string
          description: 'The data type of this variable. Determines what kind of input is expected and how the value is validated. Available types: Text (short text), LongText (multi-line text), Number (numeric value), Date (date/time), Boolean (true/false), Select (single choice from options), MultiSelect (multiple choices from options).'
          enum:
            - Text
            - LongText
            - Number
            - Date
            - Boolean
            - Select
            - MultiSelect
          example: Text
        value:
          description: |-
            Default value for this variable. The type of value depends on the variable type: - Text/LongText/Select: string
            - Number: number
            - Boolean: boolean
            - Date: ISO 8601 date string (e.g., "2024-01-15")
            - MultiSelect: array of strings

            This value is pre-filled when generating a document, but users can override it.
          example: Acme Corp
          oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: array
              items:
                type: string
        options:
          description: Available options for Select or MultiSelect variable types. For Select, users choose one option. For MultiSelect, users can choose multiple options. This field is ignored for other variable types.
          example:
            - Option 1
            - Option 2
            - Option 3
          type: array
          items:
            type: string
        required:
          type: boolean
          description: Whether this variable must be filled in when generating a document from the template. If true, users cannot proceed without providing a value. If false, the variable is optional.
          example: false
          default: false
      required:
        - name
        - type
    CreateDocumentTemplateDto:
      type: object
      properties:
        name:
          type: string
          description: The name/title of the document template. This is displayed in the templates list and when selecting a template.
          example: Project Contract Template
        description:
          type: string
          description: A brief description explaining the purpose or use case of this template. Helps users understand when to use this template.
          example: Standard contract template for project agreements with custom payment terms
        language:
          type: string
          description: ISO 639-1 language code for the template (e.g., "en" for English, "de" for German). Determines which language version of the template is used.
          example: en
        type:
          type: string
          description: The type of document this template generates. Currently only "ProjectDocument" is available, which is used for generating project-related documents.
          enum:
            - ProjectDocument
          example: ProjectDocument
        content:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your document templates. Variables are represented as HTML spans:

            **workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`
            **company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`
            **organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`
            **project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`
            **currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`
            **recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`
            **macros**: `macros.todaysDate`, `macros.dearCustomer`

            **Example HTML**: `<span data-type="variable" data-id="currentUser.firstName">John</span>`, `<span data-type="variable" data-id="project.name">Website Redesign</span>`
          example: <h1>Project Overview</h1><p>Created by <span data-type="variable" data-id="currentUser.firstName">John</span> for <span data-type="variable" data-id="project.name">Website Redesign</span>.</p>
        customVariables:
          description: Custom variables define template-specific placeholders that extend beyond standard system variables. Each variable can be referenced in the template content using `<span data-type="variable" data-id="custom.variable_name">...</span>` syntax. When generating a document from this template, users will be prompted to fill in these values. Variables support different types (Text, Number, Date, Boolean, Select, MultiSelect) and can be marked as required.
          example:
            - name: client_name
              description: Client company name
              type: Text
              value: Acme Corporation
              required: true
            - name: project_budget
              description: Project budget in USD
              type: Number
              value: 50000
              required: false
          type: array
          items:
            $ref: '#/components/schemas/DocumentCustomVariableDto'
      required:
        - name
        - description
        - language
        - type
        - content
    UpdateDocumentTemplateDto:
      type: object
      properties:
        name:
          type: string
          description: The name/title of the document template. This is displayed in the templates list and when selecting a template.
          example: Project Contract Template
        description:
          type: string
          description: A brief description explaining the purpose or use case of this template. Helps users understand when to use this template.
          example: Standard contract template for project agreements with custom payment terms
        language:
          type: string
          description: ISO 639-1 language code for the template (e.g., "en" for English, "de" for German). Determines which language version of the template is used.
          example: en
        type:
          type: string
          description: The type of document this template generates. Currently only "ProjectDocument" is available, which is used for generating project-related documents.
          enum:
            - ProjectDocument
          example: ProjectDocument
        content:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your document templates. Variables are represented as HTML spans:

            **workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`
            **company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`
            **organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`
            **project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`
            **currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`
            **recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`
            **macros**: `macros.todaysDate`, `macros.dearCustomer`

            **Example HTML**: `<span data-type="variable" data-id="currentUser.firstName">John</span>`, `<span data-type="variable" data-id="project.name">Website Redesign</span>`
          example: <h1>Project Overview</h1><p>Created by <span data-type="variable" data-id="currentUser.firstName">John</span> for <span data-type="variable" data-id="project.name">Website Redesign</span>.</p>
        customVariables:
          description: Custom variables define template-specific placeholders that extend beyond standard system variables. Each variable can be referenced in the template content using `<span data-type="variable" data-id="custom.variable_name">...</span>` syntax. When generating a document from this template, users will be prompted to fill in these values. Variables support different types (Text, Number, Date, Boolean, Select, MultiSelect) and can be marked as required.
          example:
            - name: client_name
              description: Client company name
              type: Text
              value: Acme Corporation
              required: true
            - name: project_budget
              description: Project budget in USD
              type: Number
              value: 50000
              required: false
          type: array
          items:
            $ref: '#/components/schemas/DocumentCustomVariableDto'
      required:
        - name
        - description
        - language
        - type
        - content
    PatchDocumentTemplateDto:
      type: object
      properties:
        content:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your document templates. Variables are represented as HTML spans:

            **workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`
            **company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`
            **organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`
            **project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`
            **currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`
            **recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`
            **macros**: `macros.todaysDate`, `macros.dearCustomer`

            **Example HTML**: `<span data-type="variable" data-id="currentUser.firstName">John</span>`, `<span data-type="variable" data-id="project.name">Website Redesign</span>`
          example: <h1>Project Overview</h1><p>Created by <span data-type="variable" data-id="currentUser.firstName">John</span> for <span data-type="variable" data-id="project.name">Website Redesign</span>.</p>
        customVariables:
          description: Custom variables define template-specific placeholders that extend beyond standard system variables. Each variable can be referenced in the template content using `<span data-type="variable" data-id="custom.variable_name">...</span>` syntax. When generating a document from this template, users will be prompted to fill in these values. Variables support different types (Text, Number, Date, Boolean, Select, MultiSelect) and can be marked as required. If provided, the entire custom variables array replaces the existing one.
          example:
            - name: client_name
              description: Client company name
              type: Text
              value: Acme Corporation
              required: true
            - name: project_budget
              description: Project budget in USD
              type: Number
              value: 50000
              required: false
          type: array
          items:
            $ref: '#/components/schemas/DocumentCustomVariableDto'
      required:
        - content
    AutomationApiResponse:
      type: object
      properties:
        id:
          type: string
          description: Automation ID
        workspaceId:
          type: string
          description: Workspace ID
        title:
          type: string
          description: Automation title
        executionType:
          type: string
          description: Execution type
          enum:
            - agent
            - workflow
        userPrompt:
          type: object
          description: 'User prompt for agent (HTML, automation prompt format: basic nodes, mentions, images; no files or videos)'
          nullable: true
        executionConfig:
          type: object
          description: Execution config
        scope:
          type: string
          description: Scope
          enum:
            - personal
            - workspace
        ownerUserId:
          type: object
          description: Owner user ID (for personal scope)
          nullable: true
        executorUserId:
          type: string
          description: Executor user ID
        isEnabled:
          type: boolean
          description: Whether automation is enabled
        createdAt:
          format: date-time
          type: string
          description: Creation timestamp
        updatedAt:
          format: date-time
          type: string
          description: Last update timestamp
        createdBy:
          type: string
          description: Creator user ID
        updatedBy:
          type: object
          description: Last updater user ID
          nullable: true
        triggers:
          type: array
          description: Triggers. For type webhook, webhookSecret is present after save; use POST {base}/api/automations/webhooks/{webhookSecret} to run. For type slackMessageInPublicChannel, definition contains channelId (Slack C…) and optional textPattern/useRegex. For type slackReactionAdded, definition contains emojiName and optional channelId.
          items:
            type: object
            properties:
              id:
                type: string
              type:
                type: string
                enum:
                  - daily
                  - weekly
                  - monthly
                  - customCron
                  - webhook
                  - organizationCreated
                  - projectCreated
                  - taskCreated
                  - objectCreated
                  - slackMessageInPublicChannel
                  - slackReactionAdded
              isEnabled:
                type: boolean
              sortOrder:
                type: number
              definition:
                type: object
              webhookSecret:
                type: string
                nullable: true
                description: 'Only for webhook triggers. Present after save. Inbound webhook: POST {base}/api/automations/webhooks/{webhookSecret} with Content-Type: application/json. No auth; secret authenticates. Request body is passed to the agent as context.'
      required:
        - id
        - workspaceId
        - title
        - executionType
        - userPrompt
        - executionConfig
        - scope
        - ownerUserId
        - executorUserId
        - isEnabled
        - createdAt
        - updatedAt
        - createdBy
        - updatedBy
        - triggers
    AutomationTriggerDto:
      type: object
      properties:
        type:
          type: string
          description: 'Trigger variant: daily, weekly, monthly, customCron, webhook, organizationCreated, projectCreated, taskCreated, objectCreated, slackMessageInPublicChannel, or slackReactionAdded. slackMessageInPublicChannel fires on new messages in a monitored **public** Slack channel; the workspace must have Slack connected and the agent must be in that channel.'
          enum:
            - daily
            - weekly
            - monthly
            - customCron
            - webhook
            - organizationCreated
            - projectCreated
            - taskCreated
            - objectCreated
            - slackMessageInPublicChannel
            - slackReactionAdded
          example: daily
        isEnabled:
          type: boolean
          description: Whether the trigger is enabled
          example: true
        sortOrder:
          type: number
          description: Sort order for multiple triggers
          example: 0
        definition:
          type: object
          description: 'Per-trigger settings. **slackMessageInPublicChannel** — required `channelId`: Slack **public channel ID** (`C…`), not the human-readable name. Optional `textPattern` (message must contain this substring, case-insensitive) and `useRegex` (if true, `textPattern` is a JavaScript regex). Example: `{"channelId":"C012ABCDE","textPattern":"bug","useRegex":false}`. **slackReactionAdded** — required `emojiName`: Slack emoji short name. Colons and common pasted/natural forms are accepted and normalized (`eyes`, `:eyes:`, `+1`, `:+1:`, `thumbsup`, `thumbs up`). Optional `channelId` limits the trigger to one channel. Example: `{"emojiName":":+1:","channelId":"C012ABCDE"}`. On run, the agent receives `triggerEventPayload.slack`: `channelId`, `channelType`, `userId`, `text`, `ts`, `threadTs` — use with Slack integration tools to reply in-thread (`threadTs ?? ts`) and add/remove reactions on `ts`. **organizationCreated / projectCreated / taskCreated / objectCreated** — optional `filters` (FilterStripModel). Field names and `value` shapes must match the **list grid** for that entity: use `action-details` on **`GET /tasks/grid`** for `taskCreated`, **`GET /projects`** for `projectCreated`, **`GET /organizations`** for `organizationCreated`, internal **`GET …/objects/grid`** for `objectCreated` (not interchangeable). For event triggers, `definition.filters` must be a full FilterStripModel object such as `{"quickSearch":"","filters":[...]}`. **Schedules** — timeOfDay (HH:mm), weekday (0-6), dayOfMonth (1-31), cronExpression (customCron). **webhook** — no required fields.'
          example:
            channelId: C012ABCDE
            textPattern: bug
            useRegex: false
      required:
        - type
    CreateAutomationDto:
      type: object
      properties:
        title:
          type: string
          description: Automation title
          example: Daily standup reminder
        executionType:
          type: string
          description: Execution type (agent or workflow). Currently only agent is supported.
          enum:
            - agent
            - workflow
          example: agent
        userPrompt:
          type: object
          description: 'User prompt for agent (HTML or Markdown). Automation prompt format: basic nodes, mentions, images; no files or videos.'
          example: <p>Summarize yesterday progress</p>
          nullable: true
        executionConfig:
          type: object
          description: Execution config (agent-specific or workflow metadata). Not used for trigger matching filters; event trigger filters belong in `triggers[].definition.filters`.
          example: {}
        scope:
          type: string
          description: Scope (personal or workspace). Personal requires managePersonalAutomations; workspace requires manageWorkspaceAutomations.
          enum:
            - personal
            - workspace
          example: personal
        executorUserId:
          type: string
          description: User id of the executing agent. Must be a bot/agent user — automations always execute as an agent, never as a human user.
          example: 123e4567-e89b-12d3-a456-426614174000
        isEnabled:
          type: boolean
          description: Whether the automation is enabled
          example: true
        triggers:
          description: Triggers (daily, weekly, monthly, customCron, webhook, organizationCreated, projectCreated, taskCreated, objectCreated, slackMessageInPublicChannel, slackReactionAdded). At least one required. For webhook, POST JSON to {WORKC_URL}/api/automations/webhooks/{webhookSecret} (no auth). For slackMessageInPublicChannel, see definition.channelId (Slack public channel ID) and optional text filters in AutomationTriggerDto. For slackReactionAdded, see definition.emojiName and optional definition.channelId. For event triggers, filters belong inside `triggers[].definition.filters`, not `executionConfig`.
          type: array
          items:
            $ref: '#/components/schemas/AutomationTriggerDto'
      required:
        - title
        - executionType
        - userPrompt
        - executionConfig
        - scope
        - executorUserId
        - triggers
    UpdateAutomationDto:
      type: object
      properties:
        title:
          type: string
          description: Automation title
          example: Daily standup reminder
        executionType:
          type: string
          description: Execution type (agent or workflow). Currently only agent is supported.
          enum:
            - agent
            - workflow
          example: agent
        userPrompt:
          type: object
          description: 'User prompt for agent (HTML or Markdown). Automation prompt format: basic nodes, mentions, images; no files or videos.'
          example: <p>Summarize yesterday progress</p>
          nullable: true
        executionConfig:
          type: object
          description: Execution config (agent-specific or workflow metadata). Not used for trigger matching filters; event trigger filters belong in `triggers[].definition.filters`.
          example: {}
        scope:
          type: string
          description: Scope (personal or workspace). Personal requires managePersonalAutomations; workspace requires manageWorkspaceAutomations.
          enum:
            - personal
            - workspace
          example: personal
        executorUserId:
          type: string
          description: User id of the executing agent. Must be a bot/agent user — automations always execute as an agent, never as a human user.
          example: 123e4567-e89b-12d3-a456-426614174000
        isEnabled:
          type: boolean
          description: Whether the automation is enabled
          example: true
        triggers:
          description: Triggers (daily, weekly, monthly, customCron, webhook, organizationCreated, projectCreated, taskCreated, objectCreated, slackMessageInPublicChannel, slackReactionAdded). At least one required. For webhook, POST JSON to {WORKC_URL}/api/automations/webhooks/{webhookSecret} (no auth). For slackMessageInPublicChannel, see definition.channelId (Slack public channel ID) and optional text filters in AutomationTriggerDto. For slackReactionAdded, see definition.emojiName and optional definition.channelId. For event triggers, filters belong inside `triggers[].definition.filters`, not `executionConfig`.
          type: array
          items:
            $ref: '#/components/schemas/AutomationTriggerDto'
      required:
        - title
        - executionType
        - userPrompt
        - executionConfig
        - scope
        - executorUserId
        - triggers
    TestRunAutomationDto:
      type: object
      properties: {}
    AutomationRunCreatedResponse:
      type: object
      properties:
        id:
          type: string
          description: Created run ID
        automationId:
          type: string
          description: Automation ID
        status:
          type: string
          description: Run status (queued)
      required:
        - id
        - automationId
        - status
    AutomationRunApiResponse:
      type: object
      properties:
        id:
          type: string
          description: Run ID
        automationId:
          type: string
          description: Automation ID
        cause:
          type: string
          description: Run cause (triggerFired, testRun)
        status:
          type: string
          description: Run status (queued, running, done, failed, canceled)
        executionType:
          type: string
          description: Execution type
        executorUserId:
          type: string
          description: Executor user ID
        requestedByUserId:
          type: object
          description: Requested by user ID
          nullable: true
        agentRunId:
          type: object
          description: Agent run ID when linked
          nullable: true
        failureKind:
          type: object
          description: Failure classification (technical or agentReported) when status is failed
          nullable: true
        resultSummary:
          type: object
          description: Outcome summary reported by the agent
          nullable: true
        resultPayload:
          type: object
          description: Structured result payload from the agent
          nullable: true
        testContextDoc:
          type: object
          description: Optional test context snapshot used for this run
          nullable: true
        firedAt:
          format: date-time
          type: string
          description: When the run was fired
        startedAt:
          type: object
          description: When execution started
          nullable: true
        finishedAt:
          type: object
          description: When execution finished
          nullable: true
        metadata:
          type: object
          description: Run metadata
        agentTranscript:
          type: object
          description: Sanitized agent transcript when agentRunId is linked (run metadata, lastSeq, events)
          additionalProperties: true
      required:
        - id
        - automationId
        - cause
        - status
        - executionType
        - executorUserId
        - requestedByUserId
        - agentRunId
        - failureKind
        - resultSummary
        - resultPayload
        - testContextDoc
        - firedAt
        - startedAt
        - finishedAt
        - metadata
    IProductVariantApiResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the variant
        name:
          type: string
          description: Name of the variant (e.g., "Standard", "Pro", "Enterprise")
        descriptionHtml:
          type: object
          description: Variant description converted to HTML format. Null if no description is set.
          nullable: true
        priceSubscription:
          type: number
          description: Recurring subscription price for this variant
        priceFixed:
          type: number
          description: One-time fixed price for this variant
        pricePerUnit:
          type: number
          description: Price per unit for this variant
        isActivated:
          type: boolean
          description: Whether this variant is currently activated and available for selection
      required:
        - id
        - name
        - descriptionHtml
        - priceSubscription
        - priceFixed
        - pricePerUnit
        - isActivated
    IProductApiResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the product
        name:
          type: string
          description: Name of the product or service
        descriptionHtml:
          type: string
          description: Product description converted to HTML format for easy display
        categoryId:
          type: string
          description: UUID of the product category this product belongs to
        logoId:
          type: object
          description: File ID of the product logo. Null if no logo is set.
          nullable: true
        logoUrl:
          type: object
          description: Public URL to access the product logo image. Constructed from logoId if a logo is set. Null if no logo is available.
          nullable: true
        priceFixed:
          type: number
          description: One-time fixed price for the product
        priceSubscription:
          type: number
          description: Recurring subscription price for the product
        pricePerUnit:
          type: number
          description: Price per unit for quantity-based billing
        priceUnitName:
          type: object
          description: Name of the unit for per-unit pricing (e.g., "user", "hour"). Null if per-unit pricing is not used.
          nullable: true
        priceFrequency:
          type: object
          description: Billing frequency in months (1, 3, 6, or 12). Null if subscription or per-unit pricing is not used.
          nullable: true
        variants:
          description: List of all product variants with their pricing and descriptions
          type: array
          items:
            $ref: '#/components/schemas/IProductVariantApiResponse'
        options:
          description: List of all product options with their values and configurations. Structure matches ProductOptionApiDto but with additional internal fields.
          type: array
          items:
            type: string
      required:
        - id
        - name
        - descriptionHtml
        - categoryId
        - logoId
        - logoUrl
        - priceFixed
        - priceSubscription
        - pricePerUnit
        - priceUnitName
        - priceFrequency
        - variants
        - options
    ProductVariantApiDto:
      type: object
      properties:
        id:
          type: string
          description: Variant ID. Include this when updating an existing variant. Omit when creating a new variant.
        name:
          type: string
          description: Name of the variant (e.g., "Standard", "Pro", "Enterprise"). Used to distinguish different performance levels or configurations of the same product.
        description:
          type: object
          description: |
            Variant description explaining what this variant includes or offers. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          nullable: true
        priceFixed:
          type: object
          description: One-time fixed price for this variant. Use this for one-time purchases (e.g., hardware, installation). Can be combined with subscription or per-unit pricing. Use 0 or null when the variant has no fixed charge.
          nullable: true
          example: 999.99
        priceSubscription:
          type: object
          description: Recurring subscription price for this variant. Use this for recurring revenue models (e.g., monthly SaaS subscription). The billing frequency is set at the product level via priceFrequency. Use 0 or null when the variant has no subscription charge.
          nullable: true
          example: 29.99
        pricePerUnit:
          type: object
          description: Price per unit for this variant. Use this for flexible billing by quantity (e.g., per user, per hour, per license). Requires priceUnitName to be set at the product level. Use 0 or null when the variant has no per-unit charge.
          nullable: true
          example: 5
      required:
        - name
        - description
    ProductOptionValueApiDto:
      type: object
      properties:
        id:
          type: string
          description: Option value ID. Include this when updating an existing option value. Omit when creating a new option value.
        name:
          type: string
          description: Name of the option value (e.g., "Add workshop", "Enable premium support", "Extra 10GB storage"). This is what customers see when selecting options.
        extraPriceFixed:
          type: object
          description: Additional one-time fixed price added when this option value is selected. Use this for one-time add-ons or upgrades.
          nullable: true
          example: 50
        extraPriceSubscription:
          type: object
          description: Additional recurring subscription price added when this option value is selected. Use this for recurring add-ons (e.g., monthly premium support).
          nullable: true
          example: 10
        extraPricePerUnit:
          type: object
          description: Additional per-unit price added when this option value is selected. Use this for quantity-based add-ons.
          nullable: true
          example: 2.5
      required:
        - name
    ProductOptionApiDto:
      type: object
      properties:
        id:
          type: string
          description: Option ID. Include this when updating an existing option. Omit when creating a new option.
        name:
          type: string
          description: Name of the option group (e.g., "Additional Services", "Storage Options", "Support Level"). This groups related option values together.
        values:
          description: List of available values for this option. Each value can have its own pricing. At least one value must be provided.
          type: array
          items:
            $ref: '#/components/schemas/ProductOptionValueApiDto'
        isRequired:
          type: boolean
          description: Whether this option must be selected. If true, customers must choose at least one value from this option before proceeding.
          example: false
        isMultiple:
          type: boolean
          description: Whether customers can select multiple values from this option. If true, customers can select multiple values (e.g., "Add workshop" AND "Enable premium support"). If false, only one value can be selected.
          example: false
      required:
        - name
        - values
        - isRequired
        - isMultiple
    CreateProductDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the product or service. This is the primary identifier shown in the catalog, quotes, and invoices.
          example: Website Hosting Package
        description:
          type: string
          description: |
            Product description explaining what the product includes, its benefits, and key features. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
        categoryId:
          type: string
          description: UUID of the product category this product belongs to. Categories help organize products into groups (e.g., "Hardware", "Software", "Services").
          example: 123e4567-e89b-12d3-a456-426614174000
        logoId:
          type: object
          description: File ID of the product logo image. To upload a logo, first call POST /api/public/workspace/upload to upload the image file, then use the returned file ID here. Set to null if no logo is needed.
          nullable: true
          example: 123e4567-e89b-12d3-a456-426614174000
        priceFixed:
          type: object
          description: One-time fixed price for the product. Use this for one-time purchases (e.g., hardware, installation fees, one-time services). Can be combined with subscription or per-unit pricing. Use 0 or null when the product has no fixed charge.
          nullable: true
          example: 999.99
        priceSubscription:
          type: object
          description: Recurring subscription price for the product. Use this for recurring revenue models (e.g., monthly SaaS subscription, annual maintenance). The billing frequency is controlled by priceFrequency. Use 0 or null when the product has no subscription charge.
          nullable: true
          example: 29.99
        pricePerUnit:
          type: object
          description: Price per unit for flexible quantity-based billing. Use this when pricing depends on quantity (e.g., per user, per hour, per license, per GB). Requires priceUnitName to be set. Use 0 or null when the product has no per-unit charge.
          nullable: true
          example: 5
        priceUnitName:
          type: string
          description: Name of the unit for per-unit pricing (e.g., "user", "hour", "license", "GB"). Required when pricePerUnit is greater than 0. This is displayed in quotes and invoices.
          example: user
        priceFrequency:
          type: number
          description: 'Billing frequency in months for subscription or per-unit pricing. Required when pricePerUnit or priceSubscription is greater than 0. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).'
          example: 1
          enum:
            - 1
            - 3
            - 6
            - 12
        contractPeriodMonths:
          type: number
          description: Contract period in months. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to 12 months if not specified.
          example: 12
        renewalMode:
          type: string
          description: Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to Automatic.
          enum:
            - Automatic
            - Stops
          example: Automatic
        variants:
          description: List of product variants. Variants represent different configurations or performance levels of the same product (e.g., Standard, Pro, Enterprise). Each variant can have its own pricing and description. At least one variant must be provided.
          type: array
          items:
            $ref: '#/components/schemas/ProductVariantApiDto'
        options:
          description: List of product options. Options are extra services or add-ons that customers can optionally select (e.g., "Add workshop", "Enable premium support"). Options are organized into groups with multiple selectable values. Can be an empty array if no options are needed.
          type: array
          items:
            $ref: '#/components/schemas/ProductOptionApiDto'
      required:
        - name
        - description
        - categoryId
        - variants
        - options
    UpdateProductDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the product or service. This is the primary identifier shown in the catalog, quotes, and invoices.
          example: Website Hosting Package
        description:
          type: string
          description: |
            Product description explaining what the product includes, its benefits, and key features. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
        categoryId:
          type: string
          description: UUID of the product category this product belongs to. Categories help organize products into groups (e.g., "Hardware", "Software", "Services").
          example: 123e4567-e89b-12d3-a456-426614174000
        logoId:
          type: object
          description: File ID of the product logo image. To upload a logo, first call POST /api/public/workspace/upload to upload the image file, then use the returned file ID here. Set to null if no logo is needed.
          nullable: true
          example: 123e4567-e89b-12d3-a456-426614174000
        priceFixed:
          type: object
          description: One-time fixed price for the product. Use this for one-time purchases (e.g., hardware, installation fees, one-time services). Can be combined with subscription or per-unit pricing. Use 0 or null when the product has no fixed charge.
          nullable: true
          example: 999.99
        priceSubscription:
          type: object
          description: Recurring subscription price for the product. Use this for recurring revenue models (e.g., monthly SaaS subscription, annual maintenance). The billing frequency is controlled by priceFrequency. Use 0 or null when the product has no subscription charge.
          nullable: true
          example: 29.99
        pricePerUnit:
          type: object
          description: Price per unit for flexible quantity-based billing. Use this when pricing depends on quantity (e.g., per user, per hour, per license, per GB). Requires priceUnitName to be set. Use 0 or null when the product has no per-unit charge.
          nullable: true
          example: 5
        priceUnitName:
          type: string
          description: Name of the unit for per-unit pricing (e.g., "user", "hour", "license", "GB"). Required when pricePerUnit is greater than 0. This is displayed in quotes and invoices.
          example: user
        priceFrequency:
          type: number
          description: 'Billing frequency in months for subscription or per-unit pricing. Required when pricePerUnit or priceSubscription is greater than 0. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).'
          example: 1
          enum:
            - 1
            - 3
            - 6
            - 12
        contractPeriodMonths:
          type: number
          description: Contract period in months. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to 12 months if not specified.
          example: 12
        renewalMode:
          type: string
          description: Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to Automatic.
          enum:
            - Automatic
            - Stops
          example: Automatic
        variants:
          description: List of product variants. Variants represent different configurations or performance levels of the same product (e.g., Standard, Pro, Enterprise). Each variant can have its own pricing and description. At least one variant must be provided.
          type: array
          items:
            $ref: '#/components/schemas/ProductVariantApiDto'
        options:
          description: List of product options. Options are extra services or add-ons that customers can optionally select (e.g., "Add workshop", "Enable premium support"). Options are organized into groups with multiple selectable values. Can be an empty array if no options are needed.
          type: array
          items:
            $ref: '#/components/schemas/ProductOptionApiDto'
      required:
        - name
        - description
        - categoryId
        - variants
        - options
    PatchProductDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the product or service. This is the primary identifier shown in the catalog, quotes, and invoices.
          example: Website Hosting Package
        description:
          type: string
          description: |
            Product description explaining what the product includes, its benefits, and key features. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
        categoryId:
          type: string
          description: UUID of the product category this product belongs to. Categories help organize products into groups (e.g., "Hardware", "Software", "Services").
          example: 123e4567-e89b-12d3-a456-426614174000
        logoId:
          type: object
          description: File ID of the product logo image. To upload a logo, first call POST /api/public/workspace/upload to upload the image file, then use the returned file ID here. Set to null if no logo is needed.
          nullable: true
          example: 123e4567-e89b-12d3-a456-426614174000
        priceFixed:
          type: object
          description: One-time fixed price for the product. Use this for one-time purchases (e.g., hardware, installation fees, one-time services). Can be combined with subscription or per-unit pricing. Use 0 or null when the product has no fixed charge.
          nullable: true
          example: 999.99
        priceSubscription:
          type: object
          description: Recurring subscription price for the product. Use this for recurring revenue models (e.g., monthly SaaS subscription, annual maintenance). The billing frequency is controlled by priceFrequency. Use 0 or null when the product has no subscription charge.
          nullable: true
          example: 29.99
        pricePerUnit:
          type: object
          description: Price per unit for flexible quantity-based billing. Use this when pricing depends on quantity (e.g., per user, per hour, per license, per GB). Requires priceUnitName to be set. Use 0 or null when the product has no per-unit charge.
          nullable: true
          example: 5
        priceUnitName:
          type: string
          description: Name of the unit for per-unit pricing (e.g., "user", "hour", "license", "GB"). Required when pricePerUnit is greater than 0. This is displayed in quotes and invoices.
          example: user
        priceFrequency:
          type: number
          description: 'Billing frequency in months for subscription or per-unit pricing. Required when pricePerUnit or priceSubscription is greater than 0. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).'
          example: 1
          enum:
            - 1
            - 3
            - 6
            - 12
        contractPeriodMonths:
          type: number
          description: Contract period in months. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to 12 months if not specified.
          example: 12
        renewalMode:
          type: string
          description: Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to Automatic.
          enum:
            - Automatic
            - Stops
          example: Automatic
        variants:
          description: List of product variants. Variants represent different configurations or performance levels of the same product (e.g., Standard, Pro, Enterprise). Each variant can have its own pricing and description. At least one variant must be provided.
          type: array
          items:
            $ref: '#/components/schemas/ProductVariantApiDto'
        options:
          description: List of product options. Options are extra services or add-ons that customers can optionally select (e.g., "Add workshop", "Enable premium support"). Options are organized into groups with multiple selectable values. Can be an empty array if no options are needed.
          type: array
          items:
            $ref: '#/components/schemas/ProductOptionApiDto'
    ProductCategoryTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German). This determines which language interface will display this translation.
          example: en
        name:
          type: object
          description: Translated name of the product category in the specified language. This name appears in multilingual user interfaces and documents when the user language matches.
          example: Software
        description:
          type: object
          description: Translated description of the product category in the specified language. This description appears in multilingual user interfaces and documents when the user language matches.
          example: Software products and licenses
      required:
        - language
    ProductCategoryResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) of the product category. Use this ID to reference the category in other API calls.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Display name of the product category. This is the primary name used to identify and organize products.
          example: Software
        icon:
          type: string
          description: Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns.
          example: ':package:'
        description:
          type: object
          description: Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products.
          example: Software products and licenses
        sort:
          type: number
          description: Numeric sort order for displaying categories. Lower numbers appear first. Used to control the order in which categories appear in dropdowns and lists.
          example: 1
        defaultKey:
          type: object
          description: Default key identifier for system-defined categories (e.g., "software", "hardware", "consulting", "external_costs"). Only present for categories that were created from system defaults. Custom categories created by users do not have a defaultKey. This key is used when restoring default categories.
          example: software
        translations:
          description: Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference.
          example:
            - language: en
              name: Software
              description: Software products and licenses
            - language: de
              name: Software
              description: Softwareprodukte und Lizenzen
          type: array
          items:
            $ref: '#/components/schemas/ProductCategoryTranslationDto'
        createdAt:
          format: date-time
          type: string
          description: 'ISO 8601 timestamp indicating when the product category was created. Format: YYYY-MM-DDTHH:mm:ss.sssZ'
          example: '2024-01-01T00:00:00.000Z'
        deletedAt:
          type: object
          description: 'ISO 8601 timestamp indicating when the product category was soft-deleted, or null if not deleted. Format: YYYY-MM-DDTHH:mm:ss.sssZ. Soft-deleted categories are marked as deleted but not permanently removed.'
          example: null
      required:
        - id
        - name
        - icon
        - translations
        - createdAt
    CreateProductCategoryDto:
      type: object
      properties:
        name:
          type: string
          description: 'Display name of the product category. This is the primary name used to identify and organize products. Examples: "Hardware", "Software", "Consulting", "Subscriptions".'
          example: Software
        icon:
          type: string
          description: 'Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns. Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:, :package:. Default is :rocket: if not specified.'
          example: ':package:'
        description:
          type: object
          description: Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products.
          example: Software products and licenses
        translations:
          description: 'Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference. Common languages: "en" (English), "de" (German).'
          example:
            - language: en
              name: Software
              description: Software products and licenses
            - language: de
              name: Software
              description: Softwareprodukte und Lizenzen
          type: array
          items:
            $ref: '#/components/schemas/ProductCategoryTranslationDto'
      required:
        - name
        - icon
        - translations
    UpdateProductCategoryDto:
      type: object
      properties:
        name:
          type: string
          description: 'Display name of the product category. This is the primary name used to identify and organize products. Examples: "Hardware", "Software", "Consulting", "Subscriptions". All fields are required in PUT requests.'
          example: Software
        icon:
          type: string
          description: 'Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns. Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:, :package:. All fields are required in PUT requests.'
          example: ':package:'
        description:
          type: object
          description: Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products. All fields are required in PUT requests.
          example: Software products and licenses
        translations:
          description: 'Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference. Common languages: "en" (English), "de" (German). All fields are required in PUT requests.'
          type: array
          items:
            $ref: '#/components/schemas/ProductCategoryTranslationDto'
    PatchProductCategoryDto:
      type: object
      properties:
        name:
          type: string
          description: 'Display name of the product category. This is the primary name used to identify and organize products. Examples: "Hardware", "Software", "Consulting", "Subscriptions". Only include this field if you want to update the name. If not provided, the existing name remains unchanged.'
          example: Software
        icon:
          type: string
          description: 'Icon identifier for the product category in emoji-style format :icon_name:. Icons help visually distinguish categories in lists and dropdowns. Examples: :computer:, :desktop_computer:, :money_with_wings:, :office_worker:, :package:. Only include this field if you want to update the icon. If not provided, the existing icon remains unchanged.'
          example: ':package:'
        description:
          type: object
          description: Optional description explaining what products or services belong to this category. Helps users understand the category purpose when creating or assigning products. Only include this field if you want to update the description. If not provided, the existing description remains unchanged. Set to null to clear the description.
          example: Software products and licenses
        translations:
          description: 'Array of translations for multilingual support. Each translation provides the category name and description in a specific language. Translations are automatically used in multilingual user interfaces and documents based on the user language preference. Common languages: "en" (English), "de" (German). Only include this field if you want to update translations. If provided, it replaces all existing translations. If not provided, existing translations remain unchanged.'
          type: array
          items:
            $ref: '#/components/schemas/ProductCategoryTranslationDto'
    SortProductCategoriesDto:
      type: object
      properties:
        ids:
          description: Array of product category IDs in the desired display order. The first ID in the array will be displayed first, the second ID will be displayed second, and so on. All category IDs must be valid UUIDs that exist in the workspace. The sort order is updated for all categories based on their position in this array.
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 223e4567-e89b-12d3-a456-426614174001
            - 323e4567-e89b-12d3-a456-426614174002
          type: array
          items:
            type: string
      required:
        - ids
    RestoreProductCategoriesDto:
      type: object
      properties:
        keepCustom:
          type: boolean
          description: 'Whether to preserve custom (user-created) product categories when restoring default categories. If true, system default categories are restored while keeping any custom categories you created. If false (default), all custom categories are removed and only system defaults are restored. System default categories include: External Costs, Software, Hardware, and Consulting with multilingual translations.'
          example: false
          default: false
    RoleListItem:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the role. Base roles have IDs containing underscore-prefixed identifiers (e.g., "_root_", "_ceo_", "_staff_"). Custom roles have workspace-prefixed IDs.
          example: workspace-id_employee
        name:
          type: string
          description: Display name of the role, localized based on user language preference.
          example: Employee
        description:
          type: string
          description: Human-readable description of the role purpose and responsibilities, localized based on user language preference.
          example: Standard employee role
        icon:
          type: string
          description: Icon identifier for the role. Can be a RemixIcon class name (e.g., "ri-user-line") or an emoji code (e.g., ":person_in_tuxedo:"). Used for visual identification in role lists.
          example: ri-user-line
        parentId:
          type: string
          description: ID of the parent role if this role inherits permissions from another role. Undefined if the role has no parent (e.g., base roles or top-level custom roles).
          example: workspace-id_admin
        type:
          type: string
          description: Role type indicating the intended use case. "normal" for internal team members, "guest" for external users. Determines permission restrictions.
          example: normal
        readOnly:
          type: boolean
          description: Boolean flag indicating if this is a base role (system-defined) that cannot be modified or deleted. Base roles include Root, CEO, Team Leader, Staff, and Guest.
          example: false
      required:
        - id
        - name
        - description
        - icon
        - type
        - readOnly
    PermissionItem:
      type: object
      properties:
        key:
          type: string
          description: Unique permission identifier using dot notation format "category.action" (e.g., "projects.create", "tasks.delete"). This key is used when assigning permissions to roles.
          example: projects.create
        group:
          type: string
          description: Functional group that this permission belongs to. Permissions are organized into groups like "projects", "tasks", "employees", "roles", etc. for easier management and UI display.
          example: projects
      required:
        - key
        - group
    PermissionDefinitionItem:
      type: object
      properties:
        key:
          type: string
          description: Canonical permission key.
          example: tasks.create
        name:
          type: string
          description: Product-facing permission name.
        description:
          type: string
          description: What the permission lets a person do and its most important boundary or condition.
        group:
          type: string
          description: Shared RBAC UI group, when defined.
        subgroup:
          type: string
          description: Shared RBAC UI subgroup.
      required:
        - key
        - name
        - description
    RoleDetailsResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the role. Base roles have IDs containing underscore-prefixed identifiers (e.g., "_root_", "_ceo_", "_staff_"). Custom roles have workspace-prefixed IDs.
          example: workspace-id_employee
        name:
          type: string
          description: Display name of the role, localized based on user language preference.
          example: Employee
        description:
          type: string
          description: Human-readable description of the role purpose and responsibilities, localized based on user language preference.
          example: Standard employee role
        icon:
          type: string
          description: Icon identifier for the role. Can be a RemixIcon class name (e.g., "ri-user-line") or an emoji code (e.g., ":person_in_tuxedo:"). Used for visual identification in role lists.
          example: ri-user-line
        parentId:
          type: string
          description: ID of the parent role if this role inherits permissions from another role. Undefined if the role has no parent (e.g., base roles or top-level custom roles).
          example: workspace-id_admin
        type:
          type: string
          description: Role type indicating the intended use case. "normal" for internal team members, "guest" for external users. Determines permission restrictions.
          example: normal
        permissions:
          type: object
          description: Complete map of all permissions for this role. Keys are permission identifiers (e.g., "projects.create", "tasks.delete"). Values are true if the permission is explicitly allowed, false if explicitly disallowed, or missing if inherited from parent role. This map includes both explicitly set permissions and inherited permissions.
          example:
            projects.create: true
            projects.delete: false
        rawPermissions:
          type: object
          description: Permissions explicitly stored or defined on this role before inheritance.
          example:
            tasks.create: true
            tasks.delete: false
        effectivePermissions:
          type: object
          description: Permission map after resolving the full parent chain and child overrides.
          example:
            tasks.create: true
            tasks.delete: false
            profile.manage: true
        readOnly:
          type: boolean
          description: Boolean flag indicating if this is a base role (system-defined) that cannot be modified or deleted. Base roles include Root, CEO, Team Leader, Staff, and Guest.
          example: false
        createdBy:
          type: string
          description: User ID of the person who created this custom role. Only present for custom roles, not base roles.
        createdAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp indicating when this custom role was created. Only present for custom roles, not base roles.
        editedAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp indicating when this custom role was last modified. Only present for custom roles, not base roles.
      required:
        - id
        - name
        - description
        - icon
        - type
        - permissions
        - rawPermissions
        - effectivePermissions
        - readOnly
    CreateRoleDto:
      type: object
      properties:
        name:
          type: string
          description: Display name for the role. This is shown in user interfaces when selecting or displaying roles.
          example: Team Lead
        description:
          type: string
          description: Human-readable description explaining the role purpose and responsibilities. Maximum 400 characters.
          example: Leads a delivery team
        icon:
          type: string
          description: Icon identifier for the role. Can be a RemixIcon class name (e.g., "ri-user-star-line") or an emoji code (e.g., ":person_in_tuxedo:"). Used for visual identification in role lists and user interfaces.
          example: ri-user-star-line
        parentId:
          type: string
          description: ID of the parent role for permission inheritance. If provided, this role will inherit all permissions from the parent role. Inherited permissions can be overridden by explicitly setting them in the permissions map. Must not create circular dependencies (cannot set a child role as parent).
          example: workspace-id_employee
        type:
          type: string
          description: Role type determines the intended use case and permission restrictions. "normal" roles are for internal team members (employees, managers). "guest" roles are for external users like clients or partners and have additional permission restrictions.
          example: normal
          enum:
            - normal
            - guest
        permissions:
          type: object
          description: Map of permission keys to boolean values. Keys are permission identifiers (e.g., "projects.create", "tasks.delete"). Values are true to allow the permission or false to disallow it. If not provided and a parent role is set, permissions are inherited from the parent. Guest roles cannot have certain permissions (e.g., workspace administration).
          example:
            projects.create: true
            projects.delete: false
      required:
        - name
        - icon
        - type
    UpdateRoleDto:
      type: object
      properties:
        name:
          type: string
          description: Display name for the role. This is shown in user interfaces when selecting or displaying roles.
          example: Team Lead
        description:
          type: string
          description: Human-readable description explaining the role purpose and responsibilities. Maximum 400 characters.
          example: Leads a delivery team
        icon:
          type: string
          description: Icon identifier for the role. Can be a RemixIcon class name (e.g., "ri-user-star-line") or an emoji code (e.g., ":person_in_tuxedo:"). Used for visual identification in role lists and user interfaces.
          example: ri-user-star-line
        parentId:
          type: string
          description: ID of the parent role for permission inheritance. If provided, this role will inherit all permissions from the parent role. Inherited permissions can be overridden by explicitly setting them in the permissions map. Must not create circular dependencies (cannot set a child role as parent).
          example: workspace-id_employee
        type:
          type: string
          description: Role type determines the intended use case and permission restrictions. "normal" roles are for internal team members (employees, managers). "guest" roles are for external users like clients or partners and have additional permission restrictions.
          example: normal
          enum:
            - normal
            - guest
        permissions:
          type: object
          description: Complete map of permission keys to boolean values. Keys are permission identifiers (e.g., "projects.create", "tasks.delete"). Values are true to allow the permission or false to disallow it. This replaces the entire permission map - all permissions must be specified. Guest roles cannot have certain permissions (e.g., workspace administration).
          example:
            projects.create: true
            projects.delete: false
      required:
        - name
        - icon
        - type
    PatchRoleDto:
      type: object
      properties:
        name:
          type: string
          description: Display name for the role. If provided, updates the role name. If not provided, the existing name is kept.
          example: Team Lead
        description:
          type: string
          description: Human-readable description explaining the role purpose and responsibilities. Maximum 400 characters. If provided, updates the description. If not provided, the existing description is kept.
          example: Leads a delivery team
        icon:
          type: string
          description: Icon identifier for the role. Can be a RemixIcon class name (e.g., "ri-user-star-line") or an emoji code (e.g., ":person_in_tuxedo:"). If provided, updates the icon. If not provided, the existing icon is kept.
          example: ':person_in_tuxedo:'
        parentId:
          type: string
          description: ID of the parent role for permission inheritance. If provided, changes the parent role and permissions are recalculated based on the new parent. If not provided, the existing parent is kept. Must not create circular dependencies (cannot set a child role as parent).
          example: workspace-id_employee
        type:
          type: string
          description: Role type determines the intended use case and permission restrictions. "normal" roles are for internal team members. "guest" roles are for external users and have additional permission restrictions. If provided, changes the role type. If not provided, the existing type is kept.
          example: normal
          enum:
            - normal
            - guest
    ReplacePermissionsDto:
      type: object
      properties:
        permissions:
          type: object
          description: Complete map of permission keys to boolean values that replaces the entire permission set for the role. Keys are permission identifiers using dot notation (e.g., "projects.create", "tasks.delete"). Values are true to allow the permission or false to disallow it. This is a complete replacement - all permissions must be specified. Permissions set to false are removed from the role. Permissions are validated against disallowed permissions for the role type.
          example:
            projects.create: true
            projects.delete: false
            projects.edit: true
      required:
        - permissions
    TogglePermissionDto:
      type: object
      properties:
        allow:
          type: boolean
          description: Boolean value indicating whether to grant (true) or revoke (false) the permission specified in the URL path. Setting to true explicitly allows the permission, even if it was inherited from a parent role. Setting to false explicitly disallows the permission, overriding any inheritance.
          example: true
      required:
        - allow
    TaskAutoGenerationSettingsDto:
      type: object
      properties:
        tasksAutoGenerateTitle:
          type: boolean
          description: Whether to automatically generate task titles
          example: true
        tasksAutoGenerateSummary:
          type: boolean
          description: Whether to automatically generate task summaries
          example: true
        tasksAutoGenerateIcon:
          type: boolean
          description: Whether to automatically generate task icons
          example: true
      required:
        - tasksAutoGenerateTitle
        - tasksAutoGenerateSummary
        - tasksAutoGenerateIcon
    CustomFieldTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: Language code following ISO 639-1 format (e.g., "en", "de", "fr")
          example: en
        name:
          type: string
          description: Translated name for this language. Overrides the default name when user's language matches.
          example: Priority
      required:
        - language
        - name
    FieldOptionDto:
      type: object
      properties:
        id:
          type: object
          description: Option ID
          example: '1'
        value:
          type: string
          description: Default option value (used as fallback when no translation exists for user's language)
          example: High
        translations:
          description: Language-specific translations for this option. Overrides the default value when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationDto'
      required:
        - value
        - translations
    TaskCustomFieldResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Custom field ID
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Default custom field name (used as fallback when no translation exists for user's language)
          example: Priority
        description:
          type: object
          description: Default custom field description (used as fallback when no translation exists for user's language)
          example: Task priority level
          nullable: true
        sort:
          type: number
          description: Sort order
          example: 1
        type:
          type: string
          description: Custom field type
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        entity:
          type: string
          description: Entity type (always Task for task custom fields)
          enum:
            - Task
            - Project
            - Organization
            - Object
          example: Task
        translations:
          description: Language-specific translations for field name. Overrides the default name when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationDto'
        selectOptions:
          description: Select options with their translations. Each option has a default value and language-specific overrides.
          type: array
          items:
            $ref: '#/components/schemas/FieldOptionDto'
      required:
        - id
        - name
        - description
        - sort
        - type
        - entity
        - translations
        - selectOptions
    CreateTaskCustomFieldDto:
      type: object
      properties:
        id:
          type: string
          description: Custom field ID (optional for create)
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Default custom field name (used as fallback when no translation exists for user's language)
          example: Priority
        description:
          type: string
          description: Default custom field description (used as fallback when no translation exists for user's language)
          example: Task priority level
        type:
          type: string
          description: Custom field type
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        selectOptions:
          description: Select options (required for Select and MultiSelect types)
          type: array
          items:
            $ref: '#/components/schemas/FieldOptionDto'
        translations:
          description: Language-specific translations for field name. Overrides the default name when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationDto'
        translationsDescriptions:
          description: Language-specific translations for field description. Overrides the default description when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationDto'
      required:
        - name
        - description
        - type
        - selectOptions
        - translations
        - translationsDescriptions
    UpdateTaskCustomFieldDto:
      type: object
      properties:
        id:
          type: string
          description: Custom field ID (optional, taken from URL)
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Default custom field name (used as fallback when no translation exists for user's language)
          example: Priority
        description:
          type: string
          description: Default custom field description (used as fallback when no translation exists for user's language)
          example: Task priority level
        type:
          type: string
          description: Custom field type
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        selectOptions:
          description: Select options (required for Select and MultiSelect types)
          type: array
          items:
            $ref: '#/components/schemas/FieldOptionDto'
        translations:
          description: Language-specific translations for field name. Overrides the default name when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationDto'
        translationsDescriptions:
          description: Language-specific translations for field description. Overrides the default description when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationDto'
      required:
        - name
        - description
        - type
        - selectOptions
        - translations
        - translationsDescriptions
    SortTaskCustomFieldsDto:
      type: object
      properties:
        ids:
          description: Array of custom field IDs in the desired order
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 987fcdeb-51a2-43d7-b456-426614174000
          type: array
          items:
            type: string
      required:
        - ids
    RestoreDefaultsDto:
      type: object
      properties:
        keepCustom:
          type: boolean
          description: Whether to keep custom task types, statuses, and work activities
          example: false
      required:
        - keepCustom
    WorkActivityTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: Language code following ISO 639-1 format (e.g., "en", "de", "fr")
          example: en
        name:
          type: string
          description: Translated name for this language. Overrides the default name when user's language matches.
          example: Development
      required:
        - language
        - name
    WorkActivityResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Work activity ID
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Default work activity name (used as fallback when no translation exists for user's language)
          example: Development
        sort:
          type: number
          description: Sort order
          example: 1
        translations:
          description: Language-specific translations for activity name. Overrides the default name when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/WorkActivityTranslationDto'
      required:
        - id
        - name
        - sort
        - translations
    CreateWorkActivityDto:
      type: object
      properties:
        id:
          type: string
          description: Work activity ID (optional for create)
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Default work activity name (used as fallback when no translation exists for user's language)
          example: Development
        translations:
          description: Language-specific translations for activity name. Overrides the default name when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/WorkActivityTranslationDto'
      required:
        - name
        - translations
    UpdateWorkActivityDto:
      type: object
      properties:
        name:
          type: string
          description: Default work activity name (used as fallback when no translation exists for user's language)
          example: Development
        translations:
          description: Language-specific translations for activity name. Overrides the default name when user's language matches.
          type: array
          items:
            $ref: '#/components/schemas/WorkActivityTranslationDto'
      required:
        - name
        - translations
    SortWorkActivitiesDto:
      type: object
      properties:
        ids:
          description: Array of work activity IDs in the desired order
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 987fcdeb-51a2-43d7-b456-426614174000
          type: array
          items:
            type: string
      required:
        - ids
    TaskStatusTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: Language code (e.g., "en", "de")
          example: en
        name:
          type: string
          description: Translated name of the task status
          example: In Progress
      required:
        - language
        - name
    TaskStatusResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the task status
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Name of the task status
          example: In Progress
        icon:
          type: string
          description: 'Icon for the task status in format :icon_name: (e.g., :hourglass_flowing_sand:, :check_mark:, :x:)'
          example: ':hourglass_flowing_sand:'
        type:
          type: string
          description: Type of the task status
          enum:
            - New
            - InProgress
            - Feedback
            - Backlog
            - Resolved
            - Closed
          example: InProgress
        sort:
          type: number
          description: Sort order of the task status
          example: 1
        translations:
          description: Translations for the task status
          example:
            - language: en
              name: In Progress
            - language: de
              name: In Bearbeitung
          type: array
          items:
            $ref: '#/components/schemas/TaskStatusTranslationDto'
      required:
        - id
        - name
        - icon
        - type
        - sort
        - translations
    CreateTaskStatusDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the task status
          example: In Progress
        icon:
          type: string
          description: 'Icon for the task status in format :icon_name: (e.g., :hourglass_flowing_sand:, :check_mark:, :x:)'
          example: ':hourglass_flowing_sand:'
        type:
          type: string
          description: Type of the task status
          enum:
            - New
            - InProgress
            - Feedback
            - Backlog
            - Resolved
            - Closed
          example: InProgress
        translations:
          description: Translations for the task status
          example:
            - language: en
              name: In Progress
            - language: de
              name: In Bearbeitung
          type: array
          items:
            $ref: '#/components/schemas/TaskStatusTranslationDto'
      required:
        - name
        - icon
        - type
        - translations
    UpdateTaskStatusDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the task status
          example: In Progress
        icon:
          type: string
          description: 'Icon for the task status in format :icon_name: (e.g., :hourglass_flowing_sand:, :check_mark:, :x:)'
          example: ':hourglass_flowing_sand:'
        type:
          type: string
          description: Type of the task status
          enum:
            - New
            - InProgress
            - Feedback
            - Backlog
            - Resolved
            - Closed
          example: InProgress
        translations:
          description: Translations for the task status
          example:
            - language: en
              name: In Progress
            - language: de
              name: In Bearbeitung
          type: array
          items:
            $ref: '#/components/schemas/TaskStatusTranslationDto'
    SortTaskStatusesDto:
      type: object
      properties:
        ids:
          description: Array of task status IDs in the desired order
          example:
            - status-1
            - status-2
            - status-3
          type: array
          items:
            type: string
      required:
        - ids
    TaskTypeTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: Language code for the translation
          example: en
        name:
          type: string
          description: Translated name of the task type
          example: Feature
      required:
        - language
        - name
    TaskTypeResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the task type
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Name of the task type
          example: Feature
        icon:
          type: string
          description: 'Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)'
          example: ':rocket:'
        type:
          type: string
          description: Type of the task type
          enum:
            - Feature
            - Bug
          example: Feature
        sort:
          type: number
          description: Sort order of the task type
          example: 1
        translations:
          description: Translations for the task type
          example:
            - language: en
              name: Feature
            - language: de
              name: Funktion
          type: array
          items:
            $ref: '#/components/schemas/TaskTypeTranslationDto'
        statuses:
          description: Array of task status IDs that can be used with this task type
          example:
            - status-1
            - status-2
          type: array
          items:
            type: string
        customFields:
          description: Array of custom field IDs that are available for this task type
          example:
            - field-1
            - field-2
          type: array
          items:
            type: string
        templateDoc:
          type: string
          description: Template document content as HTML
          example: <h1>Task Template</h1><p>This is a template for new tasks of this type.</p>
        requiredFields:
          description: 'Array of required field names for this task type. This is contextual metadata for POST /tasks: fields listed here must be included even when the global create-task OpenAPI schema marks them optional. Common values include summary, estimatedTime, deadline, assignedToId, accountableId, and custom field names.'
          example:
            - title
            - description
            - summary
            - estimatedTime
          type: array
          items:
            type: string
      required:
        - id
        - name
        - icon
        - type
        - sort
        - translations
        - statuses
        - customFields
        - templateDoc
        - requiredFields
    CreateTaskTypeDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the task type
          example: Feature
        icon:
          type: string
          description: 'Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)'
          example: ':rocket:'
        type:
          type: string
          description: Type of the task type
          enum:
            - Feature
            - Bug
          example: Feature
        translations:
          description: Translations for the task type
          example:
            - language: en
              name: Feature
            - language: de
              name: Funktion
          type: array
          items:
            $ref: '#/components/schemas/TaskTypeTranslationDto'
        statuses:
          description: Array of task status IDs that can be used with this task type
          example:
            - status-1
            - status-2
          type: array
          items:
            type: string
        customFields:
          description: Array of custom field IDs that are available for this task type
          example:
            - field-1
            - field-2
          type: array
          items:
            type: string
        templateDoc:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <h1>Task Template</h1><p>This is a template for new tasks of this type.</p>
        requiredFields:
          description: 'Array of required field names for this task type. This is contextual metadata for POST /tasks: fields listed here must be included even when the global create-task OpenAPI schema marks them optional. Common values include summary, estimatedTime, deadline, assignedToId, accountableId, and custom field names.'
          example:
            - title
            - description
            - summary
            - estimatedTime
          type: array
          items:
            type: string
      required:
        - name
        - icon
        - type
        - translations
        - statuses
        - customFields
        - templateDoc
        - requiredFields
    UpdateTaskTypeDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the task type
          example: Feature
        icon:
          type: string
          description: 'Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)'
          example: ':rocket:'
        type:
          type: string
          description: Type of the task type
          enum:
            - Feature
            - Bug
          example: Feature
        translations:
          description: Translations for the task type
          example:
            - language: en
              name: Feature
            - language: de
              name: Funktion
          type: array
          items:
            $ref: '#/components/schemas/TaskTypeTranslationDto'
        statuses:
          description: Array of task status IDs that can be used with this task type
          example:
            - status-1
            - status-2
          type: array
          items:
            type: string
        customFields:
          description: Array of custom field IDs that are available for this task type
          example:
            - field-1
            - field-2
          type: array
          items:
            type: string
        templateDoc:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <h1>Task Template</h1><p>This is a template for new tasks of this type.</p>
        requiredFields:
          description: Array of required field names for this task type
          example:
            - title
            - description
          type: array
          items:
            type: string
    PatchTaskTypeDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the task type
          example: Feature
        icon:
          type: string
          description: 'Icon for the task type in format :icon_name: (e.g., :rocket:, :bug:, :star:)'
          example: ':rocket:'
        type:
          type: string
          description: Type of the task type
          enum:
            - Feature
            - Bug
          example: Feature
        translations:
          description: Translations for the task type
          example:
            - language: en
              name: Feature
            - language: de
              name: Funktion
          type: array
          items:
            $ref: '#/components/schemas/TaskTypeTranslationDto'
        statuses:
          description: Array of task status IDs that can be used with this task type
          example:
            - status-1
            - status-2
          type: array
          items:
            type: string
        customFields:
          description: Array of custom field IDs that are available for this task type
          example:
            - field-1
            - field-2
          type: array
          items:
            type: string
        templateDoc:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <h1>Task Template</h1><p>This is a template for new tasks of this type.</p>
        requiredFields:
          description: Array of required field names for this task type
          example:
            - title
            - description
          type: array
          items:
            type: string
    SortTaskTypesDto:
      type: object
      properties:
        ids:
          description: Array of task type IDs in the desired sort order
          example:
            - type-1
            - type-2
            - type-3
          type: array
          items:
            type: string
      required:
        - ids
    ObjectCustomFieldResponseDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        sort:
          type: number
        type:
          type: string
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
        entity:
          type: string
          enum:
            - Task
            - Project
            - Organization
            - Object
        objectTypeId:
          type: string
        showInList:
          type: boolean
        showInHeader:
          type: boolean
        translations:
          type: array
        selectOptions:
          type: array
      required:
        - id
        - name
        - description
        - sort
        - type
        - entity
        - objectTypeId
        - showInList
        - showInHeader
        - translations
        - selectOptions
    SaveSortDTO:
      type: object
      properties: {}
    CustomFieldTranslationPublicDto:
      type: object
      properties:
        language:
          type: string
        name:
          type: string
      required:
        - language
        - name
    FieldOptionPublicDto:
      type: object
      properties:
        id:
          type: object
        value:
          type: string
        translations:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationPublicDto'
      required:
        - id
        - value
        - translations
    CreateObjectCustomFieldDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        type:
          type: string
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
        objectTypeId:
          type: string
          description: Must be the object type this field belongs to
        showInList:
          type: boolean
        showInHeader:
          type: boolean
        selectOptions:
          type: array
          items:
            $ref: '#/components/schemas/FieldOptionPublicDto'
        translations:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationPublicDto'
        translationsDescriptions:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationPublicDto'
      required:
        - name
        - type
        - objectTypeId
        - translations
        - translationsDescriptions
    UpdateObjectCustomFieldDto:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        type:
          type: string
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
        objectTypeId:
          type: string
        showInList:
          type: boolean
        showInHeader:
          type: boolean
        selectOptions:
          type: array
          items:
            $ref: '#/components/schemas/FieldOptionPublicDto'
        translations:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationPublicDto'
        translationsDescriptions:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldTranslationPublicDto'
      required:
        - name
        - type
        - objectTypeId
        - translations
        - translationsDescriptions
    ProjectCategoryTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German)
          example: en
        name:
          type: object
          description: Translated name of the project category in this language. Used for multilingual display in the user interface.
          example: Development
        description:
          type: object
          description: Translated description of the project category in this language. Provides context about when to use this category.
          example: Software development projects
      required:
        - language
    ProjectCategoryResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the project category. Used to reference this category when creating or updating projects.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Display name of the project category. Shown in dropdowns and project lists.
          example: Development
        icon:
          type: string
          description: Icon identifier in emoji format (e.g., ":rocket:"). Displayed next to the category name throughout the application.
          example: ':rocket:'
        description:
          type: object
          description: Optional description providing context about when to use this category.
          example: Projects related to software development
        sort:
          type: number
          description: Numeric sort order for display purposes. Lower numbers appear first in lists and dropdowns. May be undefined if not explicitly set.
          example: 1
        translations:
          description: Complete array of translations for all supported languages. Each translation contains the language code and localized name/description.
          example:
            - language: en
              name: Development
              description: Software development projects
            - language: de
              name: Entwicklung
              description: Softwareentwicklungsprojekte
          type: array
          items:
            $ref: '#/components/schemas/ProjectCategoryTranslationDto'
      required:
        - id
        - name
        - icon
        - translations
    CreateProjectCategoryDto:
      type: object
      properties:
        name:
          type: string
          description: Display name for the project category. Used to organize projects by type or purpose (e.g., "Development", "Support", "New Client Project").
          example: Development
        icon:
          type: string
          description: Icon identifier in emoji format. Must be wrapped in colons (e.g., ":rocket:", ":wrench:", ":briefcase:"). The icon appears next to the category name throughout the application.
          example: ':rocket:'
        description:
          type: object
          description: Optional description providing context about when to use this category. Helps users understand the purpose of the category.
          example: Projects related to software development
        translations:
          description: Array of translations for multilingual support. Each translation object contains a language code and translated name/description. The system automatically displays the correct language based on user preferences.
          example:
            - language: en
              name: Development
              description: Software development projects
            - language: de
              name: Entwicklung
              description: Softwareentwicklungsprojekte
          type: array
          items:
            $ref: '#/components/schemas/ProjectCategoryTranslationDto'
      required:
        - name
        - icon
        - translations
    UpdateProjectCategoryDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the project category
          example: Development
        icon:
          type: string
          description: 'Icon for the project category in format :icon_name:'
          example: ':rocket:'
        description:
          type: object
          description: Description of the project category
          example: Projects related to software development
        translations:
          description: Translations for the project category
          type: array
          items:
            $ref: '#/components/schemas/ProjectCategoryTranslationDto'
    PatchProjectCategoryDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the project category
          example: Development
        icon:
          type: string
          description: 'Icon for the project category in format :icon_name:'
          example: ':rocket:'
        description:
          type: object
          description: Description of the project category
          example: Projects related to software development
        translations:
          description: Translations for the project category
          type: array
          items:
            $ref: '#/components/schemas/ProjectCategoryTranslationDto'
    SortProjectCategoriesDto:
      type: object
      properties:
        ids:
          description: Array of project category IDs in the desired order
          example:
            - category-1
            - category-2
            - category-3
          type: array
          items:
            type: string
      required:
        - ids
    ProjectStatusTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German)
          example: en
        name:
          type: object
          description: Translated name of the project status in this language. Used for multilingual display in the user interface.
          example: In Progress
        description:
          type: object
          description: Translated description of the project status in this language. Provides context about when to use this status.
          example: Project is currently in progress
      required:
        - language
    ProjectStatusResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the project status. Used to reference this status when creating or updating projects.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Display name of the project status. Shown in dropdowns and project lists.
          example: In Progress
        icon:
          type: string
          description: Icon identifier in emoji format (e.g., ":hourglass_flowing_sand:"). Displayed next to the status name throughout the application.
          example: ':hourglass_flowing_sand:'
        type:
          type: string
          description: 'Status type enum value that categorizes the status. Determines billing eligibility and workflow behavior. Valid values: Sales, Requirements, Implementation, QualityManagement, Billing, Done.'
          enum:
            - Sales
            - Requirements
            - Implementation
            - QualityManagement
            - Billing
            - Done
          example: Implementation
        description:
          type: object
          description: Optional description providing context about when to use this status.
          example: Project is currently in development phase
        sort:
          type: number
          description: Numeric sort order for display purposes. Lower numbers appear first in lists and dropdowns. May be undefined if not explicitly set.
          example: 1
        translations:
          description: Complete array of translations for all supported languages. Each translation contains the language code and localized name/description.
          example:
            - language: en
              name: In Progress
              description: Project is currently in progress
            - language: de
              name: In Bearbeitung
              description: Projekt ist derzeit in Bearbeitung
          type: array
          items:
            $ref: '#/components/schemas/ProjectStatusTranslationDto'
      required:
        - id
        - name
        - icon
        - type
        - translations
    CreateProjectStatusDto:
      type: object
      properties:
        name:
          type: string
          description: Display name for the project status. Represents a phase in the project workflow (e.g., "In Progress", "On Hold", "Quality Review").
          example: In Progress
        icon:
          type: string
          description: Icon identifier in emoji format. Must be wrapped in colons (e.g., ":hourglass_flowing_sand:", ":check_mark:", ":moneybag:"). The icon appears next to the status name throughout the application.
          example: ':hourglass_flowing_sand:'
        type:
          type: string
          description: 'Status type enum value that categorizes the status. Determines billing eligibility and workflow behavior. Valid values: Sales, Requirements, Implementation, QualityManagement, Billing, Done.'
          enum:
            - Sales
            - Requirements
            - Implementation
            - QualityManagement
            - Billing
            - Done
          example: Implementation
        description:
          type: object
          description: Optional description providing context about when to use this status. Helps users understand the purpose of the status.
          example: Project is currently in development phase
        translations:
          description: Array of translations for multilingual support. Each translation object contains a language code and translated name/description. The system automatically displays the correct language based on user preferences.
          example:
            - language: en
              name: In Progress
              description: Project is currently in progress
            - language: de
              name: In Bearbeitung
              description: Projekt ist derzeit in Bearbeitung
          type: array
          items:
            $ref: '#/components/schemas/ProjectStatusTranslationDto'
      required:
        - name
        - icon
        - type
        - translations
    UpdateProjectStatusDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the project status
          example: In Progress
        icon:
          type: string
          description: 'Icon for the project status in format :icon_name:'
          example: ':hourglass_flowing_sand:'
        type:
          type: string
          description: Type of the project status
          enum:
            - Sales
            - Requirements
            - Implementation
            - QualityManagement
            - Billing
            - Done
          example: Implementation
        description:
          type: object
          description: Description of the project status
          example: Project is currently in development phase
        translations:
          description: Translations for the project status
          type: array
          items:
            $ref: '#/components/schemas/ProjectStatusTranslationDto'
    PatchProjectStatusDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the project status
          example: In Progress
        icon:
          type: string
          description: 'Icon for the project status in format :icon_name:'
          example: ':hourglass_flowing_sand:'
        type:
          type: string
          description: Type of the project status
          enum:
            - Sales
            - Requirements
            - Implementation
            - QualityManagement
            - Billing
            - Done
          example: Implementation
        description:
          type: object
          description: Description of the project status
          example: Project is currently in development phase
        translations:
          description: Translations for the project status
          type: array
          items:
            $ref: '#/components/schemas/ProjectStatusTranslationDto'
    SortProjectStatusesDto:
      type: object
      properties:
        ids:
          description: Array of project status IDs in the desired order
          example:
            - status-1
            - status-2
            - status-3
          type: array
          items:
            type: string
      required:
        - ids
    ProjectPhaseTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German)
          example: en
        name:
          type: object
          description: Translated name of the project phase in this language. Used for multilingual display in the user interface.
          example: Planning
        description:
          type: object
          description: Translated description of the project phase in this language. Provides context about when to use this phase.
          example: Project planning and preparation phase
      required:
        - language
    ProjectPhaseResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the project phase. Used to reference this phase when creating or updating opportunities.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Display name of the project phase. Shown in dropdowns and the Sales Kanban board.
          example: Planning
        icon:
          type: string
          description: Icon identifier in emoji format (e.g., ":calendar:"). Displayed next to the phase name throughout the application.
          example: ':calendar:'
        type:
          type: string
          description: 'Phase type enum value that determines which Kanban column the opportunity appears in. Valid values: new, inProgress, done.'
          enum:
            - new
            - inProgress
            - done
          example: new
        description:
          type: object
          description: Optional description providing context about when to use this phase.
          example: Initial planning and requirement gathering phase
        sort:
          type: number
          description: Numeric sort order for display purposes. Lower numbers appear first in lists and dropdowns. May be undefined if not explicitly set.
          example: 1
        translations:
          description: Complete array of translations for all supported languages. Each translation contains the language code and localized name/description.
          example:
            - language: en
              name: Planning
              description: Project planning phase
            - language: de
              name: Planung
              description: Projektplanungsphase
          type: array
          items:
            $ref: '#/components/schemas/ProjectPhaseTranslationDto'
      required:
        - id
        - name
        - icon
        - type
        - translations
    CreateProjectPhaseDto:
      type: object
      properties:
        name:
          type: string
          description: Display name for the project phase. Represents a stage in the sales pipeline (e.g., "Qualification", "Proposal Sent", "Negotiation").
          example: Planning
        icon:
          type: string
          description: Icon identifier in emoji format. Must be wrapped in colons (e.g., ":calendar:", ":phone:", ":trophy:"). The icon appears next to the phase name throughout the application.
          example: ':calendar:'
        type:
          type: string
          description: 'Phase type enum value that determines which Kanban column the opportunity appears in. Valid values: new (appears in "New" column), inProgress (appears in "In Progress" column), done (appears in "Done" column).'
          enum:
            - new
            - inProgress
            - done
          example: new
        description:
          type: object
          description: Optional description providing context about when to use this phase. Helps users understand the purpose of the phase.
          example: Initial planning and requirement gathering phase
        translations:
          description: Array of translations for multilingual support. Each translation object contains a language code and translated name/description. The system automatically displays the correct language based on user preferences.
          example:
            - language: en
              name: Planning
              description: Project planning phase
            - language: de
              name: Planung
              description: Projektplanungsphase
          type: array
          items:
            $ref: '#/components/schemas/ProjectPhaseTranslationDto'
      required:
        - name
        - icon
        - type
        - translations
    UpdateProjectPhaseDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the project phase
          example: Planning
        icon:
          type: string
          description: 'Icon for the project phase in format :icon_name:'
          example: ':calendar:'
        type:
          type: string
          description: Type of the project phase
          enum:
            - new
            - inProgress
            - done
          example: new
        description:
          type: object
          description: Description of the project phase
          example: Initial planning and requirement gathering phase
        translations:
          description: Translations for the project phase
          type: array
          items:
            $ref: '#/components/schemas/ProjectPhaseTranslationDto'
    PatchProjectPhaseDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the project phase
          example: Planning
        icon:
          type: string
          description: 'Icon for the project phase in format :icon_name:'
          example: ':calendar:'
        type:
          type: string
          description: Type of the project phase
          enum:
            - new
            - inProgress
            - done
          example: new
        description:
          type: object
          description: Description of the project phase
          example: Initial planning and requirement gathering phase
        translations:
          description: Translations for the project phase
          type: array
          items:
            $ref: '#/components/schemas/ProjectPhaseTranslationDto'
    SortProjectPhasesDto:
      type: object
      properties:
        ids:
          description: Array of project phase IDs in the desired order
          example:
            - phase-1
            - phase-2
            - phase-3
          type: array
          items:
            type: string
      required:
        - ids
    ProjectCustomFieldTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German)
          example: en
        name:
          type: string
          description: Translated name of the custom field in this language. Used for multilingual display in project forms.
          example: Priority Level
        description:
          type: string
          description: Translated description of the custom field in this language. Provides context about what information should be entered.
          example: Priority level for the project
      required:
        - language
    ProjectCustomFieldSelectOptionTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German)
          example: en
        name:
          type: string
          description: Translated display name for the select option in this language. Shown to users when selecting from dropdown options.
          example: High Priority
      required:
        - language
        - name
    ProjectCustomFieldSelectOptionDto:
      type: object
      properties:
        id:
          type: string
          description: Optional unique identifier for the select option. If not provided, the value will be used as the identifier. Required when updating existing options.
          example: option-1
        value:
          type: string
          description: Display value for the select option. This is the value stored when the option is selected. Must be unique within the field.
          example: High Priority
        translations:
          description: Array of translations for multilingual support. Each translation contains a language code and translated display name. The system automatically shows the correct language based on user preferences.
          example:
            - language: en
              name: High Priority
            - language: de
              name: Hohe Priorität
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldSelectOptionTranslationDto'
      required:
        - value
        - translations
    ProjectCustomFieldResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the custom field. Used to reference this field when setting values on projects.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Display name of the custom field. Shown as the field label in project forms.
          example: Priority Level
        description:
          type: string
          description: Description providing context about what information should be entered in this field.
          example: Priority level for the project
        sort:
          type: number
          description: Numeric sort order for display purposes. Lower numbers appear first in project forms. Determines the order in which fields are displayed.
          example: 1
        type:
          type: string
          description: 'Field type enum value indicating the input type. Valid values: Text, Textarea, Number, Date, Checkbox, Select, MultiSelect.'
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        entity:
          type: string
          description: Entity type identifier. Always "Project" for project custom fields. Used internally to scope fields to specific entity types.
          example: Project
        translations:
          description: Complete array of translations for the field name. Each translation contains the language code and localized name.
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldTranslationDto'
        selectOptions:
          description: Array of select options for Select and MultiSelect field types. Each option contains an ID, value, and translations. Empty array for other field types.
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldSelectOptionDto'
      required:
        - id
        - name
        - description
        - sort
        - type
        - entity
        - translations
        - selectOptions
    CreateProjectCustomFieldDto:
      type: object
      properties:
        id:
          type: string
          description: Optional unique identifier for the custom field. Only needed when updating an existing field. If omitted, a new field will be created.
        name:
          type: string
          description: Display name for the custom field. Shown as the field label in project forms (e.g., "Priority Level", "Region", "Server Name").
          example: Priority Level
        description:
          type: string
          description: Optional description providing context about what information should be entered in this field. Helps users understand the purpose of the field.
          example: Priority level for the project
        type:
          type: string
          description: 'Field type enum value that determines what kind of input is used. Valid values: Text (single-line), Textarea (multi-line), Number, Date, Checkbox, Select (single choice), MultiSelect (multiple choices).'
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        selectOptions:
          description: Array of select options. REQUIRED for Select and MultiSelect field types. Each option contains a value and translations. Not used for other field types.
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldSelectOptionDto'
        translations:
          description: Array of translations for the field name. Each translation contains a language code and translated name. The system automatically displays the correct language based on user preferences.
          example:
            - language: en
              name: Priority Level
            - language: de
              name: Prioritätsstufe
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldTranslationDto'
        translationsDescriptions:
          description: Optional array of translations for the field description. Each translation contains a language code and translated description. Used for multilingual help text.
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldTranslationDto'
      required:
        - name
        - type
        - translations
    UpdateProjectCustomFieldDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the custom field
          example: Priority Level
        description:
          type: string
          description: Description of the custom field
          example: Priority level for the project
        type:
          type: string
          description: Type of the custom field
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        selectOptions:
          description: Select options (required for Select type fields)
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldSelectOptionDto'
        translations:
          description: Translations for the custom field name
          example:
            - language: en
              name: Priority Level
            - language: de
              name: Prioritätsstufe
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldTranslationDto'
        translationsDescriptions:
          description: Translations for the custom field description
          type: array
          items:
            $ref: '#/components/schemas/ProjectCustomFieldTranslationDto'
      required:
        - name
        - type
        - translations
    SortProjectCustomFieldsDto:
      type: object
      properties:
        ids:
          description: Array of custom field IDs in the desired order
          example:
            - field-1
            - field-2
            - field-3
          type: array
          items:
            type: string
      required:
        - ids
    RestoreProjectSettingsDto:
      type: object
      properties:
        keepCustom:
          type: boolean
          description: Boolean flag controlling whether custom entries are preserved when restoring defaults. If false (default), all custom categories, statuses, and phases are deleted and only defaults are restored. If true, custom entries are kept and defaults are added alongside them.
          example: false
          default: false
      required:
        - keepCustom
    SupportContingentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the support contingent (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        projectId:
          type: string
          description: ID of the project this contingent belongs to (UUID)
          example: 550e8400-e29b-41d4-a716-446655440001
        title:
          type: string
          description: Title or name of the support contingent plan
          example: Maintenance flatrate
        fromDate:
          type: string
          description: Start date of the contingent period (ISO 8601 date format)
          example: '2025-01-01'
        toDate:
          type: string
          description: End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.
          example: '2025-12-31'
        frequency:
          type: number
          description: Frequency in months for the contingent period
          example: 12
        price:
          type: number
          description: Total price for the contingent period
          example: 10000
        hours:
          type: number
          description: Total hours allocated for the contingent period
          example: 120
        rate:
          type: number
          description: Calculated hourly rate (price / hours). Automatically computed.
          example: 83.33
        transferable:
          type: boolean
          description: Whether unused hours can be transferred to the next period
          example: false
        createdAt:
          type: string
          description: ISO 8601 timestamp when the contingent was created
          example: '2024-01-15T10:00:00Z'
        updatedAt:
          type: string
          description: ISO 8601 timestamp when the contingent was last updated
          example: '2024-01-20T14:30:00Z'
      required:
        - id
        - projectId
        - title
        - fromDate
        - frequency
        - price
        - hours
        - rate
        - transferable
        - createdAt
        - updatedAt
    CreateSupportContingentDto:
      type: object
      properties:
        projectId:
          type: string
          description: ID of the project this contingent belongs to (UUID)
          example: 550e8400-e29b-41d4-a716-446655440001
        title:
          type: string
          description: Title or name of the support contingent plan
          example: Maintenance flatrate
        fromDate:
          type: string
          description: Start date of the contingent period (ISO 8601 date format)
          example: '2025-01-01'
        toDate:
          type: string
          description: End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.
          example: '2025-12-31'
        frequency:
          type: number
          description: Frequency in months for the contingent period
          example: 12
        price:
          type: number
          description: Total price for the contingent period
          example: 10000
        hours:
          type: number
          description: Total hours allocated for the contingent period
          example: 120
        transferable:
          type: boolean
          description: Whether unused hours can be transferred to the next period
          example: false
      required:
        - projectId
        - title
        - fromDate
        - frequency
        - price
        - hours
    UpdateSupportContingentDto:
      type: object
      properties:
        title:
          type: string
          description: Title or name of the support contingent plan
          example: Maintenance flatrate
        fromDate:
          type: string
          description: Start date of the contingent period (ISO 8601 date format)
          example: '2025-01-01'
        toDate:
          type: string
          description: End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.
          example: '2025-12-31'
        frequency:
          type: number
          description: Frequency in months for the contingent period
          example: 12
        price:
          type: number
          description: Total price for the contingent period
          example: 10000
        hours:
          type: number
          description: Total hours allocated for the contingent period
          example: 120
        transferable:
          type: boolean
          description: Whether unused hours can be transferred to the next period
          example: false
      required:
        - title
        - fromDate
        - frequency
        - price
        - hours
    PatchSupportContingentDto:
      type: object
      properties:
        title:
          type: string
          description: Title or name of the support contingent plan
          example: Maintenance flatrate
        fromDate:
          type: string
          description: Start date of the contingent period (ISO 8601 date format)
          example: '2025-01-01'
        toDate:
          type: string
          description: End date of the contingent period (ISO 8601 date format)
          example: '2025-12-31'
        frequency:
          type: number
          description: Frequency in months for the contingent period
          example: 12
        price:
          type: number
          description: Total price for the contingent period
          example: 10000
        hours:
          type: number
          description: Total hours allocated for the contingent period
          example: 120
        transferable:
          type: boolean
          description: Whether unused hours can be transferred to the next period
          example: false
    ImportsGridResponse:
      type: object
      properties:
        items:
          type: array
          description: Array of import items in the current page. Each item represents a CSV import with its metadata and status.
          items:
            type: object
            properties:
              id:
                type: string
                description: Unique identifier (UUID) of the import
              fileName:
                type: string
                nullable: true
                description: Original name of the uploaded CSV file
              status:
                type: string
                description: 'Current status: "Uploaded" (ready), "Processing" (running), "Completed" (success), or "Failed" (error)'
              createdAt:
                type: string
                format: date-time
                description: ISO 8601 timestamp when the import was created
              importedAt:
                type: string
                format: date-time
                nullable: true
                description: ISO 8601 timestamp when the import finished processing (null if not completed)
              rowCount:
                type: number
                description: Total number of data rows in the CSV file
              columnCount:
                type: number
                description: Total number of columns in the CSV file
        total:
          type: number
          description: Total number of imports matching the current filters (across all pages)
          example: 42
        page:
          type: number
          description: Current page number (1-based indexing)
          example: 1
        pageSize:
          type: number
          description: Number of items per page in this response
          example: 100
        viewId:
          type: string
          description: View identifier for saved grid configurations (e.g., "all", "recent", custom view name)
          example: all
      required:
        - items
        - total
        - page
        - pageSize
        - viewId
    UploadImportResponseDto:
      type: object
      properties:
        importId:
          type: string
          description: Unique identifier (UUID) for the newly created import. Use this ID to fetch details, update mappings, or execute the import.
          example: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
        fileName:
          type: string
          description: Original file name of the uploaded CSV file
          example: employees.csv
        rowCount:
          type: number
          description: Total number of data rows in the CSV file (excluding header row)
          example: 150
        columnCount:
          type: number
          description: Total number of columns detected in the CSV file
          example: 12
        status:
          type: string
          description: Current status of the import. Will be "Uploaded" immediately after upload, indicating the import is ready for mapping configuration.
          example: Uploaded
          enum:
            - Uploaded
            - Processing
            - Completed
            - Failed
        mappings:
          type: array
          description: AI-generated mapping suggestions in AI format. Each mapping specifies an entity type (e.g., "Employee", "Project") and suggested field mappings from CSV columns to entity fields. Review and modify these before executing the import.
          items:
            type: object
            properties:
              type:
                type: string
                description: 'Entity type to import: "Employee", "Organization", "OrganizationMember", "Project", "Task", or "TaskComment"'
              fields:
                type: object
                description: Field mappings object. Keys are entity field names, values contain mapTo (column UUID), optional defaultValue, and optional valueMappings for enums
              customLookupFields:
                type: array
                items:
                  type: string
                description: Optional array of field names (including custom fields like "customField_abc123") to use for finding existing records. If not provided, uses entity default lookup fields.
        columns:
          type: array
          description: Metadata for each column in the CSV file, including column UUID (use this in mappings), name, detected data type, and sample values. Column UUIDs are required when creating field mappings.
        sampleRows:
          type: array
          description: First 10 rows of data from the CSV file for preview. Useful for understanding the data structure and validating mappings.
        aiMappingSuccess:
          type: boolean
          description: Indicates whether AI mapping generation completed successfully. If false, mappings array may be empty or incomplete, and you may need to manually configure mappings.
          example: true
      required:
        - importId
        - fileName
        - rowCount
        - columnCount
        - status
        - mappings
        - columns
        - sampleRows
        - aiMappingSuccess
    GetImportDetailsResponseDto:
      type: object
      properties:
        importId:
          type: string
          description: Unique identifier (UUID) of the import
          example: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
        fileName:
          type: object
          description: Original name of the uploaded CSV file
          example: employees.csv
          nullable: true
        rowCount:
          type: number
          description: Total number of data rows in the CSV file (excluding header)
          example: 150
        columnCount:
          type: number
          description: Total number of columns detected in the CSV file
          example: 12
        status:
          type: string
          description: 'Current status of the import: "Uploaded" (ready), "Processing" (running), "Completed" (success), or "Failed" (error). Check processedEntities vs totalToProcess to see progress during processing.'
          example: Uploaded
          enum:
            - Uploaded
            - Processing
            - Completed
            - Failed
        createdAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp when the import was created
          example: '2024-01-15T10:30:00Z'
        importedAt:
          type: object
          description: ISO 8601 timestamp when the import finished processing. Null if import has not completed yet.
          example: '2024-01-15T10:35:00Z'
          nullable: true
        mappings:
          type: array
          description: All current field mappings in AI format. Each mapping specifies entity type, field mappings (CSV column UUIDs to entity fields), value mappings, and custom lookup fields. Use these mappings to understand how CSV data will be imported.
        columns:
          type: array
          description: Metadata for each CSV column including UUID (required for mappings), name, detected data type, and sample values. Column UUIDs are used in field mappings.
        sampleRows:
          type: array
          description: First 10 rows of data from the CSV file for preview. Useful for understanding data structure and validating that mappings are correct.
        fileSize:
          type: object
          description: Size of the uploaded CSV file in bytes
          example: 52480
          nullable: true
        mimeType:
          type: object
          description: MIME type of the uploaded file (typically "text/csv", "application/csv", or "text/plain")
          example: text/csv
          nullable: true
        processedEntities:
          type: number
          description: Number of entities that have been processed so far. Compare with totalToProcess to calculate progress percentage. Only meaningful when status is "Processing" or "Completed".
          example: 100
        totalToProcess:
          type: number
          description: Total number of entities that need to be processed. This is calculated based on the number of rows and mappings. Compare with processedEntities to track progress.
          example: 150
        totalCreatedEntities:
          type: number
          description: Total number of new entities that were created during import execution. Only available after import completes.
          example: 80
        totalUpdatedEntities:
          type: number
          description: Total number of existing entities that were updated during import execution. Only available after import completes.
          example: 15
        totalSkippedEntities:
          type: number
          description: Total number of rows that were skipped during import execution (due to validation errors, missing required fields, etc.). Only available after import completes.
          example: 5
      required:
        - importId
        - fileName
        - rowCount
        - columnCount
        - status
        - createdAt
        - importedAt
        - mappings
        - columns
        - sampleRows
        - fileSize
        - mimeType
        - processedEntities
        - totalToProcess
        - totalCreatedEntities
        - totalUpdatedEntities
        - totalSkippedEntities
    UpdateImportMappingsRequestDto:
      type: object
      properties:
        mappings:
          type: array
          description: Array of mapping configurations in AI format. Each mapping specifies an entity type and how CSV columns map to entity fields. This replaces all existing mappings for the import.
          items:
            type: object
            properties:
              type:
                type: string
                description: 'Entity type to import: "Employee", "Organization", "OrganizationMember", "Project", "Task", or "TaskComment"'
                example: Employee
              fields:
                type: object
                description: 'Field mappings object. Keys are entity field names (or "customField_{fieldId}" for custom fields). Values are objects with: mapTo (required, CSV column UUID), defaultValue (optional), and valueMappings (optional, for enum conversions).'
                example:
                  firstName:
                    mapTo: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
                  lastName:
                    mapTo: a3ff87f4-06e8-5c09-9e6e-09ed2gc80475
                  email:
                    mapTo: b4gg98g5-17f9-6d10-0f7f-10fe3hd91586
                  role:
                    mapTo: c5hh09h6-28g0-7e11-1g8g-21gf4ie02697
                    valueMappings:
                      Manager: manager
                      Developer: developer
                  customField_abc123:
                    mapTo: d6ii10i7-39h1-8f12-2h9h-32hg5jf13708
              customLookupFields:
                type: array
                items:
                  type: string
                description: Optional array of field names to use for finding existing records. Can include built-in fields (e.g., "email", "firstName") or custom fields (e.g., "customField_abc123"). If a record matches on these fields, it will be updated; otherwise, a new record will be created. If not provided, uses the entity's default requiredToUpdate fields.
                example:
                  - email
          example:
            - type: Employee
              fields:
                firstName:
                  mapTo: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
                lastName:
                  mapTo: a3ff87f4-06e8-5c09-9e6e-09ed2gc80475
                email:
                  mapTo: b4gg98g5-17f9-6d10-0f7f-10fe3hd91586
              customLookupFields:
                - email
      required:
        - mappings
    UpdateImportMappingsResponseDto:
      type: object
      properties:
        success:
          type: boolean
          description: Indicates whether the mapping update was successful. If true, all mappings have been saved and validated.
          example: true
        mappings:
          type: array
          description: Updated mappings in AI format, including any automatically added dependency mappings (e.g., Organization mapping if OrganizationMember was specified). Use these mappings to verify the final configuration.
      required:
        - success
        - mappings
    ImportSummaryDto:
      type: object
      properties:
        totalRows:
          type: number
          description: Total number of rows that were processed during the dry-run or execution
          example: 150
        willCreate:
          type: number
          description: Number of new entities that will be created (for dry-run) or were created (for execution). These are rows where no matching existing record was found using the lookup fields.
          example: 100
        willUpdate:
          type: number
          description: Number of existing entities that will be updated (for dry-run) or were updated (for execution). These are rows where a matching existing record was found using the lookup fields.
          example: 40
        willSkip:
          type: number
          description: Number of rows that will be skipped (for dry-run) or were skipped (for execution). These rows had validation errors, missing required fields, or other issues that prevented processing.
          example: 10
        errors:
          type: number
          description: Number of rows that have validation errors. These are included in willSkip but counted separately to highlight data quality issues. Check the errors array for detailed error messages.
          example: 5
      required:
        - totalRows
        - willCreate
        - willUpdate
        - willSkip
        - errors
    DryRunResponseDto:
      type: object
      properties:
        dryRun:
          type: boolean
          description: Always true for dry-run operations. Indicates this is a preview simulation, not an actual execution.
          example: true
        mappings:
          type: array
          description: Detailed results for each entity type mapping. Each result includes lists of entities that will be created (toCreate), updated (toUpdate with old/new values), and skipped (ignored with reasons). Use this to understand exactly what will happen for each entity type.
        summary:
          description: Summary statistics aggregating results across all mappings. Provides quick overview of total rows, create/update/skip counts, and error count.
          allOf:
            - $ref: '#/components/schemas/ImportSummaryDto'
        errors:
          type: array
          description: List of all rows that will be skipped due to errors, including row index, the data from that row, and the specific error message explaining why it will be skipped. Use this to identify and fix data quality issues before execution.
      required:
        - dryRun
        - mappings
        - summary
        - errors
    ExecuteImportResponseDto:
      type: object
      properties:
        queued:
          type: boolean
          description: Indicates whether the import has been successfully queued for background processing. If true, the import will start processing asynchronously. Use GET /{importId} to monitor progress.
          example: true
        importId:
          type: string
          description: The import ID (UUID) that was queued. Use this ID with GET /{importId} to check status and monitor progress during background processing.
          example: d2ee76e3-95d7-4b98-8d5d-98dc1fb79364
        message:
          type: string
          description: Human-readable message explaining the import status and next steps for tracking progress
          example: Import has been queued for processing. Use GET /{importId} to check status.
      required:
        - queued
        - importId
        - message
    ProjectChartSheetDataDto:
      type: object
      properties:
        headers:
          description: Column headers for the sheet
          example:
            - Period
            - Period Label
            - Value
            - Cumulative Total
          type: array
          items:
            type: string
        rows:
          type: array
          description: Data rows as array of objects, where keys match headers. Each row represents one data point.
          items:
            type: object
          example:
            - Period: 2024-01
              Period Label: Jan 2024
              Value: 120.5
              Cumulative Total: 120.5
            - Period: 2024-02
              Period Label: Feb 2024
              Value: 135.2
              Cumulative Total: 255.7
        title:
          type: object
          description: Sheet title information with main title and subtitle
          example:
            main: 'Project Insights: Project Name'
            subtitle: Jan 2024 - Dec 2024, Time Series, By User, Months, Trend
      required:
        - headers
        - rows
        - title
    ProjectChartDataStructuredResponseDto:
      type: object
      properties:
        summary:
          description: Summary sheet data (wide/matrix format, like pivot table). Periods as rows, breakdown categories as columns.
          allOf:
            - $ref: '#/components/schemas/ProjectChartSheetDataDto'
        detailed:
          description: Detailed sheet data (long/tidy format, normalized). One row per period/breakdown combination.
          allOf:
            - $ref: '#/components/schemas/ProjectChartSheetDataDto'
        metadata:
          type: object
          description: Metadata about the query execution and results
          example:
            queryExecutionTime: 234
            totalTimeLogsProcessed: 1000
            periodsGenerated: 12
            hasMoreData: false
      required:
        - summary
        - detailed
        - metadata
    StaffChartSheetDataDto:
      type: object
      properties:
        headers:
          description: Column headers for the sheet
          example:
            - Period
            - Period Label
            - Employee
            - Hours
          type: array
          items:
            type: string
        rows:
          type: array
          description: Data rows as array of objects, where keys match headers. Each row represents one data point.
          items:
            type: object
          example:
            - Period: 2024-01
              Period Label: Jan 2024
              Employee: John Doe
              Hours: 160
            - Period: 2024-02
              Period Label: Feb 2024
              Employee: John Doe
              Hours: 175
        title:
          type: object
          description: Sheet title information with main title and subtitle
          example:
            main: 'Staff Insights: John Doe'
            subtitle: Comparison, Breakdown by Project, Jan 2024 - Dec 2024, By Months
      required:
        - headers
        - rows
        - title
    StaffChartDataStructuredResponseDto:
      type: object
      properties:
        summary:
          description: 'Summary sheet data. For COMPARISON model: wide format with periods/staff as rows and breakdown categories as columns. For TOTAL model: categories with hours and percentage.'
          allOf:
            - $ref: '#/components/schemas/StaffChartSheetDataDto'
        detailed:
          description: 'Detailed sheet data. For COMPARISON model: long format (normalized) with one row per period/staff/breakdown combination. For TOTAL model: same as summary.'
          allOf:
            - $ref: '#/components/schemas/StaffChartSheetDataDto'
        metadata:
          type: object
          description: Metadata about the query execution and results
          example:
            queryExecutionTime: 234
            totalTimeLogsProcessed: 480
            periodsGenerated: 12
            hasMoreData: false
      required:
        - summary
        - detailed
        - metadata
    TurnoverChartDataStructuredResponseDto:
      type: object
      properties:
        summary:
          description: Summary sheet data (wide/matrix format, like pivot table). Layout depends on `model` and `breakdown` (e.g. comparison+byProject vs comparison+byRevenueType vs total).
          allOf:
            - $ref: '#/components/schemas/ProjectChartSheetDataDto'
        detailed:
          description: Detailed sheet data (long/tidy format, normalized). One row per period with project and/or revenue-type columns depending on model and breakdown.
          allOf:
            - $ref: '#/components/schemas/ProjectChartSheetDataDto'
        forecast:
          description: Forecast sheet data (only present when includeTrend=true and forecast data exists). Contains 6-month revenue forecast based on subscription billing and project deadlines.
          allOf:
            - $ref: '#/components/schemas/ProjectChartSheetDataDto'
        metadata:
          type: object
          description: Metadata about the query execution and results
          example:
            queryExecutionTime: 234
            totalInvoicesProcessed: 120
            periodsGenerated: 12
            hasMoreData: false
      required:
        - summary
        - detailed
        - metadata
    GoalChartSheetDataDto:
      type: object
      properties:
        headers:
          description: Column headers for the sheet
          example:
            - Employee
            - Attendance Goal
            - Attendance Actual
            - Booked Goal
            - Booked Actual
            - Billable Goal
            - Billable Actual
          type: array
          items:
            type: string
        rows:
          type: array
          description: Data rows as array of objects, where keys match headers. Each row represents one employee or aggregated total.
          items:
            type: object
          example:
            - Employee: John Doe
              Attendance Goal: 160
              Attendance Actual: 152
              Booked Goal: 160
              Booked Actual: 148
              Billable Goal: 120
              Billable Actual: 115
            - Employee: Jane Smith
              Attendance Goal: 160
              Attendance Actual: 160
              Booked Goal: 160
              Booked Actual: 160
              Billable Goal: 120
              Billable Actual: 125
        title:
          type: object
          description: Sheet title information with main title and subtitle
          example:
            main: Goal Insights - Multiple Staff
            subtitle: This Month | Individual
      required:
        - headers
        - rows
        - title
    GoalChartDataStructuredResponseDto:
      type: object
      properties:
        goalProgress:
          description: 'Goal progress sheet data: Employee time tracking goals (attendance, booked, billable) with goals and actuals'
          allOf:
            - $ref: '#/components/schemas/GoalChartSheetDataDto'
        valueDistribution:
          description: 'Value distribution sheet data: Breakdown of hours by value groups (A, B, C, D)'
          allOf:
            - $ref: '#/components/schemas/GoalChartSheetDataDto'
        metadata:
          type: object
          description: Metadata about the query execution and results
          example:
            queryExecutionTime: 234
            totalEmployeesProcessed: 2
            attendanceEnabled: true
      required:
        - goalProgress
        - valueDistribution
        - metadata
    WorkloadChartSheetDataDto:
      type: object
      properties:
        headers:
          description: Column headers for the sheet
          example:
            - Period Label
            - Tasks
          type: array
          items:
            type: string
        rows:
          type: array
          description: Data rows as array of objects, where keys match headers. Each row represents one data point.
          items:
            type: object
          example:
            - Period Label: Jan 2024
              Tasks: 45
            - Period Label: Feb 2024
              Tasks: 52
        title:
          type: object
          description: Sheet title information with main title and subtitle
          example:
            main: 'Workload Insights: Project Name'
            subtitle: Over Time, Breakdown by Task Type, Jan 2024 - Dec 2024, By Months
      required:
        - headers
        - rows
        - title
    WorkloadChartDataStructuredResponseDto:
      type: object
      properties:
        summary:
          description: Summary sheet data (simple format). Period labels as rows, task counts as columns.
          allOf:
            - $ref: '#/components/schemas/WorkloadChartSheetDataDto'
        detailed:
          description: Detailed sheet data (long/normalized format). One row per period/breakdown combination.
          allOf:
            - $ref: '#/components/schemas/WorkloadChartSheetDataDto'
        metadata:
          type: object
          description: Metadata about the query execution and results
          example:
            queryExecutionTime: 234
            totalTasksProcessed: 1000
            periodsGenerated: 12
            hasMoreData: false
      required:
        - summary
        - detailed
        - metadata
    EmailAccountStatus:
      type: string
      enum:
        - active
        - token_expired
        - smtp_error
        - imap_error
        - disconnected
      description: Current operational status of the account. Indicates whether the account is working correctly or has encountered issues.
    EmailAccountCapabilitiesDto:
      type: object
      properties:
        canEdit:
          type: boolean
          description: Whether the account can be edited via API. This is `true` for IMAP accounts and `false` for OAuth accounts (Google, Office365) which must be managed through the web application for security reasons.
          example: true
        canDelete:
          type: boolean
          description: Whether the account can be deleted via API. This is `true` for all account types, including OAuth accounts.
          example: true
        reason:
          type: string
          description: Optional reason explaining why certain operations are restricted. This field is only present for OAuth accounts to explain why they cannot be created or updated via API.
          example: OAuth accounts can only be managed through the web application
      required:
        - canEdit
        - canDelete
    EmailAccountResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) of the email account
          example: 123e4567-e89b-12d3-a456-426614174000
          format: uuid
        email:
          type: string
          description: Email address associated with this account. Used for sending and receiving emails.
          example: support@example.com
        displayName:
          type: object
          description: Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.
          example: Support Team
        type:
          type: string
          description: Type of email account. Determines how the account is authenticated and what operations are available via API.
          enum:
            - imap
            - google
            - office365
          example: imap
        status:
          description: Current operational status of the account. Indicates whether the account is working correctly or has encountered issues.
          example: active
          allOf:
            - $ref: '#/components/schemas/EmailAccountStatus'
        lastError:
          type: object
          description: Most recent error message, if the account has encountered any issues. This helps diagnose connection or authentication problems.
          example: Failed to connect to SMTP server
        createdAt:
          type: string
          description: ISO 8601 timestamp indicating when the account was created
          example: '2024-01-15T10:30:00Z'
          format: date-time
        editedAt:
          type: string
          description: ISO 8601 timestamp indicating when the account was last modified
          example: '2024-01-20T14:45:00Z'
          format: date-time
        capabilities:
          description: Account capabilities indicating what operations can be performed via API. Includes restrictions for OAuth accounts.
          allOf:
            - $ref: '#/components/schemas/EmailAccountCapabilitiesDto'
      required:
        - id
        - email
        - type
        - status
        - createdAt
        - editedAt
        - capabilities
    CreateEmailAccountDto:
      type: object
      properties:
        email:
          type: string
          description: Email address for the account. Must be unique within the workspace and will be used for sending and receiving emails.
          example: support@example.com
        displayName:
          type: string
          description: Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.
          example: Support Team
        imapsHost:
          type: string
          description: IMAP server hostname for receiving emails. This is the server address where the email client connects to retrieve incoming emails (e.g., "imap.gmail.com" for Gmail, "outlook.office365.com" for Office365).
          example: imap.example.com
        imapsPort:
          type: number
          description: IMAP server port number. Typically 993 for SSL/TLS connections or 143 for unencrypted connections. Must be between 1-65535.
          example: 993
          minimum: 1
          maximum: 65535
        imapsUser:
          type: string
          description: Username for authenticating with the IMAP server. This is typically the full email address, but may vary depending on your email provider.
          example: support@example.com
        imapsPassword:
          type: string
          description: Password for authenticating with the IMAP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.
          example: secure-password
        smtpHost:
          type: string
          description: SMTP server hostname for sending emails. This is the server address where the email client connects to send outgoing emails (e.g., "smtp.gmail.com" for Gmail, "smtp.office365.com" for Office365).
          example: smtp.example.com
        smtpPort:
          type: number
          description: SMTP server port number. Typically 587 for STARTTLS, 465 for SSL/TLS, or 25 for unencrypted connections. Must be between 1-65535.
          example: 587
          minimum: 1
          maximum: 65535
        smtpUser:
          type: string
          description: Username for authenticating with the SMTP server. This is typically the full email address, but may vary depending on your email provider.
          example: support@example.com
        smtpPassword:
          type: string
          description: Password for authenticating with the SMTP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.
          example: secure-password
      required:
        - email
        - imapsHost
        - imapsPort
        - imapsUser
        - imapsPassword
        - smtpHost
        - smtpPort
        - smtpUser
        - smtpPassword
    UpdateEmailAccountDto:
      type: object
      properties:
        email:
          type: string
          description: Email address for the account. Must be unique within the workspace and will be used for sending and receiving emails.
          example: support@example.com
        displayName:
          type: string
          description: Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.
          example: Support Team
        imapsHost:
          type: string
          description: IMAP server hostname for receiving emails. This is the server address where the email client connects to retrieve incoming emails (e.g., "imap.gmail.com" for Gmail, "outlook.office365.com" for Office365).
          example: imap.example.com
        imapsPort:
          type: number
          description: IMAP server port number. Typically 993 for SSL/TLS connections or 143 for unencrypted connections. Must be between 1-65535.
          example: 993
          minimum: 1
          maximum: 65535
        imapsUser:
          type: string
          description: Username for authenticating with the IMAP server. This is typically the full email address, but may vary depending on your email provider.
          example: support@example.com
        imapsPassword:
          type: string
          description: Password for authenticating with the IMAP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.
          example: secure-password
        smtpHost:
          type: string
          description: SMTP server hostname for sending emails. This is the server address where the email client connects to send outgoing emails (e.g., "smtp.gmail.com" for Gmail, "smtp.office365.com" for Office365).
          example: smtp.example.com
        smtpPort:
          type: number
          description: SMTP server port number. Typically 587 for STARTTLS, 465 for SSL/TLS, or 25 for unencrypted connections. Must be between 1-65535.
          example: 587
          minimum: 1
          maximum: 65535
        smtpUser:
          type: string
          description: Username for authenticating with the SMTP server. This is typically the full email address, but may vary depending on your email provider.
          example: support@example.com
        smtpPassword:
          type: string
          description: Password for authenticating with the SMTP server. For some providers, you may need to use an app-specific password instead of your regular account password. This field is never returned in API responses for security reasons.
          example: secure-password
      required:
        - email
        - imapsHost
        - imapsPort
        - imapsUser
        - imapsPassword
        - smtpHost
        - smtpPort
        - smtpUser
        - smtpPassword
    PatchEmailAccountDto:
      type: object
      properties:
        email:
          type: string
          description: Email address for the account
          example: support@example.com
        displayName:
          type: string
          description: Optional display name for the email account. This name will be shown in the email account list and may be used as the sender name when sending emails.
          example: Support Team
        imapsHost:
          type: string
          description: IMAP server hostname
          example: imap.example.com
        imapsPort:
          type: number
          description: IMAP server port (typically 993 for SSL)
          example: 993
        imapsUser:
          type: string
          description: IMAP username
          example: support@example.com
        imapsPassword:
          type: string
          description: IMAP password
          example: secure-password
        smtpHost:
          type: string
          description: SMTP server hostname
          example: smtp.example.com
        smtpPort:
          type: number
          description: SMTP server port (typically 587 for TLS or 465 for SSL)
          example: 587
        smtpUser:
          type: string
          description: SMTP username
          example: support@example.com
        smtpPassword:
          type: string
          description: SMTP password
          example: secure-password
    TestEmailSendingDto:
      type: object
      properties:
        testEmailAddress:
          type: string
          description: Email address where the test email should be sent. This should be a valid email address that you can access to verify the test email was received.
          example: test@example.com
      required:
        - testEmailAddress
    TestEmailSendingResponseDto:
      type: object
      properties:
        success:
          type: boolean
          description: Indicates whether the test email was sent successfully. `true` means the email was sent, `false` means there was an error.
          example: true
        message:
          type: string
          description: Human-readable message describing the test result. Includes success confirmation or error details explaining what went wrong.
          example: Test email sent successfully
      required:
        - success
        - message
    TestEmailReceivingResponseDto:
      type: object
      properties:
        success:
          type: boolean
          description: Indicates whether the test was successful. `true` means the connection was established and an email was retrieved, `false` means there was an error.
          example: true
        message:
          type: string
          description: Human-readable message describing the test result. Includes success confirmation or error details explaining what went wrong.
          example: Successfully connected and retrieved latest email
        messageId:
          type: string
          description: Message ID of the most recent email retrieved from the inbox. This is a unique identifier assigned by the email server.
          example: <abc123@example.com>
        subject:
          type: string
          description: Subject line of the most recent email retrieved from the inbox.
          example: Test Email Subject
        from:
          type: object
          description: Sender email address(es) of the most recent email. Multiple addresses are comma-separated if present.
          example: sender@example.com
        date:
          type: string
          description: ISO 8601 timestamp indicating when the most recent email was received.
          example: '2024-01-20T10:30:00Z'
          format: date-time
      required:
        - success
        - message
    VatRateResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the VAT rate. Use this ID to update or delete the rate. This ID is generated automatically when the rate is created.
          example: 123e4567-e89b-12d3-a456-426614174000
        vat:
          type: number
          description: The VAT (Value Added Tax) rate percentage currently configured. This is a number between 0 and 100, where 21 represents 21% tax. This is the percentage that will be applied to invoices and quotes created on or after the effective date.
          example: 21
        dateFrom:
          type: string
          description: The effective start date for this VAT rate in ISO 8601 format. This date determines when the rate becomes active. The system uses this date to select the correct VAT rate when creating invoices and quotes. The rate with the most recent dateFrom that is less than or equal to the document creation date is used.
          example: '2024-01-01T00:00:00.000Z'
          format: date-time
        isDefault:
          type: boolean
          description: Indicates whether this VAT rate is marked as the default rate for the workspace. Default rates cannot be deleted and typically serve as a fallback option. When true, this rate is protected from deletion operations.
          example: false
      required:
        - id
        - vat
        - dateFrom
        - isDefault
    CreateVatRateDto:
      type: object
      properties:
        vat:
          type: number
          description: The VAT (Value Added Tax) rate percentage that will be applied to invoices and quotes. This is a number between 0 and 100, where 21 represents 21% tax. The system uses this rate for all documents created on or after the effective date (dateFrom).
          example: 21
          minimum: 0
          maximum: 100
        dateFrom:
          type: string
          description: The effective start date for this VAT rate. This date determines when the rate becomes active. The system automatically applies this rate to all invoices and quotes created on or after this date. You can set a future date to plan ahead for tax law changes. The date must be provided in ISO 8601 format (YYYY-MM-DD). The system validates that this date does not overlap with existing VAT rate periods.
          example: '2024-01-01'
          format: date
        isDefault:
          type: boolean
          description: Whether this VAT rate is the default rate for the workspace. Default rates cannot be deleted and serve as a fallback. If not specified, defaults to false. Only one rate should typically be marked as default, though the system does not enforce this.
          example: false
          default: false
      required:
        - vat
        - dateFrom
    UpdateVatRateDto:
      type: object
      properties:
        vat:
          type: number
          description: The VAT (Value Added Tax) rate percentage that will be applied to invoices and quotes. This is a number between 0 and 100, where 21 represents 21% tax. Updating this value changes the tax percentage for all future documents that use this rate.
          example: 21
          minimum: 0
          maximum: 100
        dateFrom:
          type: string
          description: The effective start date for this VAT rate. This date determines when the rate becomes active. Updating this date changes which invoices and quotes will use this rate. The date must be provided in ISO 8601 format (YYYY-MM-DD). The system validates that the updated date does not overlap with other VAT rate periods.
          example: '2024-01-01'
          format: date
        isDefault:
          type: boolean
          description: Whether this VAT rate is the default rate for the workspace. Default rates cannot be deleted and serve as a fallback. If not specified, defaults to false. Only one rate should typically be marked as default, though the system does not enforce this.
          example: false
          default: false
      required:
        - vat
        - dateFrom
    HelpCenterLocalizedValueDto:
      type: object
      properties:
        de:
          type: string
          example: Aufgabentypen
        en:
          type: string
          example: Task types
    HelpCenterSearchResultDto:
      type: object
      properties:
        slug:
          type: string
          example: administration/task-settings/custom-task-types
          description: Stable documentation slug used to retrieve the article.
        breadcrumb:
          example:
            - administration
            - task-settings
          description: Documentation path segments above this article.
          type: array
          items:
            type: string
        titles:
          $ref: '#/components/schemas/HelpCenterLocalizedValueDto'
        descriptions:
          $ref: '#/components/schemas/HelpCenterLocalizedValueDto'
        relevanceScore:
          type: number
          example: 0.75
          description: Best hybrid search score among localized variants.
        availableLocales:
          type: array
          example:
            - de
            - en
          items:
            type: string
            enum:
              - de
              - en
      required:
        - slug
        - breadcrumb
        - titles
        - descriptions
        - relevanceScore
        - availableLocales
    HelpCenterSearchResponseDto:
      type: object
      properties:
        searchTerm:
          type: string
          example: task types
        totalResults:
          type: number
          example: 2
        results:
          type: array
          items:
            $ref: '#/components/schemas/HelpCenterSearchResultDto'
      required:
        - searchTerm
        - totalResults
        - results
    HelpCenterSiblingDto:
      type: object
      properties:
        slug:
          type: string
          example: administration/task-settings/task-statuses
        title:
          type: string
          example: Task statuses
      required:
        - slug
        - title
    HelpCenterArticleResponseDto:
      type: object
      properties:
        slug:
          type: string
          example: administration/task-settings/custom-task-types
        language:
          type: string
          example: de
          enum:
            - de
            - en
        content:
          type: string
          example: |-
            ---
            title: Benutzerdefinierte Aufgabentypen
            ---

            ...
          description: The complete article Markdown for the selected language.
        fallback:
          type: boolean
          example: false
          description: Whether the requested language was unavailable and the other locale was returned.
        breadcrumb:
          example:
            - administration
            - task-settings
          type: array
          items:
            type: string
        siblings:
          type: array
          items:
            $ref: '#/components/schemas/HelpCenterSiblingDto'
      required:
        - slug
        - language
        - content
        - fallback
        - breadcrumb
        - siblings
    HelpCenterTocArticleDto:
      type: object
      properties:
        slug:
          type: string
          example: administration/task-settings/custom-task-types
        titles:
          $ref: '#/components/schemas/HelpCenterLocalizedValueDto'
        availableLocales:
          type: array
          example:
            - de
            - en
          items:
            type: string
            enum:
              - de
              - en
      required:
        - slug
        - titles
        - availableLocales
    HelpCenterTocGroupDto:
      type: object
      properties:
        breadcrumb:
          example:
            - administration
            - task-settings
          type: array
          items:
            type: string
        articles:
          type: array
          items:
            $ref: '#/components/schemas/HelpCenterTocArticleDto'
      required:
        - breadcrumb
        - articles
    HelpCenterTocResponseDto:
      type: object
      properties:
        rootPath:
          type: string
          example: administration/task-settings
        groups:
          type: array
          items:
            $ref: '#/components/schemas/HelpCenterTocGroupDto'
      required:
        - groups
    QuestionOptionDto:
      type: object
      properties:
        id:
          type: string
          description: Option ID (for updates)
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        title:
          type: string
          description: Option title. For Checkbox questions, users can select multiple options. For Radio questions, users can select only one option.
          example: Option A
        extraHours:
          type: number
          description: Extra hours for this option. When this option is selected (for Checkbox/Radio questions), this value is added to the effort calculation.
          example: 2.5
      required:
        - title
    QuestionConditionDto:
      type: object
      properties:
        id:
          type: string
          description: Condition ID (for updates)
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        type:
          type: string
          description: |-
            Condition type. Conditions control when items or questions are visible/active:
            - **QuestionAnswered/QuestionNotAnswered**: Based on whether a question is answered
            - **QuestionAnswerEqual/QuestionAnswerDoesNotEqual**: Based on specific answer value
            - **QuestionAnswerContains/QuestionAnswerDoesNotContain**: Based on answer containing values
            - **TodoIsDone/TodoIsNotDone**: Based on todo completion status
            - **TestCasePassed/TestCaseFailed**: Based on test case evaluation
          enum:
            - QuestionAnswered
            - QuestionNotAnswered
            - QuestionAnswerContains
            - QuestionAnswerDoesNotContain
            - QuestionAnswerEqual
            - QuestionAnswerDoesNotEqual
            - TodoIsDone
            - TodoIsNotDone
            - TestCasePassed
            - TestCaseFailed
          example: QuestionAnswered
        targetQuestionId:
          type: string
          description: Target question ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetTodoId:
          type: string
          description: Target todo ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetTestCaseId:
          type: string
          description: Target test case ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetValue:
          type: string
          description: Target value
          example: 'Yes'
        containsType:
          type: string
          description: Contains type
          enum:
            - Some
            - All
          example: Some
      required:
        - type
    CreateQuestionDto:
      type: object
      properties:
        id:
          type: string
          description: Question ID (for updates - if provided, updates existing question)
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        title:
          type: string
          description: Question title/heading. Questions capture customer-specific requirements or project details. They make work packages flexible and reusable by allowing later individualization in projects.
          example: What is the expected performance?
        question:
          type: string
          description: The question text that will be displayed to users. This is the actual question being asked (e.g., "Please specify the expected response time").
          example: Please specify the expected response time
        description:
          type: string
          description: Question description (accepts HTML or Markdown, returns HTML)
          example: <p>This question helps us understand performance requirements.</p>
        type:
          type: string
          description: |-
            Question type. Questions can be:
            - **ShortText**: Single-line text input
            - **Editor**: Formatted multi-line text
            - **Checkbox**: Yes/No selection (can impact effort)
            - **Radio**: Single choice from options (can impact effort)
            - **Files**: Upload files
            - **Datepicker**: Select a date
            - **Multiplier**: Dynamic calculation (quantity × duration per element)
            - **Person**: Select person(s) from team
          enum:
            - ShortText
            - Editor
            - Checkbox
            - Radio
            - Files
            - Datepicker
            - Multiplier
            - Person
          example: ShortText
        options:
          description: Question options (required for Checkbox/Radio types, must have at least one option). Each option can have an extraHours value that affects effort calculation when selected.
          type: array
          items:
            $ref: '#/components/schemas/QuestionOptionDto'
        multiplierValue:
          type: number
          description: 'Multiplier value (for Multiplier type). This is the duration per element (e.g., hours per page). When answered, the system calculates: Answer × multiplierValue = Total additional effort.'
          example: 1.5
        conditions:
          description: Question conditions that control visibility/behavior. Conditions can show/hide questions based on answers to other questions, todo completion, or test case status.
          type: array
          items:
            $ref: '#/components/schemas/QuestionConditionDto'
      required:
        - title
        - question
        - type
    UpdateQuestionDto:
      type: object
      properties:
        title:
          type: string
          description: Question title/heading. Questions capture customer-specific requirements or project details.
          example: What is the expected performance?
        question:
          type: string
          description: The question text that will be displayed to users.
          example: Please specify the expected response time
        description:
          type: string
          description: Question description (accepts HTML or Markdown, returns HTML)
          example: <p>This question helps us understand performance requirements.</p>
        type:
          type: string
          description: |-
            Question type. Questions can be:
            - **ShortText**: Single-line text input
            - **Editor**: Formatted multi-line text
            - **Checkbox**: Yes/No selection (can impact effort)
            - **Radio**: Single choice from options (can impact effort)
            - **Files**: Upload files
            - **Datepicker**: Select a date
            - **Multiplier**: Dynamic calculation (quantity × duration per element)
            - **Person**: Select person(s) from team
          enum:
            - ShortText
            - Editor
            - Checkbox
            - Radio
            - Files
            - Datepicker
            - Multiplier
            - Person
        options:
          description: Question options (required for Checkbox/Radio types)
          type: array
          items:
            $ref: '#/components/schemas/QuestionOptionDto'
        multiplierValue:
          type: number
          description: 'Multiplier value (for Multiplier type). This is the duration per element (e.g., hours per page). When answered, the system calculates: Answer × multiplierValue = Total additional effort.'
          example: 1.5
        conditions:
          description: Question conditions that control visibility/behavior. Conditions can show/hide questions based on answers to other questions, todo completion, or test case status.
          type: array
          items:
            $ref: '#/components/schemas/QuestionConditionDto'
    SortQuestionsDto:
      type: object
      properties:
        newSortOrder:
          description: Array of question IDs in new order. All questions for the item must be included in the desired display order.
          example:
            - e57dc37b-7693-4d06-b49c-17084b773aff
            - f68ed48c-8704-5e17-c5ad-28195c884b00
          type: array
          items:
            type: string
      required:
        - newSortOrder
    CreateComponentDto:
      type: object
      properties:
        name:
          type: string
          description: Component name
          example: User Authentication Module
        icon:
          type: object
          description: 'Icon identifier. Format: :icon_name: (e.g., :rocket:, :box:, :star:)'
          example: ':box:'
          nullable: true
        description:
          type: string
          description: Description (HTML or Markdown)
          example: <p>This module handles user authentication and authorization.</p>
        tags:
          description: Tag UUIDs
          example:
            - 550e8400-e29b-41d4-a716-446655440000
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note (HTML or Markdown, optional)
          example: <p>Internal implementation notes.</p>
      required:
        - name
        - description
    SaveComponentItemConditionDto:
      type: object
      properties:
        id:
          type: string
          description: Condition ID (optional, for updates)
          example: 550e8400-e29b-41d4-a716-446655440013
          nullable: true
        type:
          type: string
          description: Condition type
          enum:
            - QuestionAnswered
            - QuestionNotAnswered
            - QuestionAnswerContains
            - QuestionAnswerDoesNotContain
            - QuestionAnswerEqual
            - QuestionAnswerDoesNotEqual
            - TodoIsDone
            - TodoIsNotDone
            - TestCasePassed
            - TestCaseFailed
          example: QuestionAnswered
        targetQuestionId:
          type: string
          description: Target question ID (optional)
          example: 550e8400-e29b-41d4-a716-446655440014
          nullable: true
        targetTodoId:
          type: string
          description: Target todo ID (optional)
          example: 550e8400-e29b-41d4-a716-446655440015
          nullable: true
        targetTestCaseId:
          type: string
          description: Target test case ID (optional)
          example: 550e8400-e29b-41d4-a716-446655440016
          nullable: true
        targetValue:
          type: string
          description: Target value (optional)
          example: option-value
          nullable: true
        containsType:
          type: string
          description: Contains type
          enum:
            - Some
            - All
          example: Some
      required:
        - type
        - containsType
    CreateLibraryComponentItemDto:
      type: object
      properties:
        componentId:
          type: string
          description: Parent component ID
          example: 550e8400-e29b-41d4-a716-446655440000
        parentId:
          type: string
          description: Parent item ID (optional, for nested items)
          example: null
          nullable: true
        type:
          type: string
          description: Type of component item
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
          example: Epic
        name:
          type: string
          description: Name of the component item
          example: User Authentication Epic
        icon:
          type: object
          description: Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:check:`, `:star:`). Optional.
          example: ':check:'
          nullable: true
        timeFrame:
          type: number
          description: Time frame in hours (required for all types except Epic)
          example: 20
        description:
          type: string
          description: Description in HTML or Markdown format (will be converted to IDoc)
          example: <p>Epic description in HTML</p>
        tags:
          description: Array of tag UUIDs (optional)
          example:
            - 550e8400-e29b-41d4-a716-446655440020
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note in HTML or Markdown format (optional, will be converted to IDoc)
          example: <p>Internal implementation notes</p>
          nullable: true
        includeInSpecification:
          type: boolean
          description: Whether to include in specification
          example: true
        conditions:
          description: Array of conditions (optional)
          type: array
          items:
            $ref: '#/components/schemas/SaveComponentItemConditionDto'
      required:
        - componentId
        - type
        - name
        - description
        - includeInSpecification
    CreateLibraryComponentItemResponseDto:
      type: object
      properties:
        success:
          type: boolean
          description: Success status
          example: true
        id:
          type: string
          description: Created item ID
          example: 550e8400-e29b-41d4-a716-446655440001
      required:
        - success
        - id
    ComponentTreeComponentDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the component
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Name of the component
          example: User Authentication Module
        icon:
          type: object
          description: 'Icon identifier for the component (nullable). Format: :icon_name: (e.g., :rocket:, :box:, :star:)'
          example: ':box:'
          nullable: true
        totalTimeFrame:
          type: number
          description: Total time frame for the component
          example: 40
      required:
        - id
        - name
        - icon
        - totalTimeFrame
    ComponentTreeItemDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the component item
          example: 550e8400-e29b-41d4-a716-446655440001
        name:
          type: string
          description: Name of the component item
          example: Login Feature
        icon:
          type: object
          description: 'Icon identifier for the component item (nullable). Format: :icon_name: (e.g., :rocket:, :check:, :star:)'
          example: ':check:'
          nullable: true
        type:
          type: string
          description: |-
            Type of the component item. Items are the building blocks within components:
            - **Epic**: Groups related work packages around a common theme
            - **WorkPackage**: Self-contained task that delivers a tangible result
            - **TodoList**: Checklist of sub-steps or checkpoints
            - **TestSuite**: Formal acceptance tests with test cases
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
          example: Epic
        componentId:
          type: string
          description: ID of the parent component
          example: 550e8400-e29b-41d4-a716-446655440000
        parentId:
          type: object
          description: ID of the parent item (nullable for top-level items)
          example: null
          nullable: true
        conditionsValid:
          type: boolean
          description: Whether all conditions are valid for this item
          example: true
        failedConditionsIds:
          description: Array of failed condition IDs
          example: []
          type: array
          items:
            type: string
        totalEstimate:
          type: number
          description: Total time estimate for the item
          example: 20
      required:
        - id
        - name
        - icon
        - type
        - componentId
        - parentId
        - conditionsValid
        - failedConditionsIds
        - totalEstimate
    ComponentLibraryTreeResponseDto:
      type: object
      properties:
        components:
          description: List of components in the library
          type: array
          items:
            $ref: '#/components/schemas/ComponentTreeComponentDto'
        items:
          description: List of items (Epics, Work Packages, Todo Lists, Test Suites) in the library. Items are the building blocks within components and can be nested to any depth.
          type: array
          items:
            $ref: '#/components/schemas/ComponentTreeItemDto'
      required:
        - components
        - items
    ComponentChildrenShortItemDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier
          example: 550e8400-e29b-41d4-a716-446655440001
        name:
          type: string
          description: Name of the item
          example: Login Feature
        icon:
          type: object
          description: Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:check:`, `:star:`). Can be null if no icon is set.
          example: ':check:'
          nullable: true
        type:
          type: string
          description: |-
            Type of the component item. Items are the building blocks within components:
            - **Epic**: Groups related work packages around a common theme
            - **WorkPackage**: Self-contained task that delivers a tangible result
            - **TodoList**: Checklist of sub-steps or checkpoints
            - **TestSuite**: Formal acceptance tests with test cases
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
          example: Epic
        conditionsValid:
          type: boolean
          description: Whether all conditions are valid
          example: true
        entityUniqueId:
          type: string
          description: Entity unique ID
          example: unique-id-123
        taskId:
          type: object
          description: Task ID if linked to a task
          example: 550e8400-e29b-41d4-a716-446655440002
          nullable: true
        taskShortNumber:
          type: object
          description: Task short number if linked to a task
          example: 123
          nullable: true
        testCasesCount:
          type: number
          description: Count of test cases
          example: 3
        todosCount:
          type: number
          description: Count of todo items
          example: 5
        questionsCount:
          type: number
          description: Count of questions
          example: 2
        children:
          description: Nested children items
          type: array
          items:
            $ref: '#/components/schemas/ComponentChildrenShortItemDto'
      required:
        - id
        - name
        - icon
        - type
        - conditionsValid
        - entityUniqueId
        - taskId
        - taskShortNumber
        - testCasesCount
        - todosCount
        - questionsCount
        - children
    ComponentDetailsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the component
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Name of the component
          example: User Authentication Module
        projectId:
          type: object
          description: Project ID (null for library components)
          example: null
          nullable: true
        icon:
          type: object
          description: Icon identifier (nullable)
          example: faBox
          nullable: true
        description:
          type: string
          description: Component description in HTML format
          example: <p>This module handles user authentication and authorization.</p>
        tags:
          description: Array of tags
          example:
            - authentication
            - security
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note in HTML format
          example: <p>Internal implementation notes.</p>
        children:
          description: Array of direct children items
          type: array
          items:
            $ref: '#/components/schemas/ComponentChildrenShortItemDto'
        discountAmount:
          type: number
          description: Discount amount applied to this component. 0 means no discount.
          example: 10
        discountType:
          type: string
          description: 'Type of discount: "Fixed" (fixed amount) or "Percentage" (percentage of total price)'
          enum:
            - Fixed
            - Percentage
          example: Percentage
      required:
        - id
        - name
        - projectId
        - icon
        - description
        - tags
        - internalNote
        - children
        - discountAmount
        - discountType
    PatchComponentDto:
      type: object
      properties:
        name:
          type: string
          description: Component name
          example: User Authentication Module
        description:
          type: string
          description: Component description (HTML or Markdown)
          example: <p>This component handles user authentication and authorization.</p>
        icon:
          type: object
          description: 'Icon identifier (can be set to null). Format: :icon_name: (e.g., :rocket:, :box:, :star:)'
          example: ':box:'
          nullable: true
        tags:
          description: Array of tag IDs
          example:
            - 550e8400-e29b-41d4-a716-446655440000
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note (HTML or Markdown)
          example: <p>Implementation notes for the development team.</p>
    ComponentTestCaseDto:
      type: object
      properties:
        id:
          type: string
          description: Test case ID
          example: 550e8400-e29b-41d4-a716-446655440010
        title:
          type: string
          description: Test case title
          example: Test login with valid credentials
        description:
          type: string
          description: Test case description in HTML format
          example: <p>Verify that users can log in with valid credentials.</p>
        steps:
          type: string
          description: Test steps in HTML format
          example: <ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>
        expectedResult:
          type: string
          description: Expected result in HTML format
          example: <p>User is successfully logged in and redirected to dashboard.</p>
        testComment:
          type: string
          description: Test comment in HTML format (optional)
          example: <p>Test passed successfully.</p>
          nullable: true
        testStatus:
          type: string
          description: Test status (optional)
          enum:
            - Passed
            - PassedWithReservations
            - Failed
          example: Passed
          nullable: true
        testedBy:
          type: string
          description: User ID who tested (optional)
          example: 550e8400-e29b-41d4-a716-446655440020
          nullable: true
        testedAt:
          type: string
          description: Date when tested (optional)
          example: '2024-01-01T00:00:00.000Z'
          nullable: true
      required:
        - id
        - title
        - description
        - steps
        - expectedResult
    ComponentTodoItemDto:
      type: object
      properties:
        id:
          type: string
          description: Todo item ID
          example: 550e8400-e29b-41d4-a716-446655440011
        title:
          type: string
          description: Todo item title. Todos are checklist items used to track actionable tasks within component items. They are perfect for recurring processes, quality control checkpoints, and project preparation steps.
          example: Implement password reset
        description:
          type: string
          description: Todo item description in HTML format
          example: <p>Add password reset functionality to the authentication module.</p>
        isDone:
          type: boolean
          description: Whether the todo is marked as done
          example: false
        doneBy:
          type: object
          description: User ID who marked as done (nullable)
          example: 550e8400-e29b-41d4-a716-446655440020
          nullable: true
        doneUpdatedAt:
          type: object
          description: Date when marked as done (nullable)
          example: '2024-01-01T00:00:00.000Z'
          nullable: true
        todoComment:
          type: object
          description: Todo comment in HTML format (nullable)
          example: <p>Completed ahead of schedule.</p>
          nullable: true
      required:
        - id
        - title
        - description
        - isDone
        - doneBy
        - doneUpdatedAt
        - todoComment
    ComponentQuestionOptionDto:
      type: object
      properties:
        id:
          type: string
          description: Option ID
          example: 550e8400-e29b-41d4-a716-446655440012
        title:
          type: string
          description: Option title
          example: Two-factor authentication
        extraHours:
          type: number
          description: Extra hours for this option (optional)
          example: 8
          nullable: true
        isSelected:
          type: boolean
          description: Whether this option is selected (optional)
          example: true
          nullable: true
      required:
        - id
        - title
    ComponentQuestionDto:
      type: object
      properties:
        id:
          type: string
          description: Question ID
          example: 550e8400-e29b-41d4-a716-446655440017
        title:
          type: string
          description: Question title
          example: Security Features
        question:
          type: string
          description: Question text
          example: Which security features should be implemented?
        description:
          type: string
          description: Question description in HTML format
          example: <p>Select all security features that apply.</p>
        type:
          type: string
          description: Question type
          enum:
            - ShortText
            - Editor
            - Checkbox
            - Radio
            - Files
            - Datepicker
            - Multiplier
            - Person
          example: Checkbox
        options:
          description: Available options
          type: array
          items:
            $ref: '#/components/schemas/ComponentQuestionOptionDto'
        editorAnswer:
          type: string
          description: Editor answer in HTML format (optional)
          example: <p>Two-factor authentication is required.</p>
          nullable: true
        isAnswered:
          type: boolean
          description: Whether the question has been answered
          example: true
        answeredBy:
          type: string
          description: User ID who answered (optional)
          example: 550e8400-e29b-41d4-a716-446655440020
          nullable: true
        answeredAt:
          type: string
          description: Date when answered (optional)
          example: '2024-01-01T00:00:00.000Z'
          nullable: true
      required:
        - id
        - title
        - question
        - description
        - type
        - options
        - isAnswered
    ComponentItemConditionDto:
      type: object
      properties:
        id:
          type: string
          description: Condition ID (optional)
          example: 550e8400-e29b-41d4-a716-446655440013
          nullable: true
        type:
          type: string
          description: Condition type
          enum:
            - QuestionAnswered
            - QuestionNotAnswered
            - QuestionAnswerContains
            - QuestionAnswerDoesNotContain
            - QuestionAnswerEqual
            - QuestionAnswerDoesNotEqual
            - TodoIsDone
            - TodoIsNotDone
            - TestCasePassed
            - TestCaseFailed
          example: QuestionAnswered
        targetQuestionId:
          type: string
          description: Target question ID (optional)
          example: 550e8400-e29b-41d4-a716-446655440014
          nullable: true
        targetTodoId:
          type: string
          description: Target todo ID (optional)
          example: 550e8400-e29b-41d4-a716-446655440015
          nullable: true
        targetTestCaseId:
          type: string
          description: Target test case ID (optional)
          example: 550e8400-e29b-41d4-a716-446655440016
          nullable: true
        targetValue:
          type: string
          description: Target value (optional)
          example: option-value
          nullable: true
        containsType:
          type: string
          description: Contains type
          enum:
            - Some
            - All
          example: Some
      required:
        - type
        - containsType
    ComponentItemDetailsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Item ID
          example: 550e8400-e29b-41d4-a716-446655440001
        entityUniqueId:
          type: string
          description: Entity unique ID
          example: unique-id-123
        name:
          type: string
          description: Item name
          example: Login Feature
        icon:
          type: object
          description: Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:check:`, `:star:`). Can be null if no icon is set.
          example: ':check:'
          nullable: true
        description:
          type: string
          description: Item description in HTML format
          example: <p>Feature for user authentication.</p>
        tags:
          description: Array of tags
          example:
            - authentication
            - login
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note in HTML format
          example: <p>Implementation notes.</p>
        includeInSpecification:
          type: boolean
          description: Whether to include in specification
          example: true
        componentId:
          type: string
          description: Parent component ID
          example: 550e8400-e29b-41d4-a716-446655440000
        parentId:
          type: object
          description: Parent item ID (nullable for top-level items)
          example: null
          nullable: true
        type:
          type: string
          description: Item type
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
          example: Epic
        testCases:
          description: Test cases associated with this item. Test cases are individual test steps within a Test Suite that document formal review and approval criteria.
          type: array
          items:
            $ref: '#/components/schemas/ComponentTestCaseDto'
        todoItems:
          description: Todo items associated with this item. Todos are checklist items that can be checked off individually and support comments.
          type: array
          items:
            $ref: '#/components/schemas/ComponentTodoItemDto'
        questions:
          description: Questions associated with this item. Questions capture customer requirements and can affect effort calculation. In library components, questions are defined but not answered.
          type: array
          items:
            $ref: '#/components/schemas/ComponentQuestionDto'
        conditions:
          description: Conditions for this item
          type: array
          items:
            $ref: '#/components/schemas/ComponentItemConditionDto'
        conditionsValid:
          type: boolean
          description: Whether all conditions are valid
          example: true
        failedConditionsIds:
          description: Array of failed condition IDs
          example: []
          type: array
          items:
            type: string
        timeFrame:
          type: number
          description: Time frame in hours
          example: 40
        children:
          description: Nested children items
          type: array
          items:
            $ref: '#/components/schemas/ComponentChildrenShortItemDto'
        taskId:
          type: object
          description: Task ID if linked to a task
          example: 550e8400-e29b-41d4-a716-446655440002
          nullable: true
        taskShortNumber:
          type: object
          description: Task short number if linked to a task
          example: 123
          nullable: true
      required:
        - id
        - entityUniqueId
        - name
        - icon
        - description
        - tags
        - internalNote
        - includeInSpecification
        - componentId
        - parentId
        - type
        - testCases
        - todoItems
        - questions
        - conditions
        - conditionsValid
        - failedConditionsIds
        - timeFrame
        - children
        - taskId
        - taskShortNumber
    CreateComponentTodoDto:
      type: object
      properties:
        title:
          type: string
          description: Todo title
          example: Implement authentication
        description:
          type: string
          description: Todo description in HTML or Markdown format
          example: <p>Add user login functionality</p>
      required:
        - title
        - description
    TodoOperationResponseDto:
      type: object
      properties:
        success:
          type: boolean
          description: Operation success status
          example: true
        id:
          type: string
          description: ID of the updated todo
          example: 550e8400-e29b-41d4-a716-446655440001
      required:
        - success
    UpdateComponentTodoDto:
      type: object
      properties:
        title:
          type: string
          description: Todo title
          example: Implement authentication with 2FA
        description:
          type: string
          description: Todo description in HTML or Markdown format
          example: <p>Add user login with two-factor authentication</p>
    SortComponentTodosDto:
      type: object
      properties:
        newSortOrder:
          description: Array of todo IDs in the new sort order
          example:
            - 550e8400-e29b-41d4-a716-446655440001
            - 550e8400-e29b-41d4-a716-446655440002
          type: array
          items:
            type: string
      required:
        - newSortOrder
    CreateComponentTestCaseDto:
      type: object
      properties:
        title:
          type: string
          description: Test case title. Test cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results.
          example: Test login with valid credentials
        description:
          type: string
          description: Test case description in HTML or Markdown format
          example: <p>Verify that users can log in with valid credentials.</p>
        steps:
          type: string
          description: Test steps in HTML or Markdown format
          example: <ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>
        expectedResult:
          type: string
          description: Expected result in HTML or Markdown format
          example: <p>User is successfully logged in and redirected to dashboard.</p>
      required:
        - title
        - description
        - steps
        - expectedResult
    ComponentTestCaseResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Test case ID
          example: 550e8400-e29b-41d4-a716-446655440010
        title:
          type: string
          description: Test case title
          example: Test login with valid credentials
        description:
          type: string
          description: Test case description in HTML format
          example: <p>Verify that users can log in with valid credentials.</p>
        steps:
          type: string
          description: Test steps in HTML format
          example: <ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>
        expectedResult:
          type: string
          description: Expected result in HTML format
          example: <p>User is successfully logged in and redirected to dashboard.</p>
        sort:
          type: number
          description: Sort order
          example: 1
        testComment:
          type: string
          description: Test comment in HTML format (optional)
          example: <p>Test passed successfully.</p>
          nullable: true
        testStatus:
          type: string
          description: Test status (optional)
          enum:
            - Passed
            - PassedWithReservations
            - Failed
          example: Passed
          nullable: true
        testedBy:
          type: string
          description: User ID who tested (optional)
          example: 550e8400-e29b-41d4-a716-446655440020
          nullable: true
        testedAt:
          type: string
          description: Date when tested (optional)
          example: '2024-01-01T00:00:00.000Z'
          nullable: true
      required:
        - id
        - title
        - description
        - steps
        - expectedResult
        - sort
    UpdateComponentTestCaseDto:
      type: object
      properties:
        title:
          type: string
          description: Test case title
          example: Test login with valid credentials
        description:
          type: string
          description: Test case description in HTML or Markdown format
          example: <p>Verify that users can log in with valid credentials.</p>
        steps:
          type: string
          description: Test steps in HTML or Markdown format
          example: <ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>
        expectedResult:
          type: string
          description: Expected result in HTML or Markdown format
          example: <p>User is successfully logged in and redirected to dashboard.</p>
    SortComponentTestCasesDto:
      type: object
      properties:
        newSortOrder:
          description: Array of testcase IDs in desired order
          example:
            - 550e8400-e29b-41d4-a716-446655440010
            - 550e8400-e29b-41d4-a716-446655440011
            - 550e8400-e29b-41d4-a716-446655440012
          type: array
          items:
            type: string
      required:
        - newSortOrder
    PatchLibraryComponentItemDto:
      type: object
      properties:
        componentId:
          type: string
          description: ID of the component this item belongs to
          example: 550e8400-e29b-41d4-a716-446655440000
        parentId:
          type: string
          description: ID of parent item for nested items
          example: 550e8400-e29b-41d4-a716-446655440000
        type:
          type: string
          description: Type of item (Epic, WorkPackage, TodoList, TestSuite)
          example: WorkPackage
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
        name:
          type: string
          description: Name of the item
          example: User Authentication Feature
        icon:
          type: object
          description: 'Icon identifier (can be set to null). Format: :icon_name: (e.g., :rocket:, :check:, :star:)'
          example: ':check:'
          nullable: true
        timeFrame:
          type: number
          description: Time frame in hours. Required for all types except Epic (when type is provided)
          example: 40
        description:
          type: string
          description: Description in HTML or Markdown format (will be converted to IDoc if provided)
          example: <p>Implement user authentication and authorization</p>
        tags:
          description: Array of tag UUIDs
          example:
            - 550e8400-e29b-41d4-a716-446655440000
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note in HTML or Markdown format (will be converted to IDoc if provided)
          example: <p>Implementation notes for the development team</p>
        includeInSpecification:
          type: boolean
          description: Whether to include in specification
          example: true
        conditions:
          description: Array of conditions (full array replaces existing conditions)
          type: array
          items:
            $ref: '#/components/schemas/SaveComponentItemConditionDto'
    DeleteComponentItemResponseDto:
      type: object
      properties:
        success:
          type: boolean
          description: Success status
          example: true
      required:
        - success
    SaveComponentsSortDto:
      type: object
      properties: {}
    DuplicateComponentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the newly created duplicate component
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
      required:
        - id
    TagResponseDto:
      type: object
      properties:
        id:
          type: string
          description: The unique identifier (UUID) of the tag. Use this ID when updating or deleting the tag.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: The display name of the tag. This is what users see when the tag is applied to tasks, projects, or components.
          example: urgent
        color:
          type: string
          description: |-
            The predefined color code assigned to the tag for visual organization. Null if no color has been set. Colors help users quickly identify tags in lists and cards.

            **Available color values:**
            - `default` - Light gray (default color)
            - `red` - Red
            - `yellow` - Yellow
            - `green` - Green
            - `blue` - Blue
            - `purple` - Purple
            - `fuchsia` - Fuchsia
            - `pink` - Pink
            - `magenta` - Magenta
          enum:
            - default
            - red
            - yellow
            - green
            - blue
            - purple
            - fuchsia
            - pink
            - magenta
            - null
          example: blue
          nullable: true
        isDeleted:
          type: boolean
          description: Indicates whether the tag has been soft-deleted. Soft-deleted tags are marked as deleted but not permanently removed from the database. They will not appear in list endpoints and are automatically removed from all associated entities.
          example: false
      required:
        - id
        - name
        - color
        - isDeleted
    CreateTagDto:
      type: object
      properties:
        name:
          type: string
          description: The name of the tag to create. Must be unique within the tag type (case-insensitive). If a tag with this name already exists, the existing tag will be returned instead of creating a duplicate. Use descriptive names that clearly indicate the topic or process (e.g., "Billing", "Support", "Onboarding").
          example: urgent
      required:
        - name
    SaveTagDto:
      type: object
      properties:
        id:
          type: string
          description: The unique identifier (UUID) of the tag to update. This identifies which existing tag you want to modify.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: The new name for the tag. Must be unique within the tag type (case-insensitive). If you change the name, all entities using this tag will automatically reflect the new name.
          example: urgent
        color:
          type: string
          description: |-
            Optional predefined color code for visual organization of the tag. Must be one of the predefined color values from the palette. Colors help visually distinguish tags in lists and cards. If not provided, the tag will have no color.

            **Available color values:**
            - `default` - Light gray (default color)
            - `red` - Red
            - `yellow` - Yellow
            - `green` - Green
            - `blue` - Blue
            - `purple` - Purple
            - `fuchsia` - Fuchsia
            - `pink` - Pink
            - `magenta` - Magenta
          enum:
            - default
            - red
            - yellow
            - green
            - blue
            - purple
            - fuchsia
            - pink
            - magenta
          example: blue
      required:
        - id
        - name
    AttachTaskFormInstanceDto:
      type: object
      properties:
        templateId:
          type: string
          format: uuid
          description: 'ID of the form template to attach. List templates (each includes `id`) via GET `/workspace/form-templates` or GET `/administration/form-templates-available`. Optional: inspect field definitions before attach with GET `/workspace/form-templates/{id}`.'
      required:
        - templateId
    PatchTaskFormInstanceDto:
      type: object
      properties:
        values:
          type: object
          additionalProperties: true
          description: Map of field keys to submitted values. Each key must match the `valueKey` of a field on this instance (see GET `/tasks/{identifier}/form-instances` → `fields[].valueKey`). `valueKey` is the template field `key` when set, otherwise the field UUID. To learn types, required flags, and allowed options before editing, use GET `/workspace/form-templates/{templateId}` (use `templateId` from the instance) or read `fields` from the list response.
      required:
        - values
    ProductSettingOptionDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the product option. This identifies which option (e.g., "Color", "Size", "Support Level") is being configured.
          example: d8d9f2c3-5e8a-4f6b-9c7d-3e4f5a6b7c8d
        value:
          description: Array of UUIDs representing the selected option values. For example, if the option is "Color", this array might contain IDs for "Blue" and "Red" if multiple selections are allowed. Each value ID must be a valid UUID.
          example:
            - 550e8400-e29b-41d4-a716-446655440005
          type: array
          items:
            type: string
      required:
        - id
        - value
    ImportProductToTaskDto:
      type: object
      properties:
        productId:
          type: string
          description: UUID of the product from your workspace product catalog. The product must exist in the catalog and not be already assigned to a task or project. This product will be copied into the task.
          example: 550e8400-e29b-41d4-a716-446655440002
        quantity:
          type: number
          description: Number of units of this product to add to the task. Must be at least 1. For example, if importing a "Workshop" product, quantity 2 means two workshops.
          example: 2
          minimum: 1
        activeVariantId:
          type: object
          description: UUID of the variant to activate when importing. Optional - only needed if the product has multiple variants (e.g., Standard, Pro, Enterprise). If not provided, the first variant will be active by default.
          example: 550e8400-e29b-41d4-a716-446655440003
          nullable: true
        options:
          description: Array of option selections for this product. Each option specifies which values are selected. For example, if a product has a "Support Level" option with values "Basic" and "Premium", you would include the option ID and select one or more value IDs. Required even if empty array.
          example:
            - id: 550e8400-e29b-41d4-a716-446655440004
              value:
                - 550e8400-e29b-41d4-a716-446655440005
          type: array
          items:
            $ref: '#/components/schemas/ProductSettingOptionDto'
        activeFromType:
          type: string
          description: Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.
          enum:
            - Immediately
            - ProjectBilling
            - FixedDate
          example: Immediately
        activeFromFixedDate:
          type: string
          description: 'Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.'
          example: '2024-01-01'
          nullable: true
          format: date
        customPrices:
          type: object
          description: 'Custom price overrides for base product, variants, and options. Keys can be "base", "variant_<variantId>", or "option_<optionValueId>". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving. Note: Tasks only support priceFixed, so priceSubscription and pricePerUnit are ignored.'
          additionalProperties:
            $ref: '#/components/schemas/CustomPriceDto'
          example:
            base:
              priceFixed: 100.5
              priceSubscription: null
              pricePerUnit: null
            variant_550e8400:
              priceFixed: 150
              priceSubscription: null
              pricePerUnit: null
      required:
        - productId
        - quantity
        - options
        - customPrices
    TaskProductVariantDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the variant. Required when updating an existing variant. Omit or leave undefined when creating a new variant.
          example: 550e8400-e29b-41d4-a716-446655440006
        name:
          type: string
          description: 'Display name of the variant. Examples: "Standard", "Pro", "Enterprise", "Basic Package", "Premium Package".'
          example: Premium
        description:
          type: object
          description: Detailed description of the variant in HTML or Markdown format. This will be automatically converted to the internal document format. Can describe features, specifications, or what is included in this variant.
          example: <p>Premium variant with additional features</p>
        priceFixed:
          type: object
          description: One-time fixed price for this variant. Task products only support fixed pricing. Set to null if this pricing model does not apply. Must be 0 or greater.
          example: 150
          nullable: true
      required:
        - name
    TaskProductOptionValueDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the option value. Required when updating an existing value. Omit or leave undefined when creating a new value.
          example: 550e8400-e29b-41d4-a716-446655440008
        name:
          type: string
          description: 'Display name of the option value. Examples: "Blue", "Red", "Premium Support", "Add Workshop", "Extra Storage".'
          example: Blue
        extraPriceFixed:
          type: object
          description: Additional fixed price charged when this option value is selected. Task products only support fixed pricing. For example, if selecting "Premium Support" adds 50 EUR one-time fee, set this to 50.0. Set to null if no extra fixed price applies. Must be 0 or greater.
          example: 10
          nullable: true
      required:
        - name
    TaskProductOptionDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the product option. Required when updating an existing option. Omit or leave undefined when creating a new option.
          example: 550e8400-e29b-41d4-a716-446655440007
        name:
          type: string
          description: 'Display name of the option. Examples: "Color", "Size", "Support Level", "Additional Services". This identifies what type of choice the customer is making.'
          example: Color
        isRequired:
          type: boolean
          description: Whether this option must be selected. If true, customers must choose at least one value from this option. If false, the option is optional.
          example: true
        isMultiple:
          type: boolean
          description: Whether multiple values can be selected for this option. If true, customers can select multiple values (e.g., multiple colors). If false, only one value can be selected.
          example: false
        values:
          description: 'Array of available values for this option. Each value can have its own name and fixed pricing. Task products only support fixed pricing. For example, a "Support Level" option might have values: "Basic" (no extra cost), "Premium" (+50 EUR one-time), "Enterprise" (+200 EUR one-time).'
          type: array
          items:
            $ref: '#/components/schemas/TaskProductOptionValueDto'
      required:
        - name
        - isRequired
        - isMultiple
        - values
    PatchTaskProductDto:
      type: object
      properties:
        name:
          type: string
          description: Display name of the product. This is what appears in quotes, invoices, and task views. All fields in this DTO are optional - only provide fields you want to update.
          example: Updated Product
        description:
          type: string
          description: Product description in HTML or Markdown format. This will be automatically converted to the internal document format. Can include details about what the product includes, features, or specifications.
          example: <p>Updated product description</p>
        categoryId:
          type: object
          description: UUID of the product category. Categories help organize products (e.g., "Hardware", "Software", "Services"). Set to null to remove category assignment.
          example: 550e8400-e29b-41d4-a716-446655440010
          nullable: true
        logoId:
          type: object
          description: UUID of the logo image file. This image is displayed alongside the product in lists and views. Set to null to remove the logo.
          example: 550e8400-e29b-41d4-a716-446655440011
          nullable: true
        priceFixed:
          type: object
          description: One-time fixed price for the product. Task products only support fixed pricing. Set to null if this pricing model does not apply. Must be 0 or greater.
          example: 100
          nullable: true
        variants:
          description: Array of product variants. Variants allow different configurations or service levels (e.g., Standard, Pro, Enterprise). When updating, you must provide the complete variant structure - existing variants will be replaced. Task products only support fixed pricing.
          type: array
          items:
            $ref: '#/components/schemas/TaskProductVariantDto'
        options:
          description: Array of product options. Options allow customers to add extra services or upgrades (e.g., "Add workshop", "Enable premium support"). When updating, you must provide the complete options structure - existing options will be replaced. Task products only support fixed pricing.
          type: array
          items:
            $ref: '#/components/schemas/TaskProductOptionDto'
    TaskProductSettingsDto:
      type: object
      properties:
        quantity:
          type: number
          description: Number of units of this product. Must be at least 1. This affects the total price calculation when using per-unit pricing.
          example: 3
          minimum: 1
        activeVariantId:
          type: object
          description: UUID of the variant that should be active. Use this to switch between variants (e.g., from Standard to Pro). Set to null if the product has no variants or to deactivate variant selection.
          example: 550e8400-e29b-41d4-a716-446655440006
          nullable: true
        options:
          description: Array of option selections. Each entry specifies which option is being configured and which values are selected. You must provide selections for all options defined on the product, even if no values are selected (use empty array for value). This updates the product configuration without changing product details like name or prices.
          example:
            - id: 550e8400-e29b-41d4-a716-446655440004
              value:
                - 550e8400-e29b-41d4-a716-446655440007
          type: array
          items:
            $ref: '#/components/schemas/ProductSettingOptionDto'
        activeFromType:
          type: string
          description: Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.
          enum:
            - Immediately
            - ProjectBilling
            - FixedDate
          example: Immediately
        activeFromFixedDate:
          type: string
          description: 'Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.'
          example: '2024-01-01'
          nullable: true
          format: date
        customPrices:
          type: object
          description: 'Custom price overrides for base product, variants, and options. Keys can be "base", "variant_<variantId>", or "option_<optionValueId>". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving. Note: Tasks only support priceFixed, so priceSubscription and pricePerUnit are ignored.'
          additionalProperties:
            $ref: '#/components/schemas/CustomPriceDto'
          example:
            base:
              priceFixed: 100.5
              priceSubscription: null
              pricePerUnit: null
            variant_550e8400:
              priceFixed: 150
              priceSubscription: null
              pricePerUnit: null
      required:
        - quantity
        - options
        - customPrices
    CreateExpressQuotationDto:
      type: object
      properties:
        contactUserId:
          type: string
          description: UUID of the contact user who will receive the quotation. This user must exist in the workspace and will be the recipient when the quotation is sent via email.
          example: 550e8400-e29b-41d4-a716-446655440000
        language:
          type: string
          description: Language code for the quotation (e.g., "en" for English, "de" for German). Used for formatting dates, numbers, and generating localized item descriptions. If not provided, the workspace default language is used.
          example: en
        comment:
          type: string
          description: Optional comment or notes for the quotation. Can be provided in HTML or Markdown format. The system will automatically detect the format and convert it to the internal IDoc format. This comment appears on the quotation document when sent to the contact.
          example: <p>Please review this quotation and let us know if you have any questions.</p>
        includeEstimate:
          type: boolean
          description: If true, includes the task's estimated work time as the first quotation item. The estimated time is converted to an item with the task's hour rate. If false, only products from the task are included as items.
          example: true
      required:
        - contactUserId
        - includeEstimate
    ExpressQuotationItemOptionResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the option (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        title:
          type: string
          description: 'Display name or title of the option variant (e.g., "Color: Red", "Size: Large", "Material: Premium"). Options represent different variants or configurations of a quotation item.'
          example: 'Color: Red'
        unitPrice:
          type: number
          description: Price per unit for this option variant. This is the additional cost per unit when this option is selected.
          example: 5.99
        totalPrice:
          type: number
          description: Total price for this option variant, calculated as `unitPrice * itemQuantity`. This represents the total additional cost when this option is selected for the item.
          example: 59.9
      required:
        - id
        - title
        - unitPrice
        - totalPrice
    ExpressQuotationItemResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the quotation item (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        title:
          type: string
          description: 'Display title of the item. For time-based items, this typically includes the hours and rate (e.g., "Work time: 1440 hours at 100 EUR each"). For product items, this is the product name.'
          example: 'Work time: 1440 hours at 100 EUR each'
        description:
          type: string
          description: Detailed description of the item in HTML format. For time-based items, this may include the task summary. For product items, this is the product description. The content is converted from the internal IDoc format to HTML.
          example: <p>Development and implementation of the new feature as described in the task requirements.</p>
        unit:
          type: string
          description: Unit of measurement for this item. Can be "Hour" for time-based billing or "Piece" for product-based items.
          enum:
            - Piece
            - Hour
          example: Hour
        quantity:
          type: number
          description: Quantity of units for this item. For time-based items, this is the number of hours. For product items, this is the product quantity.
          example: 1440
        unitPrice:
          type: number
          description: Price per unit for this item. For time-based items, this is the hourly rate. For product items, this is the product unit price.
          example: 100
        totalPrice:
          type: number
          description: Total price for this item, calculated as `quantity * unitPrice`. This does not include option prices, which are added separately.
          example: 144000
        options:
          description: Array of option variants available for this item. Options represent different configurations or variants (e.g., color, size, material) that can be selected, each with its own pricing. An empty array means no options are available.
          type: array
          items:
            $ref: '#/components/schemas/ExpressQuotationItemOptionResponseDto'
      required:
        - id
        - title
        - description
        - unit
        - quantity
        - unitPrice
        - totalPrice
        - options
    ExpressQuotationResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the quotation (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        taskId:
          type: string
          description: UUID of the task this quotation is linked to. The quotation affects billing for this task when accepted.
          example: 550e8400-e29b-41d4-a716-446655440000
        shortNumber:
          type: number
          description: Sequential quotation number within the task. The first quotation for a task is 1, the second is 2, etc. This provides a human-readable reference number for the quotation.
          example: 1
        status:
          type: string
          description: Current status of the quotation. "Pending" means it has been created but not yet accepted or rejected. "Accepted" means it will be used for billing. "Rejected" means it will not be used for billing. Only one quotation per task can be Accepted at a time.
          enum:
            - Pending
            - Accepted
            - Rejected
          example: Pending
        contactUserId:
          type: string
          description: UUID of the contact user who is the recipient of this quotation. This user receives the quotation when it is sent via email.
          example: 550e8400-e29b-41d4-a716-446655440000
        language:
          type: object
          description: Language code used for this quotation (e.g., "en", "de"). Used for formatting and localization. Can be null if not specified, in which case the workspace default is used.
          example: en
          nullable: true
        comment:
          type: string
          description: Comment or notes for the quotation, formatted as HTML. This content is converted from the internal IDoc format and appears on the quotation document when sent to the contact.
          example: <p>Please review this quotation and let us know if you have any questions.</p>
        items:
          description: Array of all items included in the quotation. Items are ordered by their `order` field. Each item can have multiple options (variants). Items are typically generated from task products and optionally include estimated work time.
          type: array
          items:
            $ref: '#/components/schemas/ExpressQuotationItemResponseDto'
        subTotal:
          type: number
          description: Total amount for all items before tax is applied. This is the sum of all item total prices plus any option prices.
          example: 144000
        total:
          type: number
          description: Final total amount including tax. Calculated as `subTotal + taxAmount`. This is the amount that will be billed if the quotation is accepted.
          example: 168480
        tax:
          type: number
          description: Tax rate percentage applied to the quotation (e.g., 17 for 17% VAT). This is typically taken from the workspace or organization tax settings.
          example: 17
        taxAmount:
          type: number
          description: Tax amount calculated from the subtotal and tax rate. Calculated as `(subTotal * tax) / 100`.
          example: 24480
        createdAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp when the quotation was created
          example: '2024-01-01T00:00:00Z'
        updatedAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp when the quotation was last modified
          example: '2024-01-01T00:00:00Z'
        sentAt:
          type: object
          description: ISO 8601 timestamp when the quotation was sent via email to the contact user. Null if the quotation has not been sent yet.
          example: '2024-01-01T00:00:00Z'
          nullable: true
        createdBy:
          type: string
          description: UUID of the user who created this quotation
          example: 550e8400-e29b-41d4-a716-446655440000
        statusChangedBy:
          type: string
          description: UUID of the user who last changed the quotation status (accepted or rejected). This tracks who made the decision on the quotation.
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - id
        - taskId
        - shortNumber
        - status
        - contactUserId
        - language
        - comment
        - items
        - subTotal
        - total
        - tax
        - taxAmount
        - createdAt
        - updatedAt
        - sentAt
        - createdBy
        - statusChangedBy
    BigPictureResponseDto:
      type: object
      properties:
        groups:
          description: Array of task groups, where each group represents a task type (row) in the Big Picture view. Groups are sorted according to the configured task type order. Each group contains task items (cards) and statistics for that specific task type. Tasks without a recognized type are grouped into a special "other" category.
          example:
            - id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
              items:
                - id: task-uuid-1
                  title: Implement user authentication
                  status: In Progress
                  priority: High
                  typeId: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                  estimatedHours: 8
                  spentHours: 4.5
                  assignedUserId: user-uuid-1
                  itemStatus: 1
                  isInPipeline: true
                  lastComment: null
              stats:
                resolvedCount: 2
                totalEstimate: 16
                totalSpent: 8
                resolvedHours: 4
          type: array
          items:
            type: array
        stats:
          type: object
          description: Overall statistics aggregated across all tasks in the project. These metrics provide a high-level view of project progress, time allocation, and completion status.
          example:
            totalTasks: 10
            resolvedCount: 3
            totalEstimate: 40
            totalSpent: 20
            resolvedHours: 10
      required:
        - groups
        - stats
    UpdateBigPictureOrderDto:
      type: object
      properties:
        projectId:
          type: string
          description: UUID of the project containing the tasks to reorder. The project must exist, not be deleted, and belong to the authenticated user's workspace. The user must have BigPicture.canEdit permission and access to the project.
          example: 7225cca9-ac5f-461d-9a9d-ad8d7ee1b597
          format: uuid
        groupId:
          type: string
          description: UUID of the task type group (row) within which to reorder tasks. This identifies which horizontal row in the Big Picture view contains the tasks you want to reorder. Must be a valid task type ID for the specified project.
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          format: uuid
        newOrder:
          description: Array of task UUIDs in the desired new order (left to right = highest to lowest priority). Must include all tasks currently in the specified group - you cannot omit any tasks. The first task ID in the array will appear leftmost (highest priority), the last will appear rightmost (lowest priority).
          example:
            - f1a2b3c4-d5e6-7890-abcd-ef1234567890
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
            - b1c2d3e4-f5a6-7890-abcd-ef1234567890
          items:
            type: array
            items:
              type: string
          type: array
      required:
        - projectId
        - groupId
        - newOrder
    UpdateBigPictureGroupOrderDto:
      type: object
      properties:
        projectId:
          type: string
          description: UUID of the project containing the task type groups to reorder. The project must exist, not be deleted, and belong to the authenticated user's workspace. The user must have BigPicture.canEdit permission and access to the project.
          example: 7225cca9-ac5f-461d-9a9d-ad8d7ee1b597
          format: uuid
        newOrder:
          description: Array of task type group UUIDs in the desired new order (top to bottom). Must include all task type groups currently in the specified project - you cannot omit any groups. The first group ID in the array will appear at the top of the Big Picture view, the last will appear at the bottom. Tasks without a recognized type are grouped into a special "other" category which can also be included in this array.
          example:
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
            - b1c2d3e4-f5a6-7890-abcd-ef1234567890
            - c1d2e3f4-a5b6-7890-abcd-ef1234567890
          items:
            type: array
            items:
              type: string
          type: array
      required:
        - projectId
        - newOrder
    PoolResponseDto:
      type: object
      properties:
        stats:
          type: object
          description: Overall statistics for the entire pool, aggregating data from all projects and tasks filtered by the Accountable field (not the Assigned field). Only tasks where the accountableId user is set as the Accountable person are included. These statistics provide a high-level overview of workload and progress.
          example:
            totalTasks: 10
            resolvedCount: 3
            totalEstimate: 40
            totalSpent: 20
            resolvedHours: 10
        projects:
          type: array
          description: Array of projects, each containing groups of tasks organized by task type. Tasks are filtered by the Accountable field (not the Assigned field) - only tasks where the accountableId user is set as the Accountable person are included. Projects are sorted by custom order from PoolProjectOrder table. Each project row contains multiple task type groups (e.g., Feature, Bug, Task), which are sorted by custom order from PoolTypeProjectOrder table. Each group contains task items and group-level statistics.
          example:
            - projectId: 550e8400-e29b-41d4-a716-446655440000
              groups:
                - id: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
                  items: []
                  stats:
                    resolvedCount: 2
                    totalEstimate: 16
                    totalSpent: 8
                    resolvedHours: 4
      required:
        - stats
        - projects
    UpdatePoolOrderDto:
      type: object
      properties:
        accountableId:
          type: string
          description: UUID of the accountable user whose pool task order should be updated. The user must exist in the same workspace and not be deleted. Access is controlled by Pool edit permissions (canEditAll, canEditOwnTeams, or canEditOwn).
          example: aff55c87-49c6-4d5d-9b0b-bf7478e2d504
        newOrder:
          description: Array of task UUIDs in the desired priority order. Tasks are displayed horizontally from left to right, with the first item in the array appearing on the left (highest priority). The array should contain all task IDs that need to be reordered. Each task ID must be a valid UUID of a task where the specified accountableId user is set as the Accountable person (filtered by Accountable field, not Assigned field).
          example:
            - 550e8400-e29b-41d4-a716-446655440000
            - 6ba7b810-9dad-11d1-80b4-00c04fd430c8
            - 6ba7b811-9dad-11d1-80b4-00c04fd430c8
          type: array
          items:
            type: string
      required:
        - accountableId
        - newOrder
    UpdatePoolProjectOrderDto:
      type: object
      properties:
        accountableId:
          type: string
          description: UUID of the accountable user whose pool project order should be updated. The user must exist in the same workspace and not be deleted. Access is controlled by Pool edit permissions (canEditAll, canEditOwnTeams, or canEditOwn).
          example: aff55c87-49c6-4d5d-9b0b-bf7478e2d504
        newOrder:
          description: Array of project UUIDs in the desired vertical order. Projects are displayed as rows in the pool view, with the first item in the array appearing at the top. The array should contain all project IDs that need to be reordered. Each project ID must be a valid UUID of a project in the same workspace.
          example:
            - 550e8400-e29b-41d4-a716-446655440000
            - 6ba7b810-9dad-11d1-80b4-00c04fd430c8
            - 6ba7b811-9dad-11d1-80b4-00c04fd430c8
          type: array
          items:
            type: string
      required:
        - accountableId
        - newOrder
    UpdatePoolGroupOrderDto:
      type: object
      properties:
        accountableId:
          type: string
          description: UUID of the accountable user whose pool group order should be updated. The user must exist in the same workspace and not be deleted. Access is controlled by Pool edit permissions (canEditAll, canEditOwnTeams, or canEditOwn).
          example: aff55c87-49c6-4d5d-9b0b-bf7478e2d504
        projectId:
          type: string
          description: UUID of the project within which the task type group order should be updated. The project must exist in the same workspace. Task type groups (e.g., Feature, Bug, Task) are displayed as columns within each project row in the pool view.
          example: 550e8400-e29b-41d4-a716-446655440000
        newOrder:
          description: Array of task type UUIDs in the desired horizontal order. Task type groups are displayed as columns within the project row, with the first item in the array appearing on the left. The array should contain all task type IDs that need to be reordered. Each task type ID must be a valid UUID of a task type configured in the workspace.
          example:
            - 550e8400-e29b-41d4-a716-446655440000
            - 6ba7b810-9dad-11d1-80b4-00c04fd430c8
            - 6ba7b811-9dad-11d1-80b4-00c04fd430c8
          type: array
          items:
            type: string
      required:
        - accountableId
        - projectId
        - newOrder
    StackColumnType:
      type: string
      enum:
        - toDo
        - getFeedback
        - giveFeedback
        - done
        - backlog
      description: 'Type of column indicating the workflow stage. Possible values: toDo (pipeline tasks in New/InProgress status), giveFeedback (tasks assigned to user in Feedback status), getFeedback (tasks NOT assigned to user in Feedback status), done (pipeline tasks in Closed/Resolved status), backlog (non-pipeline tasks assigned to user). **Note**: Pipeline tasks appear regardless of assignment. Non-pipeline tasks are filtered by Assigned field.'
    TaskPriority:
      type: string
      enum:
        - Low
        - Normal
        - High
      description: 'Priority level of the task. Used for sorting: High > Normal > Low. Tasks with higher priority appear first in their column.'
    StackTaskDetailsDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) of the task
        title:
          type: string
          description: Title or name of the task
        icon:
          type: object
          description: Icon identifier for the task. Can be null if no icon is set.
          nullable: true
        shortNumber:
          type: number
          description: Short number of the task within its workspace. Used for display and sorting purposes.
        color:
          type: string
          description: Color code (hex format) representing the organization that owns the task. Used for visual identification.
        organizationShortName:
          type: string
          description: Short name or abbreviation of the organization that owns the task
        projectShortNumber:
          type: object
          description: Short number of the project this task belongs to. Can be null if task is not associated with a project.
          nullable: true
        statusId:
          type: string
          description: UUID of the task status. The status determines which column the task appears in (toDo, giveFeedback, getFeedback, done, backlog).
        summary:
          type: object
          description: Optional summary or description of the task. Provides additional context about what needs to be done.
          nullable: true
        priority:
          description: 'Priority level of the task. Used for sorting: High > Normal > Low. Tasks with higher priority appear first in their column.'
          allOf:
            - $ref: '#/components/schemas/TaskPriority'
        typeId:
          type: string
          description: UUID of the task type. Task types define workflows, fields, and statuses available for the task.
        projectId:
          type: string
          description: UUID of the project this task belongs to
        deadline:
          type: object
          description: Deadline date in ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss.sssZ). Can be null if no deadline is set.
          nullable: true
        estimatedTime:
          type: object
          description: Estimated time to complete the task, measured in hours. Can be null if no estimate is provided.
          nullable: true
        spentTime:
          type: object
          description: Total time already spent on the task, measured in hours. This field is only visible if the authenticated user has the Tasks.seeLoggedTime permission. Can be null if no time has been logged yet.
          nullable: true
        commentsCount:
          type: number
          description: Total number of comments on the task. Includes all comments from team members.
        accountableId:
          type: object
          description: UUID of the user who is accountable for the task (responsible person). Can be null if no one is assigned as accountable.
          nullable: true
        assignedToId:
          type: object
          description: 'UUID of the user who is assigned to work on the task (Assigned field). Can be null if no one is assigned. **Important**: Non-pipeline tasks in the stack are filtered by this field (assignedToId). Pipeline tasks appear regardless of this field. This field determines which column the task appears in for feedback-related columns (giveFeedback vs getFeedback).'
          nullable: true
        orgName:
          type: string
          description: Full name of the organization that owns the task. Provides human-readable context.
        tags:
          description: Array of tag names associated with the task. Tags enable cross-project organization and filtering of tasks.
          type: array
          items:
            type: string
      required:
        - id
        - title
        - icon
        - shortNumber
        - color
        - organizationShortName
        - projectShortNumber
        - statusId
        - summary
        - priority
        - typeId
        - projectId
        - deadline
        - estimatedTime
        - commentsCount
        - accountableId
        - assignedToId
        - orgName
        - tags
    StackColumnDto:
      type: object
      properties:
        type:
          description: 'Type of column indicating the workflow stage. Possible values: toDo (pipeline tasks in New/InProgress status), giveFeedback (tasks assigned to user in Feedback status), getFeedback (tasks NOT assigned to user in Feedback status), done (pipeline tasks in Closed/Resolved status), backlog (non-pipeline tasks assigned to user). **Note**: Pipeline tasks appear regardless of assignment. Non-pipeline tasks are filtered by Assigned field.'
          allOf:
            - $ref: '#/components/schemas/StackColumnType'
        tasks:
          description: Array of tasks in this column. Tasks are sorted by priority (descending) then shortNumber (ascending), with pipeline tasks appearing first.
          type: array
          items:
            $ref: '#/components/schemas/StackTaskDetailsDto'
      required:
        - type
        - tasks
    StackResponseDto:
      type: object
      properties:
        columns:
          description: 'Array of stack columns representing different workflow stages. Always includes all five column types (toDo, giveFeedback, getFeedback, done, backlog), even if some columns are empty. Columns appear in this order. **Task filtering**: Pipeline tasks (regardless of assignment) appear in toDo and done columns. Non-pipeline tasks filtered by Assigned field (assignedToId) appear in giveFeedback, getFeedback, and backlog columns. Tasks are sorted by priority and pipeline order.'
          type: array
          items:
            $ref: '#/components/schemas/StackColumnDto'
      required:
        - columns
    PipelineResponseDto:
      type: object
      properties:
        users:
          type: object
          description: Object containing pipeline data for each user, keyed by userId. Each entry contains employee information, days array (work day data), items array (planned tasks), and reality array (time logs if enabled).
          additionalProperties:
            type: object
            $ref: '#/components/schemas/PipelineUserRowDto'
          example:
            user-id-1:
              employeeId: employee-id-1
              userId: user-id-1
              name: John Doe
              days: []
              items: []
              reality: []
        ready:
          type: boolean
          description: Whether the pipeline week is marked as active/ready. When true, the planned tasks are visible in employee stacks. When false, the week is still in draft/planning mode.
          example: true
        year:
          type: number
          description: Year of the pipeline period start date (ISO week year)
          example: 2025
        week:
          type: number
          description: ISO week number of the pipeline period start date
          example: 43
      required:
        - users
        - ready
        - year
        - week
    ActivatePipelineDto:
      type: object
      properties:
        dateStart:
          type: string
          description: Start date of the pipeline week in ISO format (YYYY-MM-DD). The week is determined from this date using ISO week numbering.
          example: '2025-11-03'
        dateEnd:
          type: string
          description: End date of the pipeline week in ISO format (YYYY-MM-DD). Should match the end of the week period (typically 1-2 weeks from dateStart based on workspace settings).
          example: '2025-11-16'
      required:
        - dateStart
        - dateEnd
    CreatePipelineItemDto:
      type: object
      properties:
        taskId:
          type: string
          description: Unique identifier of the task to plan in the pipeline. The task must exist and the user must have access to view it.
          example: 123e4567-e89b-12d3-a456-426614174000
        userId:
          type: string
          description: 'Unique identifier of the user/team member to assign this task to. Permission restrictions apply: users with Pipeline.canPlanOwnTeams can only assign to themselves or team members, while users with Pipeline.canPlanAll can assign to any user.'
          example: 123e4567-e89b-12d3-a456-426614174001
        dateStart:
          type: string
          description: 'Start date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeStart to specify the actual start time relative to work day start. Example: "2025-11-04T00:00:00Z" (the time part is ignored).'
          example: '2025-11-04T00:00:00Z'
        dateEnd:
          type: string
          description: 'End date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeEnd to specify the actual end time relative to work day start. Maximum 2 weeks from dateStart. Example: "2025-11-04T00:00:00Z" (the time part is ignored).'
          example: '2025-11-04T00:00:00Z'
        timeStart:
          type: number
          description: |-
            **CRITICAL**: Start time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). 

            **Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)
            **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.

            **Examples** (assuming 8-hour work day starting at 09:00):
            - `timeStart: 0` = 09:00 (start of work day)
            - `timeStart: 60` = 10:00 (1 hour after work day start)
            - `timeStart: 240` = 13:00 (4 hours after work day start = midday)
            - `timeStart: 480` = 17:00 (8 hours = end of work day)

            **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):
            To schedule multiple tasks sequentially throughout the day, chain them together:
            - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)
            - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)
            - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)
            - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)
            - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)

            **Calculation**: Actual time = workDayStartTime + (timeStart minutes)
            **Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeStart=0 means 09:00, not 00:00.
          example: 0
          minimum: 0
        timeEnd:
          type: number
          description: |-
            **CRITICAL**: End time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). 

            **Range**: Must be greater than timeStart, up to workDayLength (typically 0-480 for an 8-hour day)
            **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.

            **Examples** (assuming 8-hour work day starting at 09:00):
            - `timeEnd: 240` = 13:00 (4 hours after work day start = midday)
            - `timeEnd: 330` = 14:30 (5.5 hours after work day start)
            - `timeEnd: 480` = 17:00 (8 hours = end of work day)

            **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):
            To schedule multiple tasks sequentially throughout the day, chain them together:
            - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)
            - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)
            - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)
            - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)
            - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)

            **Calculation**: Actual time = workDayStartTime + (timeEnd minutes)
            **Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeEnd=480 means 17:00, not 08:00. Must be greater than timeStart.
          example: 240
          minimum: 0
      required:
        - taskId
        - userId
        - dateStart
        - dateEnd
        - timeStart
        - timeEnd
    PipelineItemResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the created or updated pipeline item
          example: 123e4567-e89b-12d3-a456-426614174002
        taskId:
          type: string
          description: Unique identifier of the task that was planned
          example: 123e4567-e89b-12d3-a456-426614174000
        userId:
          type: string
          description: Unique identifier of the user/team member the task is assigned to
          example: 123e4567-e89b-12d3-a456-426614174001
        dateStart:
          type: string
          description: 'Start date and time in ISO format (RESPONSE VALUE - this is different from input). **Note**: In the request, the time component of dateStart is IGNORED. In the response, the time component IS meaningful - it represents the calculated absolute time based on work day start + timeStart minutes. The date part (YYYY-MM-DD) represents the calendar day. When reading this response value, extract the time relative to work day start, not midnight. Example: If work day starts at 09:00 and timeStart was 30, this response will show 09:30 on the specified date.'
          example: '2025-11-04T09:30:00Z'
        dateEnd:
          type: string
          description: 'End date and time in ISO format (RESPONSE VALUE - this is different from input). **Note**: In the request, the time component of dateEnd is IGNORED. In the response, the time component IS meaningful - it represents the calculated absolute time based on work day start + timeEnd minutes. The date part (YYYY-MM-DD) represents the calendar day. When reading this response value, extract the time relative to work day start, not midnight. Example: If work day starts at 09:00 and timeEnd was 330, this response will show 14:30 (5.5 hours from 09:00) on the specified date.'
          example: '2025-11-04T14:30:00Z'
      required:
        - id
        - taskId
        - userId
        - dateStart
        - dateEnd
    UpdatePipelineItemDto:
      type: object
      properties:
        taskId:
          type: string
          description: Unique identifier of the task to plan. Optional - if not provided, the existing taskId is preserved. The task must exist and the user must have access to view it.
          example: 123e4567-e89b-12d3-a456-426614174000
        userId:
          type: string
          description: 'Unique identifier of the user/team member to assign this task to. Optional - if not provided, the existing userId is preserved. Permission restrictions apply: users with Pipeline.canPlanOwnTeams can only assign to themselves or team members, while users with Pipeline.canPlanAll can assign to any user.'
          example: 123e4567-e89b-12d3-a456-426614174001
        dateStart:
          type: string
          description: 'Start date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeStart to specify the actual start time relative to work day start. Optional - if not provided, the existing dateStart is preserved. Example: "2025-11-04T00:00:00Z" (the time part is ignored).'
          example: '2025-11-04T00:00:00Z'
        dateEnd:
          type: string
          description: 'End date in ISO format (YYYY-MM-DDTHH:mm:ssZ). **IMPORTANT**: Only the date part (YYYY-MM-DD) is used - the time component is IGNORED. Use timeEnd to specify the actual end time relative to work day start. Maximum 2 weeks from dateStart. Optional - if not provided, the existing dateEnd is preserved. Example: "2025-11-04T00:00:00Z" (the time part is ignored).'
          example: '2025-11-04T00:00:00Z'
        timeStart:
          type: number
          description: |-
            **CRITICAL**: Start time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). Optional - if not provided, the existing timeStart is preserved.

            **Range**: 0 to workDayLength (typically 0-480 for an 8-hour day)
            **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.

            **Examples** (assuming 8-hour work day starting at 09:00):
            - `timeStart: 0` = 09:00 (start of work day)
            - `timeStart: 60` = 10:00 (1 hour after work day start)
            - `timeStart: 240` = 13:00 (4 hours after work day start = midday)
            - `timeStart: 480` = 17:00 (8 hours = end of work day)

            **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):
            To schedule multiple tasks sequentially throughout the day, chain them together:
            - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)
            - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)
            - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)
            - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)
            - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)

            **Calculation**: Actual time = workDayStartTime + (timeStart minutes)
            **Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeStart=0 means 09:00, not 00:00.
          example: 0
          minimum: 0
        timeEnd:
          type: number
          description: |-
            **CRITICAL**: End time in MINUTES from the beginning of the work day (NOT from midnight, NOT absolute clock time). Optional - if not provided, the existing timeEnd is preserved.

            **Range**: Must be greater than timeStart, up to workDayLength (typically 0-480 for an 8-hour day)
            **Format**: Must be in 15-minute increments: 0, 15, 30, 45, 60, 75, 90, 105, 120, etc.

            **Examples** (assuming 8-hour work day starting at 09:00):
            - `timeEnd: 240` = 13:00 (4 hours after work day start = midday)
            - `timeEnd: 330` = 14:30 (5.5 hours after work day start)
            - `timeEnd: 480` = 17:00 (8 hours = end of work day)

            **Planning Multiple Tasks for One Day** (example with 8-hour work day starting at 09:00):
            To schedule multiple tasks sequentially throughout the day, chain them together:
            - Task 1: `timeStart: 0, timeEnd: 75` = 09:00 to 10:15 (1.25 hours)
            - Task 2: `timeStart: 75, timeEnd: 150` = 10:15 to 11:30 (1.25 hours)
            - Task 3: `timeStart: 150, timeEnd: 240` = 11:30 to 13:00 (1.5 hours)
            - Task 4: `timeStart: 240, timeEnd: 330` = 13:00 to 14:30 (1.5 hours)
            - Task 5: `timeStart: 330, timeEnd: 480` = 14:30 to 17:00 (2.5 hours)

            **Calculation**: Actual time = workDayStartTime + (timeEnd minutes)
            **Important**: This is NOT the same as hours since midnight. If your work day starts at 09:00, timeEnd=480 means 17:00, not 08:00. Must be greater than timeStart.
          example: 240
          minimum: 0
    TranslationDto:
      type: object
      properties:
        language:
          type: string
          description: Language code (e.g., en, es, fr)
        name:
          type: string
          description: Translated name
        description:
          type: object
          description: Translated description
      required:
        - language
        - name
    TaskTypeDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the task type
        name:
          type: string
          description: Name of the task type (e.g., Bug, Feature, Support Request). Used for identification in the UI.
        icon:
          type: string
          description: Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:star:`). Used for visual representation in task lists and views.
        type:
          type: string
          enum:
            - Feature
            - Bug
          description: 'Base type category: Feature (value-adding work) or Bug (bug fix, typically not billable)'
        sort:
          type: number
          description: Sort order for displaying task types in lists. Lower numbers appear first.
        statuses:
          description: Array of task status UUIDs that are valid for this task type. Defines the workflow states available for tasks of this type.
          type: array
          items:
            type: string
        customFields:
          description: Array of custom field UUIDs associated with this task type. These custom fields appear when creating or editing tasks of this type.
          type: array
          items:
            type: string
        requiredFields:
          description: 'Array of field names that are required when creating tasks of this type. This is contextual metadata for POST /tasks: fields listed here must be included even when the global create-task OpenAPI schema marks them optional. Common values include summary, estimatedTime, deadline, assignedToId, accountableId, and custom field names.'
          type: array
          items:
            type: string
        translations:
          description: Multilingual translations for the task type name. Supports displaying the type name in different languages based on user preferences.
          type: array
          items:
            $ref: '#/components/schemas/TranslationDto'
        deleted:
          type: boolean
          description: Whether this task type has been soft-deleted. Deleted types are typically hidden from active use but retained for historical data.
      required:
        - id
        - name
        - icon
        - type
        - sort
        - statuses
        - customFields
        - requiredFields
        - translations
        - deleted
    TaskStatusDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the task status
        name:
          type: string
          description: Name of the task status (e.g., New, In Progress, Feedback, Resolved, Closed, Backlog). Used for identification in the UI.
        icon:
          type: string
          description: Icon identifier in the format `:icon_name:` (e.g., `:hourglass_flowing_sand:`, `:check_mark:`, `:x:`). Used for visual representation in task lists, Kanban boards, and status selectors.
        type:
          type: string
          enum:
            - New
            - InProgress
            - Feedback
            - Backlog
            - Resolved
            - Closed
          description: 'Base status type: New, InProgress, Feedback, Backlog, Resolved, or Closed. Defines the semantic meaning and behavior of the status.'
        sort:
          type: number
          description: Sort order for displaying task statuses in lists and dropdowns. Lower numbers appear first.
        translations:
          description: Multilingual translations for the task status name. Supports displaying the status name in different languages based on user preferences.
          type: array
          items:
            $ref: '#/components/schemas/TranslationDto'
        deleted:
          type: boolean
          description: Whether this task status has been soft-deleted. Deleted statuses are typically hidden from active use but retained for historical data.
      required:
        - id
        - name
        - icon
        - type
        - sort
        - translations
        - deleted
    SelectOptionDto:
      type: object
      properties:
        value:
          type: string
          description: Option value
        translations:
          description: Option translations
          type: array
          items:
            $ref: '#/components/schemas/TranslationDto'
      required:
        - value
        - translations
    TaskCustomFieldDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the custom field
        name:
          type: string
          description: Name of the custom field (e.g., Risk Level, Affected Component, SLA Level). Used as the field label in forms.
        description:
          type: object
          description: Optional description explaining what this custom field is used for and how to fill it out
        type:
          type: string
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          description: 'Type of the custom field: Text (short entries), TextArea (longer entries), Number (numeric values), Date (date selection), Checkbox (Yes/No), or Select (dropdown with predefined options)'
        sort:
          type: number
          description: Sort order for displaying custom fields in forms. Lower numbers appear first.
        entity:
          type: string
          enum:
            - Task
            - Project
            - Organization
            - Object
          description: Entity this field belongs to. For task custom fields, this is always Task.
        translations:
          description: Multilingual translations for the custom field name and description. Supports displaying field labels in different languages based on user preferences.
          type: array
          items:
            $ref: '#/components/schemas/TranslationDto'
        selectOptions:
          description: Array of select options for dropdown (Select) type fields. Each option has a value and multilingual translations. Only populated for Select type fields.
          type: array
          items:
            $ref: '#/components/schemas/SelectOptionDto'
      required:
        - id
        - name
        - type
        - sort
        - entity
        - translations
        - selectOptions
    TaskCustomFieldValueDto:
      type: object
      properties:
        fieldId:
          type: string
          description: UUID of the custom field. Must be a custom field associated with the task type being used. Use GET /tasks/types and/or GET /tasks/custom-fields.
          example: 550e8400-e29b-41d4-a716-446655440000
        value:
          type: string
          description: 'Value for the custom field. The format depends on the field type: Text/TextArea (string), Number (numeric string), Date (ISO 8601 date string), Checkbox (true/false string), Select (one of the predefined option values). Validation is performed based on the field type configuration.'
          example: Custom value
      required:
        - fieldId
        - value
    CreateTaskDto:
      type: object
      properties:
        title:
          type: string
          description: Task title or name. This is the main identifier shown in task lists and views.
          example: Implement new feature
        projectId:
          type: string
          description: UUID of the project this task belongs to. The project must exist and the user must have access to it.
          example: 550e8400-e29b-41d4-a716-446655440000
        typeId:
          type: string
          description: UUID of the task type. The type defines the workflow, available statuses, custom fields, and dynamically required fields. Must be one of the task types allowed for this project (configured in the project settings). Use GET /projects/{id} to see which task types are available for the project, then use GET /tasks/types or GET /administration/task-settings/types to inspect the selected type requiredFields before creating the task.
          example: 660e8400-e29b-41d4-a716-446655440001
        statusId:
          type: string
          description: UUID of the initial task status. The status must be valid for the selected task type. Common statuses include New, In Progress, Feedback, Resolved, Closed, and Backlog. Use GET /tasks/types or GET /administration/task-settings/types and choose a status id from the selected task type statuses array.
          example: 770e8400-e29b-41d4-a716-446655440002
        priority:
          type: string
          description: 'Task priority level indicating urgency. Options: Low, Normal, High. Used for prioritization and filtering.'
          enum:
            - Low
            - Normal
            - High
          example: Normal
        description:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <p>Task description in HTML</p>
        icon:
          type: object
          description: Task icon identifier. Must be in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:memo:`, `:white_check_mark:`). Used for visual identification in task lists and views. Leave null to use the default icon from the task type.
          example: ':rocket:'
        assignedToId:
          type: object
          description: 'UUID of the Leadtime user assigned to work on this task. Use the User.id UUID, not the Employee.id UUID. The user must be assignable in the selected project: either a project member/user with access to that project, or the workspace Leadtime agent user. Use GET /projects/{id} to inspect the project users array, and PATCH /projects/{id} to add users before assigning them. GET /workspace/users lists workspace users, but not every workspace user is assignable to every project. Leave null if no one is assigned yet.'
          example: 880e8400-e29b-41d4-a716-446655440003
          nullable: true
        accountableId:
          type: object
          description: 'UUID of the Leadtime user who is accountable for this task. Use the User.id UUID, not the Employee.id UUID. The user must be assignable in the selected project: either a project member/user with access to that project, or the workspace Leadtime agent user. Use GET /projects/{id} to inspect the project users array, and PATCH /projects/{id} to add users before making them accountable. GET /workspace/users lists workspace users, but not every workspace user is assignable to every project. Leave null if no one is accountable yet.'
          example: 990e8400-e29b-41d4-a716-446655440004
          nullable: true
        summary:
          type: string
          description: Brief summary or short description of the task. This is a concise overview that appears in task cards and lists. The global OpenAPI schema marks this optional because requiredness is configured per task type. It is required when the selected task type lists `summary` in requiredFields. Use GET /tasks/types or GET /administration/task-settings/types to inspect requiredFields before POST /tasks.
          example: Brief summary of the task
        estimatedTime:
          type: number
          description: 'Estimated time to complete the task in hours (decimal allowed). Used for planning and capacity management. Example: 8 for 8 hours, 2.5 for 2.5 hours. The global OpenAPI schema marks this optional because requiredness is configured per task type. It is required when the selected task type lists `estimatedTime` in requiredFields. Use GET /tasks/types or GET /administration/task-settings/types to inspect requiredFields before POST /tasks.'
          example: 2.5
        deadline:
          type: string
          description: Task deadline or due date in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ). Used for tracking deadlines and generating deadline warnings.
          example: '2024-12-31T23:59:59Z'
        guestAccess:
          type: object
          description: Whether organization members (guest users) can access this task. When true, guest users can view and interact with the task. When false, only workspace members can access it. Leave null to use the project default.
          example: false
          nullable: true
          default: false
        customFields:
          description: Array of custom field values. Each value must correspond to a custom field associated with the selected task type. Use GET /tasks/types and/or GET /tasks/custom-fields. The field value format depends on the field type.
          type: array
          items:
            $ref: '#/components/schemas/TaskCustomFieldValueDto'
        tags:
          description: Array of tag UUIDs to categorize this task. Use GET /tags/task/list. Tags enable cross-project organization and filtering and can be automatically inherited from project components.
          example:
            - aa0e8400-e29b-41d4-a716-446655440005
          type: array
          items:
            type: string
        subTasksIds:
          description: Array of existing task UUIDs to add as subtasks. Subtasks allow breaking down complex tasks into smaller units. Each subtask must exist and be accessible. The parent-child relationship is established immediately.
          example:
            - bb0e8400-e29b-41d4-a716-446655440006
          type: array
          items:
            type: string
        linkedObjectIds:
          description: Object instance UUIDs to link to the new task via ObjectsOnTask. Use GET /objects to find object IDs. Requires the same permission as linking objects on an existing task (`objects.canEdit`). Invalid or inaccessible object IDs result in an error and the task is not created.
          type: array
          items:
            type: string
        products:
          description: Array of product settings to associate products with this task. Use GET /workspace/product-list for product IDs and GET /administration/product-catalog/{id} for variants/options. Each product setting includes the product ID, quantity, variant, and options.
          type: array
          items:
            $ref: '#/components/schemas/TaskProductSettingsDto'
        componentItemEntityUniqueId:
          type: object
          description: Component item entity unique ID if this task is related to a project component item. Used for linking tasks to specific components in project structures.
          example: unique-component-id
        isChangeRequest:
          type: boolean
          description: Whether this task is a change request. Change requests are typically billed separately and tracked differently in Single projects. Used for scope change management.
          example: false
        enableSupportContingent:
          type: boolean
          description: Enable support contingent for this task. This flag marks a task as eligible for support contingent billing. Only available for Support projects.
          example: false
      required:
        - title
        - projectId
        - typeId
        - statusId
        - priority
        - description
    TaskDetailsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Task ID (UUID)
        title:
          type: string
          description: Task title
        titleIsGenerating:
          type: boolean
          description: Whether title is being generated by AI
        icon:
          type: object
          description: Task icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:memo:`). Can be null if no custom icon is set.
        iconIsGenerating:
          type: boolean
          description: Whether icon is being generated by AI
        summaryIsGenerating:
          type: boolean
          description: Whether summary is being generated by AI
        createdAt:
          format: date-time
          type: string
          description: Task creation timestamp
        deadline:
          format: date-time
          type: string
          description: Task deadline
        priority:
          type: object
          description: Task priority (TaskPriority enum)
        projectId:
          type: string
          description: Project ID
        statusId:
          type: string
          description: Task status ID
        userId:
          type: string
          description: Creator user ID
        assignedToId:
          type: string
          description: Assigned user ID
        accountableId:
          type: string
          description: Accountable user ID
        summary:
          type: string
          description: Task summary
        typeId:
          type: string
          description: Task type ID
        shortNumber:
          type: number
          description: Task short number (workspace-scoped)
        estimatedTime:
          type: number
          description: Estimated time in hours
        description:
          type: string
          description: Task description in HTML. May include embedded assets as elements such as `<div data-type="appImage" fileId="…" filename="…">` (images), `appFile`, or `appVideo`—binary content is not embedded; use authenticated GET `{appOrigin}/api/public/workspace/files/{fileId}` with the same Public API Bearer token to retrieve it.
        guestAccess:
          type: boolean
          description: Guest access enabled for organization members
        project:
          type: object
          description: Project information
        accountable:
          type: object
          description: Accountable user details
        assignedTo:
          type: object
          description: Assigned user details
        customFields:
          type: object
          description: Custom fields values
        history:
          description: Task history entries
          type: array
          items:
            type: array
        comments:
          description: Task comments; each `body` is HTML and may contain the same embedded `appImage` / `appFile` / `appVideo` divs with `fileId` as the task description. Fetch binaries via authenticated `/api/public/workspace/files/{fileId}` with the same Public API Bearer token when needed.
          type: array
          items:
            type: array
        quotations:
          description: Express quotations
          type: array
          items:
            type: array
        spentTime:
          type: number
          description: Total spent time in minutes
        childSpentTime:
          type: number
          description: Child tasks spent time in minutes
        participantsCount:
          type: number
          description: Number of participants
        participantsList:
          description: List of participant user IDs
          type: array
          items:
            type: array
        isParticipant:
          type: boolean
          description: Whether current user is a participant
        notificationSettings:
          type: object
          description: Notification settings
        tags:
          description: Task tags
          type: array
          items:
            type: array
        parentTaskId:
          type: object
          description: Parent task ID for subtasks
        subTasksIds:
          description: Child task IDs
          type: array
          items:
            type: array
        skipFromBilling:
          type: boolean
          description: Skip from billing
        billed:
          type: boolean
          description: Whether task is billed
        products:
          description: Task products
          type: array
          items:
            type: array
        componentItemId:
          type: object
          description: Component item ID
        isChangeRequest:
          type: boolean
          description: Whether task is a change request
        emailDetails:
          type: object
          description: Email details
        emailSendError:
          type: string
          description: Email send error message
        emailSendErrorCode:
          type: string
          description: Email send error code
        emailSendFailedAt:
          format: date-time
          type: string
          description: Email send failed timestamp
        emailSendFailedTo:
          description: Email send failed recipients
          type: array
          items:
            type: array
        enableSupportContingent:
          type: boolean
          description: Enable support contingent
      required:
        - id
        - title
        - titleIsGenerating
        - iconIsGenerating
        - summaryIsGenerating
        - createdAt
        - priority
        - projectId
        - statusId
        - userId
        - typeId
        - shortNumber
        - description
        - guestAccess
        - project
        - customFields
        - history
        - comments
        - quotations
        - spentTime
        - childSpentTime
        - participantsCount
        - participantsList
        - isParticipant
        - notificationSettings
        - tags
        - subTasksIds
        - billed
        - products
        - isChangeRequest
        - enableSupportContingent
    PatchTaskDto:
      type: object
      properties:
        title:
          type: string
          description: Task title or name. Updates the main identifier shown in task lists and views.
        icon:
          type: object
          description: Task icon identifier. Must be in the format `:icon_name:` (e.g., `:rocket:`, `:bug:`, `:memo:`, `:white_check_mark:`). Used for visual identification. Set to null to remove the custom icon and use the task type default.
        description:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
        summary:
          type: string
          description: Brief summary or short description of the task. Appears in task cards and lists. Can be auto-generated by AI or manually entered.
        priority:
          type: string
          enum:
            - Low
            - Normal
            - High
          description: 'Task priority level: Low, Normal, or High. Used for prioritization and filtering.'
        statusId:
          type: string
          description: UUID of the task status to change to. The status must be valid for the current task type. Use GET /tasks/types and choose from the selected task type statuses array.
        typeId:
          type: string
          description: UUID of the task type to change to. Use GET /tasks/types; changing type may require changing status and customFields to values allowed by the new type.
        projectId:
          type: string
          description: UUID of the project to move the task to. Use GET /projects. Changing the project cascades to all subtasks and time logs, moving them to the new project as well. The user must have access to the new project.
        assignedToId:
          type: object
          description: UUID of the user to assign the task to. Use User.id from GET /projects/{id} users array for the task project; GET /workspace/users is the global pool and not every user is assignable to every project. Set to null to unassign.
        accountableId:
          type: object
          description: UUID of the user who is accountable for the task. Use User.id from GET /projects/{id} users array for the task project; GET /workspace/users is the global pool and not every user is assignable to every project. Set to null to remove accountability.
        estimatedTime:
          type: number
          description: 'Estimated time to complete the task in hours (decimal allowed). Example: 8 for 8 hours, 2.5 for 2.5 hours.'
          example: 2.5
        deadline:
          type: string
          description: Task deadline or due date in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ). Used for tracking deadlines and generating deadline warnings. Set to null to remove the deadline.
        guestAccess:
          type: boolean
          description: Whether organization members (guest users) can access this task. When true, guest users can view and interact with the task. When false, only workspace members can access it.
          default: false
        tags:
          description: Array of tag UUIDs to categorize this task. Replaces all existing tags. Use GET /tags/task/list. Use an empty array to remove all tags.
          type: array
          items:
            type: string
        customFields:
          type: array
          description: Array of custom field values to update. Each value must correspond to a custom field associated with the task type. The field value format depends on the field type. Replaces all existing custom field values. Use an empty array to clear all custom fields.
          items:
            type: object
            properties:
              fieldId:
                type: string
                format: uuid
                description: Custom field ID (UUID). Use GET /tasks/types and/or GET /tasks/custom-fields.
              value:
                type: string
                description: Field value (format depends on field type)
        isChangeRequest:
          type: boolean
          description: Whether this task is a change request. Change requests are typically billed separately and tracked differently in Single projects. Used for scope change management.
        subTasksIds:
          description: Array of task UUIDs to set as subtasks. Replaces all existing subtasks. Use GET /tasks/grid to find accessible tasks. Use an empty array to remove all subtasks.
          type: array
          items:
            type: string
        enableSupportContingent:
          type: boolean
          description: Enable support contingent for this task
    AddTaskCommentDto:
      type: object
      properties:
        comment:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <p>This is a comment in HTML</p>
        assignedToId:
          type: object
          description: Optional UUID of the user to assign the task to. If provided, the task assignee is updated when the comment is created. Leave null to keep the current assignee.
          example: 550e8400-e29b-41d4-a716-446655440000
        statusId:
          type: object
          description: Optional UUID of the task status to update the task to. If provided, the task status is changed when the comment is created. The status must be valid for the task type.
          example: 660e8400-e29b-41d4-a716-446655440000
        timeBooking:
          type: object
          description: Optional time booking data to log working hours on the task. If provided, time is logged when the comment is created. Includes hours (number) and activityId (UUID of the work activity type).
          example:
            hours: 2
            activityId: 770e8400-e29b-41d4-a716-446655440000
        guestAccess:
          type: object
          description: Whether this comment should be visible to organization members (guest users). When true, the comment is public and visible to guests. When false, the comment is internal and only visible to workspace members. Leave null to use the task default.
          default: false
        helpdeskEmailId:
          type: string
          description: UUID of the helpdesk email this comment is related to. Used for linking comments to incoming helpdesk emails in the helpdesk integration.
          example: 880e8400-e29b-41d4-a716-446655440000
        sendHelpdeskTo:
          description: Array of email addresses to send the comment to via helpdesk. Used for sending task comments as emails to external recipients (e.g., customers). Each email address must be valid.
          example:
            - user@example.com
          type: array
          items:
            type: array
      required:
        - comment
    TaskCommentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Comment unique identifier (UUID)
        userId:
          type: string
          description: User ID of the comment author who created this comment
        createdAt:
          format: date-time
          type: string
          description: Timestamp when the comment was originally created (ISO 8601 format)
        lastUpdated:
          format: date-time
          type: string
          description: Timestamp when the comment was last modified (ISO 8601 format). Updated when the comment body is edited.
        body:
          type: string
          description: Comment body content in HTML format. Converted from the internal editor format for API consumption. May include formatting, links, mentions, and other rich text elements.
        guestAccess:
          type: boolean
          description: Whether guest access (public visibility) is enabled for this comment. When true, organization members (guest users) can see this comment.
        emailDetails:
          type: object
          description: Email details if this comment was sent via email (helpdesk integration)
        emailSendError:
          type: string
          description: Error message if email sending failed for this comment
        emailSendErrorCode:
          type: string
          description: Error code categorizing the email send failure
        emailSendFailedAt:
          format: date-time
          type: string
          description: Timestamp when email sending failed (ISO 8601 format)
        emailSendFailedTo:
          description: Array of email addresses where email sending failed
          type: array
          items:
            type: array
      required:
        - id
        - userId
        - createdAt
        - lastUpdated
        - body
        - guestAccess
    UpdateTaskCommentDto:
      type: object
      properties:
        comment:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <p>Updated comment with <strong>formatting</strong></p>
      required:
        - comment
    AddSubtaskDto:
      type: object
      properties:
        subtaskId:
          type: string
          description: UUID of an existing task to add as a subtask to the parent task. The subtask must exist in the workspace and be accessible to the user. The subtask will be linked to the parent task specified in the URL path.
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - subtaskId
    GetProjectBillingSettingsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: 'Unique identifier for the project. Format: UUID v4.'
          example: 123e4567-e89b-12d3-a456-426614174000
        allowMixedBilling:
          type: boolean
          description: 'Controls invoice structure: true = combined invoices (support and subscriptions on one invoice), false = split invoices (support invoices generated separately). Combined invoices bill support efforts by booked hours and subscriptions together. Split invoices generate separate invoices for each billing type.'
          example: true
        bugsAreBillable:
          type: boolean
          description: Determines whether time spent on tasks with "Bug" activity type can be billed to the customer. When true, bug tasks are included in billable hours. When false, bug tasks are excluded from billing calculations.
          example: true
        includeComponentsInBilling:
          type: boolean
          description: When true, component hours and amounts are included in estimates and invoices. When false, component totals are excluded from billing calculations.
          example: true
        invoiceContactId:
          type: object
          description: Default invoice recipient contact person from the customer organization. This is the OrganizationMember ID who will receive invoices. This person is auto-filled when creating invoices but can be changed during invoice creation. Set to null if no default recipient is configured.
          example: 123e4567-e89b-12d3-a456-426614174001
          nullable: true
        billingProjectSnapshotId:
          type: object
          description: Project version snapshot ID used as the basis for main billing. This version includes components, manual items, and products as agreed with the customer. Only applicable for Single project type. The selected version determines what items and prices are included when generating invoices for the project.
          example: 123e4567-e89b-12d3-a456-426614174002
          nullable: true
        productsBillingProjectSnapshotId:
          type: object
          description: Project version snapshot ID used for billing subscriptions independently of the main project scope. This allows products with recurring payments to be billed separately using a different version snapshot. Only applicable for Single project type. If not set, the main billing version (billingProjectSnapshotId) is used automatically for subscriptions.
          example: 123e4567-e89b-12d3-a456-426614174003
          nullable: true
      required:
        - id
        - allowMixedBilling
        - bugsAreBillable
        - includeComponentsInBilling
        - invoiceContactId
        - billingProjectSnapshotId
        - productsBillingProjectSnapshotId
    PatchProjectBillingSettingsDto:
      type: object
      properties:
        allowMixedBilling:
          type: boolean
          description: 'Controls invoice structure: true = combined invoices (support and subscriptions on one invoice), false = split invoices (support invoices generated separately). Optional field - if not provided, current value remains unchanged.'
          example: false
        bugsAreBillable:
          type: boolean
          description: Determines whether time spent on tasks with "Bug" activity type can be billed to the customer. Optional field - if not provided, current value remains unchanged.
          example: false
        includeComponentsInBilling:
          type: boolean
          description: When true, component hours and amounts are included in estimates and invoices. When false, component totals are excluded. Optional field - if not provided, current value remains unchanged.
          example: false
        invoiceContactId:
          type: object
          description: Default invoice recipient contact person from the customer organization (OrganizationMember ID). This person is auto-filled when creating invoices but can be changed during invoice creation. Set to null to remove the default recipient. Optional field - if not provided, current value remains unchanged.
          example: 123e4567-e89b-12d3-a456-426614174001
          nullable: true
        billingProjectSnapshotId:
          type: object
          description: Project version snapshot ID used as the basis for main billing. Only applicable for Single project type. The selected version determines what items and prices are included when generating invoices. Optional field - if not provided, current value remains unchanged.
          example: 123e4567-e89b-12d3-a456-426614174002
          nullable: true
        productsBillingProjectSnapshotId:
          type: object
          description: Project version snapshot ID used for billing subscriptions independently of the main project scope. Only applicable for Single project type. If not set, the main billing version is used automatically. Optional field - if not provided, current value remains unchanged.
          example: 123e4567-e89b-12d3-a456-426614174003
          nullable: true
    CreateProjectComponentDto:
      type: object
      properties:
        name:
          type: string
          description: Component name
          example: Authentication Module
        description:
          type: string
          description: Component description in HTML or Markdown format
          example: <p>User authentication and authorization module</p>
        icon:
          type: object
          description: Icon identifier for the component
          example: icon-lock
        tags:
          description: Array of tag IDs to associate with the component
          example:
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note in HTML or Markdown format
          example: <p>This is an internal development note</p>
      required:
        - name
        - description
    CreateProjectComponentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the created component
          example: f1a2b3c4-d5e6-7890-abcd-ef1234567890
      required:
        - id
    ImportComponentsFromLibraryDto:
      type: object
      properties:
        componentIds:
          description: Array of library component IDs to import
          example:
            - e57dc37b-7693-4d06-b49c-17084b773aff
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
          type: array
          items:
            type: string
      required:
        - componentIds
    ImportComponentsFromLibraryResponseDto:
      type: object
      properties:
        ids:
          description: Array of imported component IDs
          example:
            - f1a2b3c4-d5e6-7890-abcd-ef1234567890
          type: array
          items:
            type: string
      required:
        - ids
    CreateTodoDto:
      type: object
      properties:
        title:
          type: string
          description: Todo title. Todos are checklist items used to track actionable tasks within component items. They are perfect for recurring processes, quality control checkpoints, and project preparation steps.
          example: Implement authentication with 2FA
        description:
          type: string
          description: Todo description in HTML or Markdown format. Todos are checklist items that can be checked off individually and support comments.
          example: <p>Add user login with two-factor authentication</p>
      required:
        - title
        - description
    UpdateTodoDto:
      type: object
      properties:
        title:
          type: string
          description: Todo title
          example: Implement authentication with 2FA
        description:
          type: string
          description: Todo description in HTML or Markdown format
          example: <p>Add user login with two-factor authentication</p>
    CreateQuestionOptionDto:
      type: object
      properties:
        title:
          type: string
          description: Option title
          example: Option A
        extraHours:
          type: number
          description: Extra hours for this option
          example: 2.5
      required:
        - title
    CreateComponentItemConditionDto:
      type: object
      properties:
        type:
          type: string
          description: 'Condition type determines what must be true for the condition to be met. Available types depend on the target element: QuestionAnswered, QuestionNotAnswered, QuestionAnswerContains, QuestionAnswerDoesNotContain, QuestionAnswerEqual, QuestionAnswerDoesNotEqual (for questions), TodoIsDone, TodoIsNotDone (for todos), TestCasePassed, TestCaseFailed (for test cases).'
          enum:
            - QuestionAnswered
            - QuestionNotAnswered
            - QuestionAnswerContains
            - QuestionAnswerDoesNotContain
            - QuestionAnswerEqual
            - QuestionAnswerDoesNotEqual
            - TodoIsDone
            - TodoIsNotDone
            - TestCasePassed
            - TestCaseFailed
          example: QuestionAnswered
        targetValue:
          type: string
          description: Target value to compare against
          example: 'Yes'
        targetQuestionId:
          type: string
          description: Target question ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetTodoId:
          type: string
          description: Target todo ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetTestCaseId:
          type: string
          description: Target test case ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        containsType:
          type: string
          description: Contains type for condition
          enum:
            - Some
            - All
          example: Some
      required:
        - type
    CreateProjectQuestionDto:
      type: object
      properties:
        title:
          type: string
          description: Question title/heading. Questions capture customer-specific requirements or project details. They make work packages flexible and reusable by allowing later individualization in projects.
          example: What is the expected performance?
        question:
          type: string
          description: The question text
          example: Please specify the expected response time
        description:
          type: string
          description: Question description (accepts HTML or Markdown, returns HTML)
          example: <p>This question helps us understand performance requirements.</p>
        type:
          type: string
          description: |-
            Question type determines what kind of answer is expected and how it is collected. Each type is suited for different data collection needs:
            - **ShortText**: Single-line text input for names, titles, keywords, or short answers
            - **Editor**: Formatted multi-line text for descriptions, detailed explanations, or free text answers
            - **Checkbox**: Yes/No selection or multiple checkbox options. Answers can automatically impact effort calculation and pricing if options have extraHours configured
            - **Radio**: Single choice from multiple predefined options. Answers can automatically impact effort calculation and pricing if options have extraHours configured
            - **Files**: Upload files such as logos, documents, graphics, or reference materials
            - **Datepicker**: Select a date for deadlines, delivery dates, or scheduling
            - **Multiplier**: Dynamic calculation based on quantity. The system calculates: Answer (quantity) × Duration per element = Total additional effort. Perfect for questions like "How many pages?" where effort scales with quantity
            - **Person**: Select one or more people from the project team or clients
          enum:
            - ShortText
            - Editor
            - Checkbox
            - Radio
            - Files
            - Datepicker
            - Multiplier
            - Person
          example: ShortText
        options:
          description: Array of answer options for Checkbox or Radio question types. Required for Checkbox/Radio types and must have at least 1 item. Each option can have an optional extraHours value that affects effort calculation when selected. For Checkbox questions, multiple options can be selected. For Radio questions, only one option can be selected.
          type: array
          items:
            $ref: '#/components/schemas/CreateQuestionOptionDto'
        multiplierValue:
          type: number
          description: 'Multiplier value (duration per element) for Multiplier question type. This represents the time needed for a single element (e.g., per page, per product, per user). When the question is answered with a quantity, the system calculates: Answer (quantity) × multiplierValue = Total additional effort in hours. For example, if multiplierValue is 2.5 and the answer is 10 pages, the total additional effort is 25 hours.'
          example: 1.5
        conditions:
          description: Array of conditions that control question visibility/behavior
          type: array
          items:
            $ref: '#/components/schemas/CreateComponentItemConditionDto'
      required:
        - title
        - question
        - description
        - type
    UpdateQuestionOptionDto:
      type: object
      properties:
        id:
          type: string
          description: Option ID - if provided, updates existing option; if omitted, creates new option
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        title:
          type: string
          description: Option title
          example: Option A
        extraHours:
          type: number
          description: Extra hours for this option
          example: 2.5
      required:
        - title
    UpdateComponentItemConditionDto:
      type: object
      properties:
        id:
          type: string
          description: Condition ID - if provided, updates existing condition; if omitted, creates new condition
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        type:
          type: string
          description: Condition type
          enum:
            - QuestionAnswered
            - QuestionNotAnswered
            - QuestionAnswerContains
            - QuestionAnswerDoesNotContain
            - QuestionAnswerEqual
            - QuestionAnswerDoesNotEqual
            - TodoIsDone
            - TodoIsNotDone
            - TestCasePassed
            - TestCaseFailed
          example: QuestionAnswered
        targetQuestionId:
          type: string
          description: Target question ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetTodoId:
          type: string
          description: Target todo ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetTestCaseId:
          type: string
          description: Target test case ID
          example: e57dc37b-7693-4d06-b49c-17084b773aff
        targetValue:
          type: string
          description: Target value to compare against
          example: 'Yes'
        containsType:
          type: string
          description: Contains type for condition
          enum:
            - Some
            - All
          example: Some
      required:
        - type
    UpdateProjectQuestionDto:
      type: object
      properties:
        title:
          type: string
          description: Question title/heading
          example: What is the expected performance?
        question:
          type: string
          description: The question text
          example: Please specify the expected response time
        description:
          type: string
          description: Question description (accepts HTML or Markdown, returns HTML)
          example: <p>This question helps us understand performance requirements.</p>
        type:
          type: string
          description: Question type
          enum:
            - ShortText
            - Editor
            - Checkbox
            - Radio
            - Files
            - Datepicker
            - Multiplier
            - Person
          example: ShortText
        options:
          description: Array of answer options (creates/updates/deletes based on presence of id). Required if type is updated to Checkbox/Radio.
          type: array
          items:
            $ref: '#/components/schemas/UpdateQuestionOptionDto'
        multiplierValue:
          type: number
          description: Multiplier value for calculations
          example: 1.5
        conditions:
          description: Array of conditions that control question visibility/behavior (creates/updates/deletes based on presence of id)
          type: array
          items:
            $ref: '#/components/schemas/UpdateComponentItemConditionDto'
    SortProjectQuestionsDto:
      type: object
      properties:
        newSortOrder:
          description: Array of question IDs in new order. All questions for the item must be included.
          example:
            - f68ed48c-8704-5e17-c5ad-28195c884b00
            - a47dc37b-7693-4d06-b49c-17084b773aff
          type: array
          items:
            type: string
      required:
        - newSortOrder
    AnswerProjectQuestionDto:
      type: object
      properties:
        shortTextAnswer:
          type: string
          description: Short text answer for ShortText question type
          example: This is my answer
        editorAnswer:
          type: string
          description: Rich text answer for Editor question type (HTML or Markdown format)
          example: <p>This is a <strong>rich text</strong> answer</p>
        fileUrls:
          description: Array of file URLs for File question type
          example:
            - https://example.com/file1.pdf
            - https://example.com/file2.jpg
          type: array
          items:
            type: string
        dateAnswer:
          type: string
          description: Date answer for Datepicker question type (ISO 8601 format)
          example: '2024-12-31T00:00:00.000Z'
        selectedOptions:
          description: Array of selected option IDs for Checkbox question type (multiple selections)
          example:
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
            - b2c3d4e5-f6g7-8901-bcde-f12345678901
          type: array
          items:
            type: string
        selectedOption:
          type: string
          description: Single selected option ID for Radio question type
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        multiplierAnswer:
          type: number
          description: Numeric multiplier answer for Number question type
          example: 2.5
        selectedPersonId:
          description: Array of person IDs for Person question type
          example:
            - e57dc37b-7693-4d06-b49c-17084b773aff
          type: array
          items:
            type: string
    CreateTestCaseDto:
      type: object
      properties:
        title:
          type: string
          description: Test case title. Test cases are individual test steps within a Test Suite. They document formal review and approval criteria for project results.
          example: Test login with valid credentials
        description:
          type: string
          description: Test case description in HTML or Markdown format
          example: <p>Verify that users can log in with valid credentials.</p>
        steps:
          type: string
          description: Test steps in HTML or Markdown format. Provide step-by-step instructions for running the test (e.g., "1. Navigate to login page 2. Enter credentials 3. Click login").
          example: <ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>
        expectedResult:
          type: string
          description: Expected result in HTML or Markdown format. Describes the expected or correct system behavior when the test is run successfully.
          example: <p>User is successfully logged in and redirected to dashboard.</p>
      required:
        - title
        - description
        - steps
        - expectedResult
    UpdateTestCaseDto:
      type: object
      properties:
        title:
          type: string
          description: Test case title
          example: Test login with valid credentials
        description:
          type: string
          description: Test case description in HTML or Markdown format
          example: <p>Verify that users can log in with valid credentials.</p>
        steps:
          type: string
          description: Test steps in HTML or Markdown format
          example: <ol><li>Navigate to login page</li><li>Enter credentials</li><li>Click login button</li></ol>
        expectedResult:
          type: string
          description: Expected result in HTML or Markdown format
          example: <p>User is successfully logged in and redirected to dashboard.</p>
    EvaluateTestCaseDto:
      type: object
      properties:
        status:
          type: string
          description: Test status
          enum:
            - Passed
            - PassedWithReservations
            - Failed
          example: Passed
        testComment:
          type: string
          description: Test comment in HTML or Markdown format (required if status is not Passed)
          example: <p>Found an issue with the login validation.</p>
      required:
        - status
    GenerateTasksFromItemsDto:
      type: object
      properties:
        itemType:
          type: string
          description: Type of item to generate tasks from
          example: ComponentItem
          enum:
            - Component
            - ComponentItem
      required:
        - itemType
    GenerateTasksFromItemsResponseDto:
      type: object
      properties:
        createdTasksCount:
          type: number
          description: Number of tasks created
          example: 5
        componentItemsCount:
          type: number
          description: Total number of component items processed
          example: 8
        tasksNumbers:
          description: Array of task short numbers for created tasks
          example:
            - 123
            - 124
            - 125
            - 126
            - 127
          type: array
          items:
            type: number
      required:
        - createdTasksCount
        - componentItemsCount
        - tasksNumbers
    DisconnectItemFromTaskDto:
      type: object
      properties:
        deleteTask:
          type: boolean
          description: Whether to delete the associated task(s) after disconnecting
          example: false
      required:
        - deleteTask
    PatchProjectComponentDto:
      type: object
      properties:
        name:
          type: string
          description: Component name
          example: User Authentication Module
        description:
          type: string
          description: Component description (HTML or Markdown)
          example: <p>This component handles user authentication and authorization.</p>
        icon:
          type: object
          description: Icon identifier (can be set to null)
          example: 🔐
          nullable: true
        tags:
          description: Array of tag IDs
          example:
            - 550e8400-e29b-41d4-a716-446655440000
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note (HTML or Markdown)
          example: <p>Implementation notes for the development team.</p>
        discountAmount:
          type: number
          description: Discount amount to apply to this component. For percentage discounts, this is the percentage (0-100). For fixed discounts, this is the amount in workspace currency. Set to 0 to remove the discount.
          example: 10
          minimum: 0
        discountType:
          type: string
          description: Type of discount. "Percentage" applies a percentage reduction, "Fixed" applies a fixed amount reduction. Required when discountAmount is provided.
          enum:
            - Fixed
            - Percentage
          example: Percentage
    UpdateProjectComponentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the updated component
          example: f1a2b3c4-d5e6-7890-abcd-ef1234567890
      required:
        - id
    ToggleTodoDto:
      type: object
      properties:
        isDone:
          type: boolean
          description: Whether the todo should be marked as done (true) or not done (false)
          example: true
      required:
        - isDone
    SortTodosDto:
      type: object
      properties:
        newSortOrder:
          description: Array of todo item IDs in the desired sort order
          example:
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
            - b2c3d4e5-f6g7-8901-bcde-f12345678901
          type: array
          items:
            type: string
      required:
        - newSortOrder
    SortTestCasesDto:
      type: object
      properties:
        newSortOrder:
          description: Array of test case IDs in desired order
          example:
            - e57dc37b-7693-4d06-b49c-17084b773aff
            - a1b2c3d4-e5f6-7890-abcd-ef1234567890
          type: array
          items:
            type: string
      required:
        - newSortOrder
    ExportComponentToLibraryDto:
      type: object
      properties:
        name:
          type: string
          description: Custom name for the library component. If not provided, uses original component name.
          example: Authentication Module Template
    ExportComponentToLibraryResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the created library component
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
      required:
        - id
    PatchComponentItemConditionDto:
      type: object
      properties:
        id:
          type: string
          description: Condition ID for updates. If not provided, creates new condition
          example: 550e8400-e29b-41d4-a716-446655440000
        type:
          type: string
          description: Condition type. Required if creating new condition
          example: QuestionAnswered
          enum:
            - QuestionAnswered
            - QuestionNotAnswered
            - QuestionAnswerContains
            - QuestionAnswerDoesNotContain
            - QuestionAnswerEqual
            - QuestionAnswerDoesNotEqual
            - TodoIsDone
            - TodoIsNotDone
            - TestCasePassed
            - TestCaseFailed
        targetValue:
          type: string
          description: Target value for condition
          example: 'Yes'
        targetQuestionId:
          type: string
          description: Target question ID
          example: 550e8400-e29b-41d4-a716-446655440000
        targetTodoId:
          type: string
          description: Target todo ID
          example: 550e8400-e29b-41d4-a716-446655440000
        targetTestCaseId:
          type: string
          description: Target test case ID
          example: 550e8400-e29b-41d4-a716-446655440000
        containsType:
          type: string
          description: Contains type for condition
          example: Some
          enum:
            - Some
            - All
    PatchComponentItemDto:
      type: object
      properties:
        componentId:
          type: string
          description: ID of the component this item belongs to
          example: 550e8400-e29b-41d4-a716-446655440000
        parentId:
          type: string
          description: ID of parent item for nested items
          example: 550e8400-e29b-41d4-a716-446655440000
        type:
          type: string
          description: Type of item (Epic, Feature, Task, etc.)
          example: WorkPackage
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
        name:
          type: string
          description: Name of the item
          example: User Authentication Feature
        icon:
          type: object
          description: Icon identifier (can be set to null)
          example: 🔐
          nullable: true
        timeFrame:
          type: number
          description: Time frame in hours. Required for all types except Epic (when type is provided)
          example: 40
        description:
          type: string
          description: Description in HTML or Markdown format (will be converted to IDoc if provided)
          example: <p>Implement user authentication and authorization</p>
        tags:
          description: Array of tag UUIDs
          example:
            - 550e8400-e29b-41d4-a716-446655440000
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note in HTML or Markdown format (will be converted to IDoc if provided)
          example: <p>Implementation notes for the development team</p>
        includeInSpecification:
          type: boolean
          description: Whether to include in specification
          example: true
        conditions:
          description: Array of conditions (full array replaces existing conditions)
          type: array
          items:
            $ref: '#/components/schemas/PatchComponentItemConditionDto'
    ConnectItemToTaskDto:
      type: object
      properties:
        taskId:
          type: object
          description: Task ID to connect the item to. Pass null or omit to disconnect the item from any existing task.
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          nullable: true
    AddTodoCommentDto:
      type: object
      properties:
        todoComment:
          type: string
          description: Todo comment content in HTML or Markdown format. Will be converted to internal IDoc format.
          example: <p>This is a comment on the todo item</p>
      required:
        - todoComment
    CreateComponentItemDto:
      type: object
      properties:
        componentId:
          type: string
          description: ID of the component this item belongs to
          example: 550e8400-e29b-41d4-a716-446655440000
        parentId:
          type: string
          description: ID of parent item for nested items
          example: a47dc37b-7693-4d06-b49c-17084b773aff
          nullable: true
        type:
          type: string
          description: |-
            Type of component item. Items are the building blocks within components that structure project work:
            - **Epic**: Major sections or phases that group related work packages around a common theme (e.g., "Technical Implementation", "Design & Development"). Epics help track project progress at a strategic level and can nest other items.
            - **WorkPackage**: Self-contained, clearly defined tasks that deliver tangible results (e.g., "Set up hosting", "Design homepage layout"). Work packages can have timeframes, questions, todos, and test cases attached.
            - **TodoList**: Checklists of sub-steps or checkpoints perfect for recurring processes, quality control, or project preparation (e.g., "Kickoff Checklist", "Quality Control"). Each todo item can be checked off individually.
            - **TestSuite**: Formal acceptance tests for project results (e.g., "Contact Form Tests", "SEO Review"). Contains multiple test cases with steps and expected results. The project is considered finished when all test cases pass.
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
          example: WorkPackage
        name:
          type: string
          description: Name of the item
          example: User Authentication Feature
        icon:
          type: object
          description: Icon identifier
          example: faCheckSquare
          nullable: true
        timeFrame:
          type: number
          description: Time frame in hours representing the estimated effort for completing this item. Required for all item types except Epic. For Work Packages, this represents the estimated time to complete the task. For Todo Lists, this is the estimated time to complete the entire checklist. For Test Suites, this is the estimated time to run all test cases.
          example: 20
        description:
          type: string
          description: Description in HTML or Markdown format (will be converted to IDoc)
          example: <p>Implement user authentication with OAuth 2.0</p>
        tags:
          description: Array of tag UUIDs
          example:
            - 550e8400-e29b-41d4-a716-446655440020
          type: array
          items:
            type: string
        internalNote:
          type: string
          description: Internal note in HTML or Markdown format (will be converted to IDoc)
          example: <p>Technical implementation details</p>
        includeInSpecification:
          type: boolean
          description: Whether to include in specification
          example: true
        conditions:
          description: Array of conditions that control when this item is visible or active. Conditions link items to questions, todos, or test cases. An item is only shown when all its conditions are met. For example, a work package can be hidden until a specific question is answered a certain way, or a todo list can appear only after a test case passes. Conditions enable dynamic, context-dependent project structures that adapt based on customer answers and project progress.
          type: array
          items:
            $ref: '#/components/schemas/CreateComponentItemConditionDto'
      required:
        - componentId
        - type
        - name
        - description
        - includeInSpecification
    CreateComponentItemResponseDto:
      type: object
      properties:
        id:
          type: string
          description: ID of the created component item
          example: 550e8400-e29b-41d4-a716-446655440001
      required:
        - id
    ProjectDocumentationResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the documentation article (UUID format)
          example: 550e8400-e29b-41d4-a716-446655440000
        projectId:
          type: string
          description: ID of the project this documentation article belongs to (UUID format)
          example: 550e8400-e29b-41d4-a716-446655440000
        title:
          type: string
          description: Title of the documentation article. Should be clear and descriptive to help team members find the article quickly (e.g., "Access to production system", "Prioritizing support cases")
          example: Getting Started Guide
        content:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <h1>Introduction</h1><p>This is a documentation article with <strong>formatting</strong>.</p>
        userId:
          type: string
          description: User ID of the person who created this documentation article (UUID format). This is automatically set to the authenticated user when the article is created.
          example: 550e8400-e29b-41d4-a716-446655440000
        createdAt:
          type: string
          description: ISO 8601 timestamp indicating when the article was created
          example: '2024-01-15T10:30:00Z'
        updatedAt:
          type: string
          description: ISO 8601 timestamp indicating when the article was last updated. This timestamp is automatically updated whenever the article is modified.
          example: '2024-01-15T10:30:00Z'
      required:
        - id
        - projectId
        - title
        - content
        - userId
        - createdAt
        - updatedAt
    CreateProjectDocumentationDto:
      type: object
      properties:
        projectId:
          type: string
          description: ID of the project this documentation article belongs to. Must be a valid UUID of an existing project in the workspace.
          example: 550e8400-e29b-41d4-a716-446655440000
        title:
          type: string
          description: 'Title of the documentation article. Should be clear and descriptive to help team members find the article quickly. Examples: "Access to production system", "Prioritizing support cases", "Deployment procedures".'
          example: Getting Started Guide
        content:
          type: string
          description: |
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
          example: <h1>Introduction</h1><p>Content with <strong>formatting</strong>.</p>
      required:
        - projectId
        - title
        - content
    PatchProjectDocumentationDto:
      type: object
      properties:
        projectId:
          type: string
          description: ID of the project this documentation article belongs to. If provided, the article will be moved to the specified project. Must be a valid UUID of an existing project in the workspace.
          example: 550e8400-e29b-41d4-a716-446655440000
        title:
          type: string
          description: Title of the documentation article. If provided, only the title will be updated. Should be clear and descriptive.
          example: Getting Started Guide
        content:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **codeBlock**
            Type: block
            Content: text* (text only (plus marks if applicable), no block wrappers)
            Atts:
            - language: null
            ```html
            <pre><code class="language-null/some-value"></code></pre>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            **entity**
            Type: inline
            Inline link chip to a Leadtime entity (task, project, etc.), usually from the !-picker or by pasting a leadtime link. Display text is #title; data-type is "entity".
            - entityId: Stable id of the target entity (e.g. task or project id). Use ids from the Public API or the user’s context; do not fabricate ids.
            - type: What kind of entity is linked (e.g. task, project) — must be consistent with entityId in your workspace.
            - title: Shown as the visible #label after the chip; should match the real entity title.
            - data: Optional extra JSON the client provides (may be empty). Do not put secrets in HTML.
            If the user has not given a real entity, use the API to search or create—do not guess UUIDs or labels.
            Atts:
            - entityId: null
            - type: null
            - title: null
            - data: {}
            ```html
            <span data-type="entity" entityid="null/some-value" type="null/some-value" title="null/some-value" data="[object Object]">#null/some-value</span>
            ```

            **appFile**
            Type: block
            Block for a file attachment that already exists in the workspace.
            - fileId: Id from Leadtime file storage. Must be real; do not make up.
            - filename and size: Should match the stored file. Use the app upload or API to get a file id; do not embed arbitrary ids.
            Atts:
            - fileId: 
            - filename: 
            - size: 0
            ```html
            <div data-type="appFile" fileid="" filename="" size="0"></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **mention**
            Type: inline
            Mentions a workspace member (inserted in the app via the @-picker). The visible label is @name.
            - id: Leadtime user/employee id for the mentioned person. Must match a real user in the workspace; do not invent ids—resolve people from context or the Public API.
            - name: Display name for the @mention label. Should match the user shown for id.
            HTML must keep data-type="mention" with data-id and data-name in sync; plain text in the node is the visible @name.
            Atts:
            - id: null
            - name: null
            ```html
            <span data-type="mention" data-name="null/some-value" data-id="null/some-value" id="null/some-value" name="null/some-value">@null/some-value</span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **editorPlaceholder**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Used in task-type / template fields as a single editable “field” slot. Renders as a <placeholder> block with inline* content (label/instructions) inside. Prefer normal paragraph nodes for free-form user content; use this when reproducing a structured template the user is editing, not for generic text.
            Atts: none
            ```html
            <placeholder></placeholder>
            ```

            **appVideo**
            Type: block
            Block for a workspace video file already uploaded in Leadtime (not a generic external embed).
            - fileId: Id of the stored file. Must be a file that actually exists in the workspace after upload; do not fabricate.
            - filename: Display name; should match the file.
            - width, align, size: Layout hints as in the editor; keep realistic values. If you do not have a file id, use the file upload or Public API first—do not guess ids.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appVideo" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **embedVideo**
            Type: block
            Embeds a third-party video in an iframe. The div carries data-video-embed with data-embed-url (iframe src) and data-original-url (canonical page URL).
            - embedUrl: Must be a real embed/iframe URL (e.g. YouTube embed) that matches originalUrl.
            - originalUrl: Human-facing link to the video page; keep it in sync with embedUrl. Do not leave placeholder URLs in final HTML if you claim a real video.
            Atts:
            - originalUrl: 
            - embedUrl: 
            ```html
            <div originalurl="" embedurl="" data-video-embed="" data-original-url="" data-embed-url=""><iframe src=""></iframe></div>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             If provided, the entire article content will be replaced with this new content.
          example: <h1>Introduction</h1><p>Content with <strong>formatting</strong>.</p>
    ProjectDocumentUserResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the user. Use this ID when assigning the user as a contact person in document creation/update requests.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Full name of the user (first name + last name). Displayed in contact person selection lists and document headers.
          example: John Doe
        position:
          type: string
          description: Job title or position of the user within their organization. Displayed alongside the name in contact person selection lists.
          example: Project Manager
        avatarId:
          type: object
          description: File ID of the user avatar/profile picture. Null if no avatar is set. Can be used to display user avatars in UI.
          example: 123e4567-e89b-12d3-a456-426614174000
      required:
        - id
        - name
        - position
    ProjectDocumentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the document. Used to reference the document in subsequent API calls.
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: object
          description: The title/name of the document. This appears in document lists, headers, and exports. Null for Letter-style Estimate documents (title is not used in letter format).
          example: Q4 2024 Project Estimate
        description:
          type: object
          description: 'Document description or introduction text, returned as HTML (converted from internal IDoc format). Contains rich formatting: headings, lists, tables, links. Null if no description was provided.'
          example: <p>Quarterly estimate for client approval</p>
        projectId:
          type: string
          description: The ID of the project this document belongs to. Documents are always associated with a specific project.
          example: 123e4567-e89b-12d3-a456-426614174000
        snapshotId:
          type: string
          description: The project version (snapshot) ID that this document is based on. This ensures the document content is tied to a specific project state for traceability and audit purposes.
          example: 123e4567-e89b-12d3-a456-426614174000
        contactUserId:
          type: string
          description: The user ID of the contact person assigned to this document. This person appears as the addressed recipient in the document header and recipient fields. Empty string if no contact person is assigned.
          example: 123e4567-e89b-12d3-a456-426614174000
        type:
          type: string
          description: Document type
          enum:
            - Estimate
            - Specification
            - CustomEditor
          example: Estimate
        status:
          type: string
          description: Document status
          enum:
            - Draft
            - Final
            - WaitingForApproval
            - Accepted
            - Rejected
            - Parked
          example: Draft
        includeToc:
          type: boolean
          description: Include table of contents
          example: true
        indicationMode:
          type: boolean
          description: Enable indication mode
          example: false
        titlePage:
          type: boolean
          description: Include title page
          example: true
        headingStyle:
          type: string
          description: Heading style for document
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
        customEditorContent:
          type: object
          description: 'Custom document content for CustomEditor document type, returned as HTML (converted from internal IDoc format). Contains rich formatting: headings, lists, tables, links, variables, conditional sections. Null if no custom content was provided.'
          example: <h1>Custom Document Content</h1><p>Details here...</p>
        customVariables:
          description: Array of custom variables with their current values. Each variable includes its definition (name, type, description, options) and the current value formatted according to the variable type. Empty array if no custom variables are defined.
          type: array
          items:
            $ref: '#/components/schemas/DocumentCustomVariableDto'
        createdAt:
          format: date-time
          type: string
          description: ISO 8601 timestamp indicating when the document was created. Used for sorting, filtering, and audit trails.
          example: '2024-01-15T10:30:00Z'
        offerNumber:
          type: object
          description: 'Auto-generated offer number (integer). Display format is computed as: [ProjectShortcode]-v[Version]-[DDMMYYYY]-[OfferNumber]. Only populated for Estimate type documents.'
          example: 1
        formattedOfferNumber:
          type: object
          description: 'Fully formatted offer number string. Format: [ProjectShortcode]-v[Version]-[DDMMYYYY]-[OfferNumber]. Example: WKM-1-v2-27012026-1. Only populated for Estimate type documents.'
          example: WKM-1-v2-27012026-1
        validUntil:
          type: object
          description: The date until which this offer is valid. Used for Letter-style offers to indicate expiration date. Null if not set.
          example: '2024-02-15T00:00:00Z'
        language:
          type: object
          description: Document language override (BCP 47 tag). Null when using workspace/organization default.
          example: de
        documentStyle:
          type: string
          description: Document layout style
          enum:
            - Letter
            - Document
          example: Letter
        breakdownLevel:
          type: string
          description: Component breakdown level
          enum:
            - Complete
            - UpToEpics
            - NoBreakdown
          example: Complete
        introText:
          type: object
          description: Custom introduction text for the offer, returned as HTML (converted from internal IDoc format). Null if not set.
          example: <p>Thank you for your inquiry. We are pleased to submit the following offer:</p>
        outroText:
          type: object
          description: Custom closing text for the offer, returned as HTML (converted from internal IDoc format). Null if not set.
          example: <p>This offer is non-binding. We thank you for your interest.</p>
      required:
        - id
        - projectId
        - snapshotId
        - contactUserId
        - type
        - status
        - includeToc
        - indicationMode
        - titlePage
        - customVariables
        - createdAt
        - documentStyle
        - breakdownLevel
    CreateProjectDocumentDto:
      type: object
      properties:
        title:
          type: object
          description: The title/name of the document. This appears in document lists, headers, and exports. Should be descriptive and unique enough to identify the document. For Letter-style Estimate documents, this field is optional and will be automatically cleared (title is not used in letter format).
          example: Q4 2024 Project Estimate
        description:
          type: string
          description: 'Optional document description or introduction text. Accepts HTML or Markdown format - will be automatically converted to internal IDoc format for storage. Supports rich formatting: headings, lists, tables, links, bold, italic. When retrieved via GET, content is returned as HTML for easy display.'
          example: <p>Quarterly estimate for client approval</p>
        snapshotId:
          type: string
          description: The project version (snapshot) ID that this document is based on. Documents are always linked to a specific project version for traceability and audit purposes. Use GET /projects/:id/versions endpoint to retrieve available versions for a project. The snapshot must exist and belong to the project.
          example: 123e4567-e89b-12d3-a456-426614174000
        contactUserId:
          type: string
          description: The user ID of the contact person who will receive this document. This person appears as the addressed recipient in the document header and recipient fields. Must be from the list of available contact persons for this project (use GET /projects/:id/documents/contact-persons). For external projects, must be an organization member. For internal projects, must be a workspace employee.
          example: 123e4567-e89b-12d3-a456-426614174000
        type:
          type: string
          description: 'The type/category of document. Estimate: Commercial proposals with services, quantities, and costs. Specification: Detailed requirement documents describing deliverables. CustomEditor: Free-form documents like contracts, NDAs, or custom agreements that can use templates with variables.'
          enum:
            - Estimate
            - Specification
            - CustomEditor
          example: Estimate
        status:
          type: string
          description: 'The workflow status of the document. Draft: Initial version, not yet finalized. WaitingForApproval: Sent to client for review. Final: Ready for signing. Accepted: Approved by client. Rejected: Declined by client. Parked: Temporarily set aside. Documents typically start as Draft and progress through the workflow.'
          enum:
            - Draft
            - Final
            - WaitingForApproval
            - Accepted
            - Rejected
            - Parked
          example: Draft
        includeToc:
          type: boolean
          description: Whether to include an automatically generated table of contents in the document. The TOC is built from document headings and appears at the beginning of the document. Useful for longer documents with multiple sections. Defaults to true.
          example: true
          default: true
        indicationMode:
          type: boolean
          description: When enabled, marks the document as a non-binding cost indication or preliminary estimate. This is important for legal purposes to distinguish between binding offers and preliminary estimates. When false, the document is treated as a binding offer. Defaults to false.
          example: false
          default: false
        titlePage:
          type: boolean
          description: Whether to include a title page at the beginning of the document. The title page typically includes document title, project information, contact details, and creation date. Useful for formal documents and exports. Defaults to true.
          example: true
          default: true
        headingStyle:
          type: string
          description: 'Controls how headings are automatically numbered in the document. Normal: No automatic numbering. Sequential: Numbered from level 1 (1, 1.1, 1.1.1). SequentialFromSecondLevel: Numbering starts from level 2 (H1 unnumbered, then 1.1, 1.1.1). SequentialWithParagraphs: Same as SequentialFromSecondLevel but with paragraph sign prefix (§ 1, § 1.1). Important for legal documents and contracts. Numbering is dynamic and adjusts when conditional sections are shown/hidden.'
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
        customEditorContent:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your document templates. Variables are represented as HTML spans:

            **workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`
            **company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`
            **organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`
            **project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`
            **currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`
            **recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`
            **macros**: `macros.todaysDate`, `macros.dearCustomer`

            **Example HTML**: `<span data-type="variable" data-id="currentUser.firstName">John</span>`, `<span data-type="variable" data-id="project.name">Website Redesign</span>`
          example: <h1>Custom Document Content</h1><p>Details here...</p>
        customVariables:
          description: 'Array of custom variables defined for this document. Custom variables allow you to add project- or template-specific values that are not covered by standard system variables (like client name, project budget, contract duration, etc.). Each variable has a name, type, value, and optional description. Variables can be referenced in document templates using the syntax: custom.variable_name. If the document template includes custom variables, you must provide values for all required variables.'
          type: array
          items:
            $ref: '#/components/schemas/DocumentCustomVariableDto'
        validUntil:
          format: date-time
          type: string
          description: The date until which this offer is valid. Used for Letter-style offers to indicate expiration date. Defaults to one month from creation date if not specified.
          example: '2024-02-15T00:00:00Z'
        documentStyle:
          type: string
          description: 'The document layout style. Letter: Invoice-like layout suitable for direct mailing. Document: Traditional multi-page document with TOC. Only applies to Estimate type documents. Defaults to Letter for new documents.'
          enum:
            - Letter
            - Document
          example: Letter
        breakdownLevel:
          type: string
          description: 'Controls how project components are broken down in the document. Complete: Shows all levels (Components → Epics → Work Packages). UpToEpics: Shows Components with totals and Epics with their effort. NoBreakdown: Shows only Components with total price/hours. Applies to both Letter and Document styles.'
          enum:
            - Complete
            - UpToEpics
            - NoBreakdown
          example: Complete
        introText:
          type: string
          description: Custom introduction text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.
          example: <p>Thank you for your inquiry. We are pleased to submit the following offer:</p>
        outroText:
          type: string
          description: Custom closing text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.
          example: <p>This offer is non-binding. We thank you for your interest.</p>
        language:
          type: object
          description: BCP 47 language tag for generated document text (e.g. de, en). When omitted or null, workspace/organization defaults apply.
          example: de
      required:
        - snapshotId
        - contactUserId
        - type
        - status
    PatchProjectDocumentDto:
      type: object
      properties:
        title:
          type: object
          description: The title/name of the document. This appears in document lists, headers, and exports. Should be descriptive and unique enough to identify the document. For Letter-style Estimate documents, this field is optional and will be automatically cleared (title is not used in letter format).
          example: Q4 2024 Project Estimate
        description:
          type: string
          description: 'Optional document description or introduction text. Accepts HTML or Markdown format - will be automatically converted to internal IDoc format for storage. Supports rich formatting: headings, lists, tables, links, bold, italic. When retrieved via GET, content is returned as HTML for easy display.'
          example: <p>Quarterly estimate for client approval</p>
        snapshotId:
          type: string
          description: The project version (snapshot) ID that this document is based on. Documents are always linked to a specific project version for traceability and audit purposes. Use GET /projects/:id/versions endpoint to retrieve available versions for a project. The snapshot must exist and belong to the project.
          example: 123e4567-e89b-12d3-a456-426614174000
        contactUserId:
          type: string
          description: The user ID of the contact person who will receive this document. This person appears as the addressed recipient in the document header and recipient fields. Must be from the list of available contact persons for this project (use GET /projects/:id/documents/contact-persons). For external projects, must be an organization member. For internal projects, must be a workspace employee.
          example: 123e4567-e89b-12d3-a456-426614174000
        type:
          type: string
          description: 'The type/category of document. Estimate: Commercial proposals with services, quantities, and costs. Specification: Detailed requirement documents describing deliverables. CustomEditor: Free-form documents like contracts, NDAs, or custom agreements that can use templates with variables.'
          enum:
            - Estimate
            - Specification
            - CustomEditor
          example: Estimate
        status:
          type: string
          description: 'The workflow status of the document. Draft: Initial version, not yet finalized. WaitingForApproval: Sent to client for review. Final: Ready for signing. Accepted: Approved by client. Rejected: Declined by client. Parked: Temporarily set aside. Documents typically start as Draft and progress through the workflow.'
          enum:
            - Draft
            - Final
            - WaitingForApproval
            - Accepted
            - Rejected
            - Parked
          example: Draft
        includeToc:
          type: boolean
          description: Whether to include an automatically generated table of contents in the document. The TOC is built from document headings and appears at the beginning of the document. Useful for longer documents with multiple sections. Defaults to true.
          example: true
          default: true
        indicationMode:
          type: boolean
          description: When enabled, marks the document as a non-binding cost indication or preliminary estimate. This is important for legal purposes to distinguish between binding offers and preliminary estimates. When false, the document is treated as a binding offer. Defaults to false.
          example: false
          default: false
        titlePage:
          type: boolean
          description: Whether to include a title page at the beginning of the document. The title page typically includes document title, project information, contact details, and creation date. Useful for formal documents and exports. Defaults to true.
          example: true
          default: true
        headingStyle:
          type: string
          description: 'Controls how headings are automatically numbered in the document. Normal: No automatic numbering. Sequential: Numbered from level 1 (1, 1.1, 1.1.1). SequentialFromSecondLevel: Numbering starts from level 2 (H1 unnumbered, then 1.1, 1.1.1). SequentialWithParagraphs: Same as SequentialFromSecondLevel but with paragraph sign prefix (§ 1, § 1.1). Important for legal documents and contracts. Numbering is dynamic and adjusts when conditional sections are shown/hidden.'
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
        customEditorContent:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            **pageBreak**
            Type: block
            Page break for PDF/print output (void <page-break> element). Inserts a hard page break when exporting. Use sparingly where the user asked for a new printed page, not for normal on-screen line breaks (use hardBreak/paragraphs instead).
            Atts: none
            ```html
            <page-break></page-break>
            ```

            **condition**
            Type: block
            Content: conditionBody (a single condition body block)
            This node conditionally renders its content based on a set of rules. The rules are defined in the 'conditions' attribute. 
            'conditions' is an object like: { type: 'and' | 'or', expressions: Array<Expression | ConditionGroup> }.
            An 'Expression' is { field: string, operator: string, value: any }.
            A 'ConditionGroup' is a nested { type: 'and' | 'or', expressions: [...] }.
            Example for an expression: { field: 'invoice.total', operator: 'gt', value: 100 } means 'if invoice total is greater than 100'.
            Supported operators typically include: eq (equals), neq (not equals), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), contains, in, is_true, is_false, etc. Check editor UI for available fields and operators.
            ALL FIELDS CAN ONLY BE EXISTING VARIABLES. DO NOT INVENT NEW FIELDS.
            Atts:
            - conditions: {
              "type": "and",
              "expressions": []
            }
            ```html
            <condition conditions="{&quot;type&quot;:&quot;and&quot;,&quot;expressions&quot;:[]}"><condition-body><p class="paragraph-base"></p></condition-body></condition>
            ```

            **conditionBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <condition-body><p class="paragraph-base"></p></condition-body>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your document templates. Variables are represented as HTML spans:

            **workspace**: `workspace.vat`, `workspace.hourRate`, `workspace.invoiceDaysTillOverdue`, `workspace.standardReminderFee`, `workspace.baseInterestRate`, `workspace.interestRate`
            **company**: `company.companyName`, `company.legalForm`, `company.country`, `company.zip`, `company.city`, `company.street`, `company.houseNumber`, `company.phone`, `company.email`, `company.website`, `company.fax`, `company.taxNumber`, `company.registrationNumber`, `company.registrationCourt`
            **organization**: `organization.companyName`, `organization.shortName`, `organization.legalForm`, `organization.description`, `organization.country`, `organization.zip`, `organization.city`, `organization.street`, `organization.houseNumber`, `organization.phone`, `organization.email`, `organization.website`, `organization.fax`, `organization.taxNumber`, `organization.registrationNumber`, `organization.registrationCourt`, `organization.hourRate`, `organization.invoiceDaysTillOverdue`, `organization.standardReminderFee`, `organization.baseInterestRate`, `organization.interestRate`
            **project**: `project.name`, `project.category`, `project.status`, `project.defaultAccountable`, `project.country`, `project.projectId`, `project.activeVersionId`, `project.activeVersionName`
            **currentUser**: `currentUser.title`, `currentUser.degree`, `currentUser.email`, `currentUser.firstName`, `currentUser.lastName`, `currentUser.country`, `currentUser.zip`, `currentUser.city`, `currentUser.street`, `currentUser.houseNr`, `currentUser.birthday`, `currentUser.phone`, `currentUser.signature`
            **recipient**: `recipient.firstName`, `recipient.lastName`, `recipient.position`, `recipient.gender`, `recipient.title`, `recipient.degree`, `recipient.birthdate`, `recipient.activeVersionName`, `recipient.country`, `recipient.zip`, `recipient.city`, `recipient.street`, `recipient.houseNumber`, `recipient.phone`, `recipient.email`, `recipient.socialNetwork`
            **macros**: `macros.todaysDate`, `macros.dearCustomer`

            **Example HTML**: `<span data-type="variable" data-id="currentUser.firstName">John</span>`, `<span data-type="variable" data-id="project.name">Website Redesign</span>`
          example: <h1>Custom Document Content</h1><p>Details here...</p>
        customVariables:
          description: 'Array of custom variables defined for this document. Custom variables allow you to add project- or template-specific values that are not covered by standard system variables (like client name, project budget, contract duration, etc.). Each variable has a name, type, value, and optional description. Variables can be referenced in document templates using the syntax: custom.variable_name. If the document template includes custom variables, you must provide values for all required variables.'
          type: array
          items:
            $ref: '#/components/schemas/DocumentCustomVariableDto'
        validUntil:
          format: date-time
          type: string
          description: The date until which this offer is valid. Used for Letter-style offers to indicate expiration date. Defaults to one month from creation date if not specified.
          example: '2024-02-15T00:00:00Z'
        documentStyle:
          type: string
          description: 'The document layout style. Letter: Invoice-like layout suitable for direct mailing. Document: Traditional multi-page document with TOC. Only applies to Estimate type documents. Defaults to Letter for new documents.'
          enum:
            - Letter
            - Document
          example: Letter
        breakdownLevel:
          type: string
          description: 'Controls how project components are broken down in the document. Complete: Shows all levels (Components → Epics → Work Packages). UpToEpics: Shows Components with totals and Epics with their effort. NoBreakdown: Shows only Components with total price/hours. Applies to both Letter and Document styles.'
          enum:
            - Complete
            - UpToEpics
            - NoBreakdown
          example: Complete
        introText:
          type: string
          description: Custom introduction text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.
          example: <p>Thank you for your inquiry. We are pleased to submit the following offer:</p>
        outroText:
          type: string
          description: Custom closing text for the offer, in HTML or Markdown format. Overrides workspace/organization defaults. Supports rich formatting and template variables. When retrieved via GET, content is returned as HTML.
          example: <p>This offer is non-binding. We thank you for your interest.</p>
        language:
          type: object
          description: BCP 47 language tag for generated document text (e.g. de, en). When omitted or null, workspace/organization defaults apply.
          example: de
    ProjectInterimPaymentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the interim payment (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        projectId:
          type: string
          description: ID of the project this payment belongs to (UUID)
          example: 550e8400-e29b-41d4-a716-446655440001
        title:
          type: string
          description: Short label or title for the payment (e.g., "Prepayment", "50% Deposit", "Milestone 1")
          example: Advance Payment Q1
        description:
          type: string
          description: Detailed description of the payment formatted as HTML. This is automatically converted from the internal IDoc format for API responses. May include notes about when the payment was made, its purpose, or related contract terms.
          example: <p>Payment for Q1 milestone - Client paid €1,000 upfront</p>
        amount:
          type: number
          description: Payment amount in the workspace currency. This amount is automatically deducted from the final invoice when the payment is billed.
          example: 10000
        isBilled:
          type: boolean
          description: Whether this payment has already been included in an invoice. When true, the payment has been billed and deducted from a final invoice. This field is read-only and managed automatically by the invoicing system.
          example: false
        userId:
          type: string
          description: ID of the user who created this interim payment (UUID)
          example: 550e8400-e29b-41d4-a716-446655440002
        createdAt:
          type: string
          description: ISO 8601 timestamp when the payment was created
          example: '2024-01-15T10:00:00Z'
        updatedAt:
          type: string
          description: ISO 8601 timestamp when the payment was last updated
          example: '2024-01-20T14:30:00Z'
        sort:
          type: number
          description: Numeric sort order for displaying payments in a list. Lower numbers appear first. Used to organize payments chronologically or by priority.
          example: 0
      required:
        - id
        - projectId
        - title
        - description
        - amount
        - isBilled
        - userId
        - createdAt
        - updatedAt
        - sort
    CreateProjectInterimPaymentDto:
      type: object
      properties:
        title:
          type: string
          description: 'Short label or title for the payment. Examples: "Prepayment", "50% Deposit", "Milestone 1", "Advance Payment Q1". This field is required and cannot be empty.'
          example: Advance Payment Q1
        description:
          type: string
          description: Optional detailed description or notes about the payment. Can be provided as HTML (e.g., "<p>Client paid €1,000 upfront</p>") or Markdown format. The system automatically detects the format and converts it to internal IDoc format for storage. If not provided, the description will be empty.
          example: <p>Payment for Q1 milestone - Client paid €1,000 upfront</p>
        amount:
          type: number
          description: Payment amount in the workspace currency. Must be a positive number with a minimum value of 0.01. This amount will be deducted from the final invoice when the payment is billed.
          example: 10000
      required:
        - title
        - amount
    PatchProjectInterimPaymentDto:
      type: object
      properties:
        title:
          type: string
          description: Short label or title for the payment. If provided, must not be empty. If omitted, the current title remains unchanged.
          example: Updated Payment Title
        description:
          type: string
          description: Optional detailed description or notes about the payment. Can be provided as HTML or Markdown format. If omitted, the current description remains unchanged. To clear the description, provide an empty string.
          example: <p>Updated payment description</p>
        amount:
          type: number
          description: Payment amount in the workspace currency. If provided, must be a positive number with a minimum value of 0.01. If omitted, the current amount remains unchanged.
          example: 20000
    SortInterimPaymentsDto:
      type: object
      properties:
        paymentIds:
          description: Array of payment IDs in the desired display order. Each ID must be a valid UUID v4 belonging to the project. The first ID in the array will have sort order 0, the second will have sort order 1, and so on. Payments not included in this array will retain their current sort order. The array cannot be empty.
          example:
            - 550e8400-e29b-41d4-a716-446655440000
            - 550e8400-e29b-41d4-a716-446655440001
            - 550e8400-e29b-41d4-a716-446655440002
          type: array
          items:
            type: string
      required:
        - paymentIds
    ManualPositionResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the manual position (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Name of the manual position as it appears in quotes and invoices
          example: Consulting Hours
        description:
          type: string
          description: Description of the position in HTML format. The description is converted from internal IDoc format to HTML for API responses. May be empty if no description was provided.
          example: <p>Professional consulting services</p>
        price:
          type: number
          description: Price of the position in the workspace currency (net price before taxes)
          example: 150
        categoryId:
          type: object
          description: Category ID if the position is assigned to a category, or null if no category is assigned. Categories help organize related positions together.
          example: 550e8400-e29b-41d4-a716-446655440000
          nullable: true
        sort:
          type: number
          description: Sort order of the position. Lower numbers appear first. Used to control the display order in project planning views, quotes, and invoices.
          example: 0
        createdAt:
          type: string
          description: ISO 8601 timestamp indicating when the position was created
          example: '2024-01-01T00:00:00Z'
        discountAmount:
          type: number
          description: Discount amount applied to this position. 0 means no discount.
          example: 10
        discountType:
          type: string
          description: 'Type of discount: "Fixed" (fixed amount) or "Percentage" (percentage of price)'
          enum:
            - Fixed
            - Percentage
          example: Fixed
      required:
        - id
        - name
        - description
        - price
        - sort
        - createdAt
        - discountAmount
        - discountType
    CreateManualPositionDto:
      type: object
      properties:
        name:
          type: string
          description: 'Name of the manual position. This will appear in project quotes and invoices. Examples: "Printing costs", "Translation services", "Workshop travel expenses"'
          example: Consulting Hours
        description:
          type: string
          description: Detailed description of the position. Can be provided as HTML or Markdown - the system will automatically detect and convert the format. This description appears in project quotes and invoices to provide additional context about the service or cost.
          example: <p>Professional consulting services</p>
        price:
          type: number
          description: Price of the position in the workspace currency. Must be a non-negative number. This is the net price before taxes. The position can later be discounted if needed.
          example: 150
          minimum: 0
        categoryId:
          type: string
          description: Optional category ID to organize positions. Categories help group related positions together (e.g., "External costs", "Travel expenses"). Must be a valid UUID of an existing project manual position category.
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - name
        - price
    PatchManualPositionDto:
      type: object
      properties:
        name:
          type: string
          description: Updated name of the manual position. Only provide this field if you want to change the name.
          example: Consulting Hours
        description:
          type: string
          description: Updated description. Can be provided as HTML or Markdown - the system will automatically detect and convert the format. Only provide this field if you want to change the description. To clear the description, provide an empty string.
          example: <p>Professional consulting services</p>
        price:
          type: number
          description: Updated price in the workspace currency. Must be a non-negative number. Only provide this field if you want to change the price.
          example: 150
          minimum: 0
        categoryId:
          type: object
          description: Updated category ID. Provide a valid UUID to assign a category, or null to remove the category assignment. Only provide this field if you want to change the category.
          example: 550e8400-e29b-41d4-a716-446655440000
          nullable: true
        discountAmount:
          type: number
          description: Discount amount to apply. For percentage discounts, this is the percentage (0-100). For fixed discounts, this is the amount in workspace currency. Set to 0 to remove the discount.
          example: 10
          minimum: 0
        discountType:
          type: string
          description: Type of discount. "Percentage" applies a percentage reduction, "Fixed" applies a fixed amount reduction. Required when discountAmount is provided.
          enum:
            - Fixed
            - Percentage
          example: Percentage
    SortManualPositionsDto:
      type: object
      properties:
        positionIds:
          description: Array of position IDs in the desired display order. The first ID will have sort order 0, the second will have sort order 1, and so on. All IDs must belong to the same project. Positions not included in this array will maintain their current sort order.
          example:
            - 550e8400-e29b-41d4-a716-446655440000
            - 550e8400-e29b-41d4-a716-446655440001
          type: array
          items:
            type: string
      required:
        - positionIds
    ProjectJournalResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the journal entry (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        projectId:
          type: string
          description: The UUID of the project this journal entry belongs to
          example: 550e8400-e29b-41d4-a716-446655440000
        createdAt:
          type: string
          description: ISO 8601 timestamp indicating when the journal entry was created
          example: '2024-01-15T10:30:00Z'
        createdBy:
          type: string
          description: UUID of the user who created this journal entry
          example: 550e8400-e29b-41d4-a716-446655440000
        lastUpdated:
          type: string
          description: ISO 8601 timestamp indicating when the journal entry was last updated. This is automatically updated whenever any field is modified via PATCH.
          example: '2024-01-16T14:20:00Z'
        mood:
          type: string
          description: Mood indicator for the journal entry. Sad indicates negative reactions, problems, or escalations. Neutral indicates factual observations or open-ended notes. Happy indicates positive feedback, wins, or progress.
          enum:
            - Sad
            - Neutral
            - Happy
          example: Neutral
        reminder:
          type: object
          description: Optional reminder date in ISO 8601 format (YYYY-MM-DD). Set when creating or updating an entry to schedule a follow-up reminder. Null if no reminder is set.
          example: '2024-12-31'
          nullable: true
        body:
          type: string
          description: The journal entry content converted to HTML format with all formatting preserved. Includes paragraphs, headings, lists, highlighting, and other rich text elements.
          example: <h1>Project Update</h1><p>Today we completed milestone 1.</p>
      required:
        - id
        - projectId
        - createdAt
        - createdBy
        - lastUpdated
        - mood
        - reminder
        - body
    CreateProjectJournalDto:
      type: object
      properties:
        projectId:
          type: string
          description: The UUID of the project this journal entry belongs to. The project must exist and be accessible to the authenticated user.
          example: 550e8400-e29b-41d4-a716-446655440000
        body:
          type: string
          description: |-
            The journal entry content in HTML or Markdown format. Supports rich text formatting including paragraphs, headings, lists, and highlighting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             This field is required and cannot be empty.
          example: <h1>Project Update</h1><p>Today we completed milestone 1.</p>
        mood:
          type: string
          description: Mood indicator for the journal entry. Use Sad for negative reactions, problems, or escalations. Use Neutral for factual observations or open-ended notes. Use Happy for positive feedback, wins, or progress. If not provided, defaults to Neutral.
          enum:
            - Sad
            - Neutral
            - Happy
          example: Neutral
        reminder:
          type: string
          description: Optional reminder date in ISO 8601 format (YYYY-MM-DD). Use this to set a follow-up reminder for the journal entry. Useful for escalation checks, review points, or follow-up tasks.
          example: '2024-12-31'
      required:
        - projectId
        - body
    PatchProjectJournalDto:
      type: object
      properties:
        projectId:
          type: string
          description: The UUID of the project this journal entry belongs to. If provided, the entry will be moved to this project. The project must exist and be accessible to the authenticated user. If not provided, the current projectId is preserved.
          example: 550e8400-e29b-41d4-a716-446655440000
        body:
          type: string
          description: |-
            The journal entry content in HTML or Markdown format. Supports rich text formatting including paragraphs, headings, lists, and highlighting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             If provided, must not be empty. If not provided, the current body content is preserved.
          example: <h1>Updated Project Status</h1><p>Progress update...</p>
        mood:
          type: string
          description: Mood indicator for the journal entry. Use Sad for negative reactions, problems, or escalations. Use Neutral for factual observations or open-ended notes. Use Happy for positive feedback, wins, or progress. If not provided, the current mood is preserved.
          enum:
            - Sad
            - Neutral
            - Happy
          example: Happy
        reminder:
          type: object
          description: Reminder date in ISO 8601 format (YYYY-MM-DD). Provide a date string to set or update the reminder. Set to null to clear an existing reminder. If not provided (omitted from request), the current reminder is preserved.
          example: '2024-12-31'
          nullable: true
    ProjectUploadResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the project upload record (UUID format)
          example: 123e4567-e89b-12d3-a456-426614174333
        projectId:
          type: string
          description: UUID of the project this upload belongs to. Determines which project the file is associated with.
          example: 123e4567-e89b-12d3-a456-426614174000
        fileId:
          type: string
          description: UUID of the underlying file in workspace storage. This references the file uploaded via POST /workspace/upload.
          example: 123e4567-e89b-12d3-a456-426614174111
        description:
          type: object
          description: Free text description for classifying the upload content. Can be null if no description was provided.
          example: Project documentation for Q1 2024
          nullable: true
        categoryId:
          type: object
          description: UUID of the category assigned to this upload for thematic organization. Can be null if no category is assigned.
          example: 123e4567-e89b-12d3-a456-426614174222
          nullable: true
        fileName:
          type: string
          description: Original filename of the uploaded file, as stored in the File relation. This is the name the file had when uploaded.
          example: documentation.pdf
        mimeType:
          type: string
          description: MIME type of the file indicating its format (e.g., application/pdf, image/png, text/plain). Retrieved from the File relation.
          example: application/pdf
        fileSize:
          type: number
          description: Size of the file in bytes. Use this to display file sizes in a human-readable format (e.g., KB, MB).
          example: 1048576
        userId:
          type: string
          description: UUID of the user who created this upload. This identifies who uploaded or last updated the file.
          example: 123e4567-e89b-12d3-a456-426614174444
        createdAt:
          type: string
          description: 'ISO 8601 formatted timestamp indicating when the upload record was created. Format: YYYY-MM-DDTHH:mm:ss.sssZ'
          example: '2024-01-01T12:00:00.000Z'
        updatedAt:
          type: string
          description: 'ISO 8601 formatted timestamp indicating when the upload record was last updated. Format: YYYY-MM-DDTHH:mm:ss.sssZ. This changes whenever any field of the upload is modified.'
          example: '2024-01-02T14:30:00.000Z'
      required:
        - id
        - projectId
        - fileId
        - description
        - categoryId
        - fileName
        - mimeType
        - fileSize
        - userId
        - createdAt
        - updatedAt
    CreateProjectUploadDto:
      type: object
      properties:
        projectId:
          type: string
          description: The UUID of the project to attach this upload to. The project must exist in the workspace and the user must have access to it. This determines which project the file will be associated with.
          example: 123e4567-e89b-12d3-a456-426614174000
        fileId:
          type: string
          description: The UUID of the file obtained from the POST /workspace/upload endpoint. You must upload the file first using that endpoint to receive a fileId, then use that ID here. The file must exist in the workspace and belong to the same workspace.
          example: 123e4567-e89b-12d3-a456-426614174111
        description:
          type: string
          description: Optional free text description for classifying and identifying the upload content. Use clear, descriptive text like "Signed contract 10/28/2025", "Kickoff protocol", or "Technical specification v2". This helps users quickly understand what the file contains without opening it.
          example: Project documentation for Q1 2024
        categoryId:
          type: object
          description: Optional UUID of a category for organizing uploads thematically (e.g., Contract, Offer, Documentation, Dossier). Categories help with filtering, sorting, and finding files. The category must exist in the workspace. Set to null to create an upload without a category. Categories can be created dynamically during upload if they do not exist yet.
          example: 123e4567-e89b-12d3-a456-426614174222
          nullable: true
      required:
        - projectId
        - fileId
    PatchProjectUploadDto:
      type: object
      properties:
        projectId:
          type: string
          description: Change which project this upload belongs to. Provide the UUID of the target project. The project must exist in the workspace and the user must have access to it. If not provided, the upload remains associated with its current project.
          example: 123e4567-e89b-12d3-a456-426614174000
        description:
          type: object
          description: Update the description text for the upload. Provide a new description string, or set to null to clear the description. If not provided, the description remains unchanged.
          example: Updated project documentation
          nullable: true
        categoryId:
          type: object
          description: Change the category assignment for the upload. Provide the UUID of a category, or set to null to remove the category. The category must exist in the workspace if provided. If not provided, the category remains unchanged.
          example: 123e4567-e89b-12d3-a456-426614174222
          nullable: true
    GetProjectDocumentSettingsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Project UUID
          example: 550e8400-e29b-41d4-a716-446655440000
        projectDocumentEnableToc:
          type: object
          description: Enable table of contents for project documents. When true, automatically generated documents include a table of contents. When false, table of contents is disabled for this project. When null, the project inherits the organization/workspace default. This setting is especially helpful for long documents and makes them easier to navigate.
          example: true
          nullable: true
        projectDocumentTitlePage:
          type: object
          description: Enable title page for project documents. When true, generated documents include a title page. When false, title page is disabled for this project. When null, the project inherits the organization/workspace default.
          example: true
          nullable: true
        projectDocumentDefaultContactUserId:
          type: object
          description: Default contact person user ID for project documents. This person is automatically set as the contact when creating new documents for this project. Useful for recurring offer processes, automated preambles, and binding customer-side addressing. When null, the project inherits the organization default (if set) or no default contact is used. Must be a valid user UUID from the workspace.
          example: 550e8400-e29b-41d4-a716-446655440001
          nullable: true
        projectDocumentHeadingStyle:
          type: string
          description: 'Heading style for project documents. Controls the numbering and formatting of headings in generated documents. This feature keeps document outlines consistent, especially in contract documents when dynamic sections appear or disappear based on form parameters. When null, the project inherits the organization/workspace default. Available styles: Normal (no numbering), Sequential (numbering from level 1), SequentialFromSecondLevel (numbering from level 2), SequentialWithParagraphs (numbering with symbol prefix like §).'
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
          nullable: true
        projectDocumentEnableTocDefault:
          type: boolean
          description: Default value for table of contents inherited from organization/workspace hierarchy. This is the value that will be used when projectDocumentEnableToc is null. Shows what the organization or workspace default is set to.
          example: false
        projectDocumentTitlePageDefault:
          type: boolean
          description: Default value for title page inherited from organization/workspace hierarchy. This is the value that will be used when projectDocumentTitlePage is null. Shows what the organization or workspace default is set to.
          example: true
        projectDocumentDefaultContactUserIdDefault:
          type: object
          description: Default contact person user ID inherited from organization hierarchy. This is the value that will be used when projectDocumentDefaultContactUserId is null. When null, there is no default contact person set at the organization/workspace level.
          example: 550e8400-e29b-41d4-a716-446655440002
          nullable: true
        projectDocumentHeadingStyleDefault:
          type: string
          description: Default heading style inherited from organization/workspace hierarchy. This is the value that will be used when projectDocumentHeadingStyle is null. Shows what the organization or workspace default heading style is set to.
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Normal
      required:
        - id
        - projectDocumentEnableToc
        - projectDocumentTitlePage
        - projectDocumentDefaultContactUserId
        - projectDocumentHeadingStyle
        - projectDocumentEnableTocDefault
        - projectDocumentTitlePageDefault
        - projectDocumentDefaultContactUserIdDefault
        - projectDocumentHeadingStyleDefault
    PatchProjectDocumentSettingsDto:
      type: object
      properties:
        projectDocumentEnableToc:
          type: object
          description: Enable table of contents for project documents. Set to true to enable table of contents, false to disable, or null to inherit from organization/workspace default. This setting is especially helpful for long documents and makes them easier to navigate. All fields in this DTO are optional - only provided fields will be updated.
          example: true
          nullable: true
        projectDocumentTitlePage:
          type: object
          description: Enable title page for project documents. Set to true to enable title page, false to disable, or null to inherit from organization/workspace default. All fields in this DTO are optional - only provided fields will be updated.
          example: true
          nullable: true
        projectDocumentDefaultContactUserId:
          type: object
          description: Default contact person user ID for project documents. Set to a valid user UUID to automatically set this person as contact when creating new documents, or null to inherit from organization default (if set) or remove the default contact. Useful for recurring offer processes and automated preambles. Must be a valid UUID of a user in the workspace. All fields in this DTO are optional - only provided fields will be updated.
          example: 550e8400-e29b-41d4-a716-446655440001
          nullable: true
        projectDocumentHeadingStyle:
          type: string
          description: 'Heading style for project documents. Controls the numbering and formatting of headings in generated documents. Set to a specific style to override, or null to inherit from organization/workspace default. Available styles: Normal (no numbering), Sequential (numbering from level 1), SequentialFromSecondLevel (numbering from level 2), SequentialWithParagraphs (numbering with symbol prefix like §). This feature keeps document outlines consistent, especially in contract documents. All fields in this DTO are optional - only provided fields will be updated.'
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
          nullable: true
    ProjectSubscriptionBillingResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the subscription billing item. UUID format.
          example: 550e8400-e29b-41d4-a716-446655440000
        projectId:
          type: string
          description: The project this subscription billing belongs to. UUID format.
          example: 550e8400-e29b-41d4-a716-446655440001
        fromDate:
          type: string
          description: 'Start date of the subscription period. Format: ISO 8601 date string (YYYY-MM-DD). The subscription becomes active on this date.'
          example: '2024-01-01'
        toDate:
          type: string
          description: 'End date of the subscription period. Format: ISO 8601 date string (YYYY-MM-DD). The subscription expires after this date. Must be equal to or after fromDate.'
          example: '2024-12-31'
        type:
          type: string
          description: Type of subscription billing. "manual" indicates a manually created subscription (typical for API-created subscriptions). "product" indicates a subscription linked to a product catalog item.
          enum:
            - manual
            - product
          example: manual
        payment:
          type: string
          description: Payment calculation method for the subscription. "fixed" = fixed amount per billing period (e.g., $500/month). "perUnit" = variable pricing based on units (e.g., per user, per license). "oneTime" = one-time charge within the subscription period.
          enum:
            - fixed
            - perUnit
            - oneTime
          example: fixed
        billingTiming:
          type: string
          description: 'When billing rows become invoiceable: **arrears** = after the billing period ends (default; row `to` date is before today). **advance** = at the start of the billing period (row `from` date is on or before today).'
          enum:
            - arrears
            - advance
          example: arrears
        manualTitle:
          type: string
          description: 'Human-readable title or description of the subscription. This appears on invoices and in project settings. Examples: "Monthly Support Package", "Quarterly Maintenance", "Annual License Renewal".'
          example: Monthly Support Package
        price:
          type: number
          description: Price amount for the subscription. For fixed payment type, this is the amount per billing period. For perUnit payment type, this is the price per unit. For oneTime payment type, this is the total charge. Must be a positive number.
          example: 5000
        frequency:
          type: number
          description: 'Billing frequency in months. Determines how often billing rows are generated. Common values: 1 = monthly billing, 3 = quarterly billing, 6 = semi-annual billing, 12 = yearly billing. Must be between 1 and 12.'
          example: 1
          minimum: 1
          maximum: 12
        sort:
          type: number
          description: Display sort order for the subscription billing item. Lower numbers appear first. Used to control the order when multiple subscriptions are displayed. Automatically calculated when creating new items.
          example: 0
        quantity:
          type: number
          description: Quantity for variable pricing. Required when payment type is "oneTime". Used to calculate total charge (price × quantity). Optional for other payment types.
          example: 1
      required:
        - id
        - projectId
        - fromDate
        - toDate
        - type
        - payment
        - billingTiming
        - manualTitle
        - price
        - frequency
        - sort
    CreateProjectSubscriptionBillingDto:
      type: object
      properties:
        fromDate:
          type: string
          description: 'Start date when the subscription becomes active. Format: ISO 8601 date string (YYYY-MM-DD). The system will generate billing rows starting from the beginning of this month. Required.'
          example: '2024-01-01'
        toDate:
          type: string
          description: 'End date when the subscription expires. Format: ISO 8601 date string (YYYY-MM-DD). The system will generate billing rows up to the end of this month. Must be equal to or after fromDate. Required.'
          example: '2024-12-31'
        type:
          type: string
          description: Type of subscription billing. Defaults to "manual" for API-created subscriptions. Use "product" only if linking to an existing product catalog item. Optional, defaults to manual.
          enum:
            - manual
            - product
          example: manual
          default: manual
        payment:
          type: string
          description: Payment calculation method. "fixed" = fixed amount per billing period (e.g., $500/month). "perUnit" = variable pricing based on units (e.g., $50 per user per month). "oneTime" = one-time charge (requires quantity field). Required.
          enum:
            - fixed
            - perUnit
            - oneTime
          example: fixed
        billingTiming:
          type: string
          description: Invoice timing. **arrears** (default) = include a billing row in potential invoices only after the period ends. **advance** = include it from the first day of the billing period.
          enum:
            - arrears
            - advance
          example: arrears
          default: arrears
        manualTitle:
          type: string
          description: 'Human-readable title or description that appears on invoices and in project settings. Examples: "Monthly Support Package", "Quarterly Maintenance Contract", "Annual License Renewal". Required.'
          example: Monthly Support Package
        price:
          type: number
          description: 'Price amount. For "fixed" payment type: amount per billing period. For "perUnit" payment type: price per unit. For "oneTime" payment type: total charge amount. Must be a positive number. Required.'
          example: 5000
        frequency:
          type: number
          description: 'Billing frequency in months. Determines how often billing rows are generated within the date range. Common values: 1 = monthly, 3 = quarterly, 6 = semi-annual, 12 = yearly. Must be between 1 and 12. Required.'
          example: 1
          minimum: 1
          maximum: 12
        quantity:
          type: number
          description: Quantity for variable pricing. Required when payment type is "oneTime" to calculate total charge (price × quantity). Optional for "fixed" and "perUnit" payment types.
          example: 1
      required:
        - fromDate
        - toDate
        - payment
        - manualTitle
        - price
        - frequency
    PatchProjectSubscriptionBillingDto:
      type: object
      properties:
        fromDate:
          type: string
          description: 'Start date when the subscription becomes active. Format: ISO 8601 date string (YYYY-MM-DD). Updating this will regenerate billing rows. If both fromDate and toDate are provided, fromDate must be before or equal to toDate. Optional.'
          example: '2024-01-01'
        toDate:
          type: string
          description: 'End date when the subscription expires. Format: ISO 8601 date string (YYYY-MM-DD). Updating this will regenerate billing rows. If both fromDate and toDate are provided, fromDate must be before or equal to toDate. Optional.'
          example: '2024-12-31'
        type:
          type: string
          description: Type of subscription billing. "manual" for manually created subscriptions, "product" for subscriptions linked to product catalog items. Optional.
          enum:
            - manual
            - product
          example: manual
        payment:
          type: string
          description: Payment calculation method. "fixed" = fixed amount per billing period. "perUnit" = variable pricing based on units. "oneTime" = one-time charge (requires quantity). Optional.
          enum:
            - fixed
            - perUnit
            - oneTime
          example: fixed
        billingTiming:
          type: string
          description: 'Invoice timing: arrears (after period ends) or advance (from period start). Updating changes when unbilled rows qualify for potential invoices; it does not regenerate billing rows.'
          enum:
            - arrears
            - advance
          example: advance
        manualTitle:
          type: string
          description: 'Human-readable title or description that appears on invoices and in project settings. Examples: "Monthly Support Package", "Quarterly Maintenance". Optional.'
          example: Monthly Support Package
        price:
          type: number
          description: 'Price amount. For "fixed": amount per billing period. For "perUnit": price per unit. For "oneTime": total charge amount. Must be positive if provided. Optional.'
          example: 5000
        frequency:
          type: number
          description: 'Billing frequency in months. Common values: 1 = monthly, 3 = quarterly, 6 = semi-annual, 12 = yearly. Updating this will regenerate billing rows. Must be between 1 and 12 if provided. Optional.'
          example: 1
          minimum: 1
          maximum: 12
        quantity:
          type: number
          description: Quantity for variable pricing. Required when payment type is "oneTime" to calculate total charge (price × quantity). Optional for other payment types.
          example: 1
    ProjectProductResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the product in the project
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Product name as defined in the product
          example: Product Name
        finalName:
          type: object
          description: Final display name combining product name and active variant name (if a variant is active). If no variant is active, this matches the product name.
          example: Variant Name
          nullable: true
        categoryId:
          type: string
          description: UUID of the product category
          example: 550e8400-e29b-41d4-a716-446655440001
        logoId:
          type: object
          description: UUID of the logo file associated with this product. Null if no logo is set.
          example: 550e8400-e29b-41d4-a716-446655440008
          nullable: true
        quantity:
          type: number
          description: Number of units of this product in the project
          example: 1
        finalPriceFixed:
          type: object
          description: Final calculated fixed price including variant and option adjustments. This is the price that will be used for billing. Null if the product does not have fixed pricing.
          example: 100
          nullable: true
        finalPriceSubscription:
          type: object
          description: Final calculated subscription price including variant and option adjustments. This is the recurring price per billing period. Null if the product does not have subscription pricing.
          example: 25
          nullable: true
        finalPricePerUnit:
          type: object
          description: Final calculated per-unit price including variant and option adjustments. This is the price per unit (e.g., per user, per hour). Null if the product does not have per-unit pricing.
          example: 5
          nullable: true
        activeFrom:
          type: string
          description: 'Start date when this product becomes active. Used for time-limited pricing versions. Format: ISO 8601 date (YYYY-MM-DD). Null if no start date is set.'
          example: '2024-01-01'
          nullable: true
          format: date
        activeTo:
          type: string
          description: 'End date when this product becomes inactive. Used for time-limited pricing versions. Format: ISO 8601 date (YYYY-MM-DD). Null if no end date is set.'
          example: '2024-12-31'
          nullable: true
          format: date
        isActive:
          type: boolean
          description: Whether the product is currently active based on the activeFrom and activeTo date range. True if current date is within the active range (or no dates are set).
          example: true
        fixedDiscountAmount:
          type: number
          description: Fixed price discount amount (0 = no discount)
          example: 0
        fixedDiscountType:
          type: string
          description: Fixed price discount type
          enum:
            - Fixed
            - Percentage
          example: Fixed
        subscriptionDiscountAmount:
          type: number
          description: Subscription price discount amount (0 = no discount)
          example: 0
        subscriptionDiscountType:
          type: string
          description: Subscription price discount type
          enum:
            - Fixed
            - Percentage
          example: Fixed
        perUnitDiscountAmount:
          type: number
          description: Per-unit price discount amount (0 = no discount)
          example: 0
        perUnitDiscountType:
          type: string
          description: Per-unit price discount type
          enum:
            - Fixed
            - Percentage
          example: Fixed
      required:
        - id
        - name
        - finalName
        - categoryId
        - logoId
        - quantity
        - finalPriceFixed
        - finalPriceSubscription
        - finalPricePerUnit
        - activeFrom
        - activeTo
        - isActive
        - fixedDiscountAmount
        - fixedDiscountType
        - subscriptionDiscountAmount
        - subscriptionDiscountType
        - perUnitDiscountAmount
        - perUnitDiscountType
    ProductVariantResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the variant
          example: 550e8400-e29b-41d4-a716-446655440006
        name:
          type: string
          description: Name of the variant (e.g., Standard, Pro, Enterprise)
          example: Premium
        description:
          type: object
          description: Variant description formatted as HTML. Describes what makes this variant different from others.
          example: <p>Premium variant with additional features</p>
          nullable: true
        priceFixed:
          type: object
          description: One-time fixed price for this variant. Null if this variant does not have fixed pricing.
          example: 150
          nullable: true
        priceSubscription:
          type: object
          description: Recurring subscription price per billing period for this variant. Null if this variant does not have subscription pricing.
          example: 50
          nullable: true
        pricePerUnit:
          type: object
          description: Per-unit price for this variant. Null if this variant does not have per-unit pricing.
          example: 10
          nullable: true
        isActivated:
          type: boolean
          description: Whether this variant is currently active for the product. Only one variant can be active at a time.
          example: true
        sort:
          type: number
          description: Sort order of this variant. Lower values appear first when listing variants.
          example: 0
      required:
        - id
        - name
        - description
        - priceFixed
        - priceSubscription
        - pricePerUnit
        - isActivated
        - sort
    ProductOptionValueResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the option value
          example: 550e8400-e29b-41d4-a716-446655440007
        name:
          type: string
          description: Name of the option value (e.g., Red, Blue, Large, Small)
          example: 'Color: Red'
        extraPriceFixed:
          type: object
          description: Additional fixed price added when this option value is selected. Null or 0 if no extra fixed cost.
          example: 5
          nullable: true
        extraPriceSubscription:
          type: object
          description: Additional subscription price added per billing period when this option value is selected. Null or 0 if no extra subscription cost.
          example: 2
          nullable: true
        extraPricePerUnit:
          type: object
          description: Additional per-unit price added when this option value is selected. Null or 0 if no extra per-unit cost.
          example: 1
          nullable: true
        isActivated:
          type: boolean
          description: Whether this option value is currently selected/activated for the product
          example: true
        sort:
          type: number
          description: Sort order of this option value. Lower values appear first when listing option values.
          example: 0
      required:
        - id
        - name
        - extraPriceFixed
        - extraPriceSubscription
        - extraPricePerUnit
        - isActivated
        - sort
    ProductOptionResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the option
          example: 550e8400-e29b-41d4-a716-446655440004
        name:
          type: string
          description: Name of the option (e.g., Color, Size, Add-ons)
          example: Color
        values:
          description: Array of all possible values for this option
          type: array
          items:
            $ref: '#/components/schemas/ProductOptionValueResponseDto'
        isRequired:
          type: boolean
          description: Whether this option must be selected when configuring the product
          example: true
        isMultiple:
          type: boolean
          description: Whether multiple values can be selected from this option
          example: false
        sort:
          type: number
          description: Sort order of this option. Lower values appear first when listing options.
          example: 0
      required:
        - id
        - name
        - values
        - isRequired
        - isMultiple
        - sort
    ProjectProductDetailsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: UUID of the product in the project
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Product name as defined in the product
          example: Product Name
        description:
          type: string
          description: Product description formatted as HTML. Converted from the internal document format.
          example: <p>Product description</p>
        categoryId:
          type: string
          description: UUID of the product category
          example: 550e8400-e29b-41d4-a716-446655440001
        logoId:
          type: object
          description: UUID of the logo file associated with this product. Null if no logo is set.
          example: 550e8400-e29b-41d4-a716-446655440008
          nullable: true
        quantity:
          type: number
          description: Number of units of this product in the project
          example: 1
        priceFixed:
          type: object
          description: Base fixed price for the product before variant and option adjustments
          example: 100
          nullable: true
        priceSubscription:
          type: object
          description: Base subscription price per billing period before variant and option adjustments
          example: 25
          nullable: true
        pricePerUnit:
          type: object
          description: Base per-unit price before variant and option adjustments
          example: 5
          nullable: true
        priceUnitName:
          type: object
          description: Unit name for per-unit pricing (e.g., per user, per hour)
          example: per user
          nullable: true
        priceFrequency:
          type: object
          description: 'Billing frequency in months for subscription and per-unit pricing. Valid values: 1, 3, 6, or 12.'
          example: 1
          nullable: true
        finalPriceFixed:
          type: object
          description: Final calculated fixed price including variant and option adjustments. This is the price used for billing.
          example: 100
          nullable: true
        finalPriceSubscription:
          type: object
          description: Final calculated subscription price including variant and option adjustments. This is the recurring price per billing period.
          example: 25
          nullable: true
        finalPricePerUnit:
          type: object
          description: Final calculated per-unit price including variant and option adjustments
          example: 5
          nullable: true
        finalName:
          type: object
          description: Final display name combining product name and active variant name (if a variant is active)
          example: Variant Name
          nullable: true
        activeFrom:
          type: string
          description: 'Start date when this product becomes active. Format: ISO 8601 date (YYYY-MM-DD). Null if no start date is set.'
          example: '2024-01-01'
          nullable: true
          format: date
        activeTo:
          type: string
          description: 'End date when this product becomes inactive. Format: ISO 8601 date (YYYY-MM-DD). Null if no end date is set.'
          example: '2024-12-31'
          nullable: true
          format: date
        variants:
          description: Array of all available variants for this product
          type: array
          items:
            $ref: '#/components/schemas/ProductVariantResponseDto'
        options:
          description: Array of all available options for this product
          type: array
          items:
            $ref: '#/components/schemas/ProductOptionResponseDto'
      required:
        - id
        - name
        - description
        - categoryId
        - logoId
        - quantity
        - priceFixed
        - priceSubscription
        - pricePerUnit
        - priceUnitName
        - priceFrequency
        - finalPriceFixed
        - finalPriceSubscription
        - finalPricePerUnit
        - finalName
        - activeFrom
        - activeTo
        - variants
        - options
    ImportProductToProjectDto:
      type: object
      properties:
        productId:
          type: string
          description: UUID of the catalog product to import into the project. The product must exist in the product catalog. When imported, a copy is created in the project that can be configured independently.
          example: 550e8400-e29b-41d4-a716-446655440002
        quantity:
          type: number
          description: Number of units of this product to add to the project. Must be at least 1. The quantity affects the final price calculation when using per-unit pricing.
          example: 2
          minimum: 1
        activeVariantId:
          type: object
          description: UUID of the variant to activate for this product instance. Variants represent different configurations or tiers of a product (e.g., Standard, Pro, Enterprise). If not provided, no variant is active. The variant must belong to the product being imported.
          example: 550e8400-e29b-41d4-a716-446655440003
          nullable: true
        options:
          description: Array of option selections for this product. Options are add-ons or configuration choices that customers can select. Each option selection specifies which option is being configured and which values are selected. Required options must be included. Can be an empty array if no options are needed.
          example:
            - id: 550e8400-e29b-41d4-a716-446655440004
              value:
                - 550e8400-e29b-41d4-a716-446655440005
          type: array
          items:
            $ref: '#/components/schemas/ProductSettingOptionDto'
        priceFrequency:
          type: number
          description: 'Billing frequency in months for subscription or per-unit pricing. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).'
          example: 1
        contractPeriodMonths:
          type: number
          description: Contract period in months. Defaults to 12 months if not specified.
          example: 12
        renewalMode:
          type: string
          description: Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period.
          enum:
            - Automatic
            - Stops
          example: Automatic
        activeFromType:
          type: string
          description: Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.
          enum:
            - Immediately
            - ProjectBilling
            - FixedDate
          example: Immediately
        activeFromFixedDate:
          type: string
          description: 'Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.'
          example: '2024-01-01'
          nullable: true
          format: date
        customPrices:
          type: object
          description: Custom price overrides for base product, variants, and options. Keys can be "base", "variant_<variantId>", or "option_<optionValueId>". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving.
          additionalProperties:
            $ref: '#/components/schemas/CustomPriceDto'
          example:
            base:
              priceFixed: 100.5
              priceSubscription: 50.25
              pricePerUnit: 10
            variant_550e8400:
              priceFixed: 150
              priceSubscription: 75
              pricePerUnit: 15
      required:
        - productId
        - quantity
        - options
        - customPrices
    ProductSettingsDto:
      type: object
      properties:
        quantity:
          type: number
          description: Number of units of this product in the project. Must be at least 1. This affects the final price calculation, especially for per-unit pricing.
          example: 3
          minimum: 1
        activeVariantId:
          type: object
          description: UUID of the variant to activate for this product. Variants represent different configurations or tiers (e.g., Standard, Pro, Enterprise). Set to null to deactivate any currently active variant. The variant must belong to this product.
          example: 550e8400-e29b-41d4-a716-446655440006
          nullable: true
        options:
          description: Array of option selections for this product. Each selection specifies which option is being configured and which values are selected. Required options must be included. Provide an empty array if no options should be selected.
          example:
            - id: 550e8400-e29b-41d4-a716-446655440004
              value:
                - 550e8400-e29b-41d4-a716-446655440007
          type: array
          items:
            $ref: '#/components/schemas/ProductSettingOptionDto'
        priceFrequency:
          type: number
          description: 'Billing frequency in months for subscription or per-unit pricing. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).'
          example: 1
        contractPeriodMonths:
          type: number
          description: Contract period in months. Defaults to 12 months if not specified.
          example: 12
        renewalMode:
          type: string
          description: Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period.
          enum:
            - Automatic
            - Stops
          example: Automatic
        activeFromType:
          type: string
          description: Type of activation start for the product. Immediately means it starts right away, ProjectBilling means it starts with project billing, FixedDate means it starts on a specific date.
          enum:
            - Immediately
            - ProjectBilling
            - FixedDate
          example: Immediately
        activeFromFixedDate:
          type: string
          description: 'Fixed start date when activeFromType is FixedDate. Format: ISO 8601 date (YYYY-MM-DD). Required when activeFromType is FixedDate.'
          example: '2024-01-01'
          nullable: true
          format: date
        customPrices:
          type: object
          description: Custom price overrides for base product, variants, and options. Keys can be "base", "variant_<variantId>", or "option_<optionValueId>". Each value contains priceFixed, priceSubscription, and pricePerUnit overrides. These prices are applied to the product when saving.
          additionalProperties:
            $ref: '#/components/schemas/CustomPriceDto'
          example:
            base:
              priceFixed: 100.5
              priceSubscription: 50.25
              pricePerUnit: 10
            variant_550e8400:
              priceFixed: 150
              priceSubscription: 75
              pricePerUnit: 15
        fixedDiscountAmount:
          type: number
          description: Discount amount for the fixed price component. For percentage discounts, this is the percentage (0-100). For fixed discounts, this is the amount. Set to 0 to remove.
          example: 10
          minimum: 0
        fixedDiscountType:
          type: string
          description: Discount type for the fixed price component.
          enum:
            - Fixed
            - Percentage
        subscriptionDiscountAmount:
          type: number
          description: Discount amount for the subscription price component.
          example: 15
          minimum: 0
        subscriptionDiscountType:
          type: string
          description: Discount type for the subscription price component.
          enum:
            - Fixed
            - Percentage
        perUnitDiscountAmount:
          type: number
          description: Discount amount for the per-unit price component.
          example: 5
          minimum: 0
        perUnitDiscountType:
          type: string
          description: Discount type for the per-unit price component.
          enum:
            - Fixed
            - Percentage
      required:
        - quantity
        - options
        - customPrices
    ProductSortDto:
      type: object
      properties:
        ids:
          description: Array of product UUIDs in the desired display order. The first UUID in the array will appear first in the product list, the second UUID will appear second, and so on. All product IDs must belong to the same project. Products not included in this array will retain their current sort order.
          example:
            - 550e8400-e29b-41d4-a716-446655440008
            - 550e8400-e29b-41d4-a716-446655440009
            - 550e8400-e29b-41d4-a716-446655440010
          type: array
          items:
            type: string
      required:
        - ids
    ProjectVersionDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the version (UUID)
          example: 660e8400-e29b-41d4-a716-446655440001
        title:
          type: string
          description: Title/name of the version as provided when creating it
          example: Version 1.2.3
        semverVersion:
          type: string
          description: Semantic version string automatically generated based on parent version and branch. Follows semver-like format (e.g., "1.2.3")
          example: 1.2.3
        branchId:
          type: string
          description: Branch identifier (UUID) for version tracking. Used to group related versions together and enable branching/versioning workflows
          example: 770e8400-e29b-41d4-a716-446655440002
        parentId:
          type: object
          description: Parent version ID (UUID). Null if this is the first version created for the project. Used to track version lineage and generate semantic version numbers
          example: 660e8400-e29b-41d4-a716-446655440000
          nullable: true
        createdAt:
          type: string
          description: ISO 8601 timestamp when the version was created
          example: '2024-01-15T10:30:00.000Z'
        createdBy:
          type: string
          description: User ID (UUID) who created the version
          example: 880e8400-e29b-41d4-a716-446655440003
        description:
          type: string
          description: HTML-formatted description of the version. Originally provided as HTML or Markdown when creating the version, always returned as HTML
          example: <h1>Version Description</h1><p>This is version 1.2.3</p>
        isCurrent:
          type: boolean
          description: Whether this is the current active version for the project. Only one version can be current at a time. The current version represents the active project state
          example: true
      required:
        - id
        - title
        - semverVersion
        - branchId
        - parentId
        - createdAt
        - createdBy
        - description
        - isCurrent
    ProjectVersionDetailsDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the version (UUID)
          example: 660e8400-e29b-41d4-a716-446655440001
        title:
          type: string
          description: Title/name of the version as provided when creating it
          example: Version 1.2.3
        semverVersion:
          type: string
          description: Semantic version string automatically generated based on parent version and branch. Follows semver-like format (e.g., "1.2.3")
          example: 1.2.3
        branchId:
          type: string
          description: Branch identifier (UUID) for version tracking. Used to group related versions together and enable branching/versioning workflows
          example: 770e8400-e29b-41d4-a716-446655440002
        parentId:
          type: object
          description: Parent version ID (UUID). Null if this is the first version created for the project. Used to track version lineage and generate semantic version numbers
          example: 660e8400-e29b-41d4-a716-446655440000
          nullable: true
        createdAt:
          type: string
          description: ISO 8601 timestamp when the version was created
          example: '2024-01-15T10:30:00.000Z'
        createdBy:
          type: string
          description: User ID (UUID) who created the version
          example: 880e8400-e29b-41d4-a716-446655440003
        description:
          type: string
          description: HTML-formatted description of the version. Originally provided as HTML or Markdown when creating the version, always returned as HTML
          example: <h1>Version Description</h1><p>This is version 1.2.3</p>
        isCurrent:
          type: boolean
          description: Whether this is the current active version for the project. Only one version can be current at a time. The current version represents the active project state
          example: true
        estimateData:
          type: object
          description: Complete estimate data captured at the time the version was created. Includes products with variants and prices, components with nested structure, manual positions, and pricing breakdowns (fixed, subscriptions, per-unit). This data represents the exact project state when the version was saved.
      required:
        - id
        - title
        - semverVersion
        - branchId
        - parentId
        - createdAt
        - createdBy
        - description
        - isCurrent
        - estimateData
    CreateProjectVersionDto:
      type: object
      properties:
        projectId:
          type: string
          description: UUID of the project to create a version for
          example: 550e8400-e29b-41d4-a716-446655440000
        title:
          type: string
          description: Title/name of the version. Use clear, descriptive titles like "Version 1.2.3 - Initial Release" or "Customer Proposal v2"
          example: Version 1.2.3 - Initial Release
        description:
          type: string
          description: Description of the version in HTML or Markdown format (optional). This can include notes about what changed, why the version was created, or any relevant context.
          example: <h1>Version 1.2.3</h1><p>This version includes initial features</p>
      required:
        - projectId
        - title
    ProjectConfigurationStateDto:
      type: object
      properties:
        projectId:
          type: string
          description: Project UUID
          example: 550e8400-e29b-41d4-a716-446655440000
        lastConfigurationSnapshot:
          type: object
          description: Information about the last saved version (snapshot). Null if no versions have been created yet. Contains the version ID, semantic version string, creation timestamp, and title.
          nullable: true
          properties:
            id:
              type: string
              description: Version ID (UUID)
              example: 660e8400-e29b-41d4-a716-446655440001
            version:
              type: string
              description: Semantic version string
              example: 1.2.3
            createdAt:
              type: string
              format: date-time
              description: ISO 8601 timestamp when version was created
              example: '2024-01-15T10:30:00.000Z'
            title:
              type: string
              description: Version title
              example: Version 1.2.3
        configurationChanged:
          type: boolean
          description: Whether the project configuration has changed since the last version was saved. True means there are unsaved changes that should be versioned before proceeding with offers or documents.
          example: false
        configurationChangedAt:
          type: object
          description: ISO 8601 timestamp when the configuration last changed. Null if configuration has not changed since the last version was saved.
          example: null
          nullable: true
        productCount:
          type: number
          description: Number of products currently in the project
          example: 5
        componentCount:
          type: number
          description: Number of components currently in the project
          example: 10
        manualPositionCount:
          type: number
          description: Number of manual positions currently in the project
          example: 2
        discountCount:
          type: number
          description: Number of discounts currently applied to the project
          example: 1
        snapshotCount:
          type: number
          description: Total number of versions (snapshots) saved for the project
          example: 3
        documentCount:
          type: number
          description: Number of documents associated with the project
          example: 0
      required:
        - projectId
        - lastConfigurationSnapshot
        - configurationChanged
        - configurationChangedAt
        - productCount
        - componentCount
        - manualPositionCount
        - discountCount
        - snapshotCount
        - documentCount
    ProjectCategoryDto:
      type: object
      properties:
        id:
          type: string
          description: Project category ID
        name:
          type: string
          description: Project category name
        icon:
          type: string
          description: Icon identifier in the format `:icon_name:` (e.g., `:rocket:`, `:wrench:`, `:briefcase:`)
        description:
          type: object
          description: Category description
        translations:
          description: Translations
          type: array
          items:
            $ref: '#/components/schemas/TranslationDto'
      required:
        - id
        - name
        - icon
        - translations
    ProjectStatusDto:
      type: object
      properties:
        id:
          type: string
          description: Project status ID
        name:
          type: string
          description: Project status name
        icon:
          type: string
          description: Icon identifier in the format `:icon_name:` (e.g., `:hourglass_flowing_sand:`, `:check_mark:`, `:moneybag:`)
        description:
          type: object
          description: Status description
        type:
          type: string
          enum:
            - Sales
            - Requirements
            - Implementation
            - QualityManagement
            - Billing
            - Done
          description: Project status type
        translations:
          description: Translations
          type: array
          items:
            $ref: '#/components/schemas/TranslationDto'
      required:
        - id
        - name
        - icon
        - type
        - translations
    ProjectPhaseDto:
      type: object
      properties:
        id:
          type: string
          description: Project phase ID
        name:
          type: string
          description: Project phase name
        icon:
          type: string
          description: Icon identifier in the format `:icon_name:` (e.g., `:calendar:`, `:phone:`, `:trophy:`)
        description:
          type: object
          description: Phase description
        type:
          type: string
          enum:
            - new
            - inProgress
            - done
          description: Project phase type
        translations:
          description: Translations
          type: array
          items:
            $ref: '#/components/schemas/TranslationDto'
      required:
        - id
        - name
        - icon
        - type
        - translations
    ProjectResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Project ID
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Project name
          example: Website Redesign
        shortName:
          type: string
          description: Project short name (organization prefix + number)
          example: ACME-123
        shortNumber:
          type: number
          description: Project short number
          example: 123
        type:
          type: string
          description: Project type
          enum:
            - Single
            - Support
          example: Single
        valueGroup:
          type: string
          description: Value group classification
          enum:
            - DirectValue
            - IndirectValue
            - NonValue
            - Waste
          example: DirectValue
        description:
          type: string
          description: Project description (HTML)
          example: <h1>Project Description</h1><p>This is a comprehensive project.</p>
        icon:
          type: object
          description: Icon/emoji
          example: 🚀
          nullable: true
        categoryId:
          type: string
          description: ID of the project category
          example: 550e8400-e29b-41d4-a716-446655440001
        statusId:
          type: string
          description: ID of the project status
          example: 550e8400-e29b-41d4-a716-446655440002
        phaseId:
          type: object
          description: ID of the project phase
          example: 550e8400-e29b-41d4-a716-446655440003
          nullable: true
        organizationId:
          type: object
          description: ID of the organization
          example: 550e8400-e29b-41d4-a716-446655440004
          nullable: true
        organization:
          type: object
          description: Organization details
          additionalProperties: false
          properties:
            name:
              type: string
            shortName:
              type: string
            color:
              type: string
          example:
            name: ACME Corporation
            shortName: ACME
            color: '#FF5733'
          nullable: true
        defaultAccountableId:
          type: object
          description: ID of the default accountable user
          example: 550e8400-e29b-41d4-a716-446655440005
          nullable: true
        responsibleId:
          type: object
          description: ID of the responsible user
          example: 550e8400-e29b-41d4-a716-446655440006
          nullable: true
        guestAccess:
          type: boolean
          description: Whether guest access is enabled
          example: false
        deadline:
          type: object
          description: Project deadline
          example: '2024-12-31T23:59:59.000Z'
          nullable: true
        enableExpressQuotations:
          type: boolean
          description: Whether express quotations are enabled
          example: false
        createdAt:
          format: date-time
          type: string
          description: Creation timestamp
          example: '2024-01-01T00:00:00.000Z'
        editedAt:
          format: date-time
          type: string
          description: Last edit timestamp
          example: '2024-01-15T10:30:00.000Z'
        archivedAt:
          type: object
          description: Archived timestamp
          example: null
          nullable: true
        users:
          description: Array of user IDs assigned to the project
          example:
            - 550e8400-e29b-41d4-a716-446655440007
            - 550e8400-e29b-41d4-a716-446655440008
          type: array
          items:
            type: string
        teams:
          description: Array of team IDs assigned to the project
          example:
            - 550e8400-e29b-41d4-a716-446655440009
          type: array
          items:
            type: string
        taskTypes:
          description: Array of task type IDs enabled for the project
          example:
            - 550e8400-e29b-41d4-a716-44665544000a
            - 550e8400-e29b-41d4-a716-44665544000b
          type: array
          items:
            type: string
        activities:
          description: Array of time activity IDs enabled for the project
          example:
            - 550e8400-e29b-41d4-a716-44665544000c
            - 550e8400-e29b-41d4-a716-44665544000d
          type: array
          items:
            type: string
        customFields:
          type: object
          description: Custom fields as key-value pairs
          additionalProperties: true
          example:
            customField1: value1
            customField2: 123
        tags:
          description: Array of tags
          example:
            - urgent
            - high-priority
          type: array
          items:
            type: string
        color:
          type: string
          description: Project color (from organization or workspace default)
          example: '#FF5733'
        isFavorite:
          type: boolean
          description: Whether the project is marked as favorite by the current user
          example: false
      required:
        - id
        - name
        - shortName
        - shortNumber
        - type
        - valueGroup
        - description
        - icon
        - categoryId
        - statusId
        - phaseId
        - organizationId
        - organization
        - defaultAccountableId
        - responsibleId
        - guestAccess
        - deadline
        - enableExpressQuotations
        - createdAt
        - editedAt
        - archivedAt
        - users
        - teams
        - taskTypes
        - activities
        - customFields
        - tags
        - color
        - isFavorite
    CreateProjectDto:
      type: object
      properties:
        name:
          type: string
          description: Full name of the project. This is the primary display name used throughout the application.
          example: Website Redesign
        type:
          type: string
          description: Project type determines the project structure and workflow. Single projects are one-off initiatives with defined start and finish, usually planned and billed as quoted. Ongoing projects are continuous activities where tasks are handled and billed based on effort.
          enum:
            - Single
            - Support
          example: Single
        valueGroup:
          type: string
          description: Value group classification indicates how the project contributes to value creation. A - Direct value creation (external customer projects), B - Indirect value creation (internal projects that increase product/service value), C - Not value creating (administrative tasks), D - Waste (unproductive time).
          enum:
            - DirectValue
            - IndirectValue
            - NonValue
            - Waste
          example: DirectValue
        categoryId:
          type: string
          description: UUID of the project category. Categories classify projects by functional area or project type (e.g., Onboarding, Support, Implementation). Use the GET /projects/categories endpoint to retrieve available categories.
          example: 550e8400-e29b-41d4-a716-446655440000
        statusId:
          type: string
          description: UUID of the project status. Statuses represent the current progress stage of a project (e.g., Sales, Implementation, Billing, Done). Use the GET /projects/statuses endpoint to retrieve available statuses.
          example: 550e8400-e29b-41d4-a716-446655440001
        phaseId:
          type: object
          description: UUID of the project phase. Phases represent distinct stages within a project lifecycle (e.g., Planning, Development, Testing). Required for external single projects, optional otherwise. Use the GET /projects/phases endpoint to retrieve available phases.
          example: 550e8400-e29b-41d4-a716-446655440002
        organizationId:
          type: object
          description: UUID of the organization (client). Required for external projects - must be set to link the project to a client organization. Must be null for internal projects. Use the GET /organizations endpoint to retrieve available organizations.
          example: 550e8400-e29b-41d4-a716-446655440003
        description:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             The description will be automatically converted from HTML/Markdown to the internal IDoc format. When retrieved later via GET, it will be returned as HTML. Also accepts ProseMirror IDoc JSON (object or string).
          example: <h1>Project Description</h1><p>This is a comprehensive project for redesigning our company website.</p>
        icon:
          type: string
          description: Visual identifier for the project. Must be in the format `:icon_name:` (e.g., `:rocket:`, `:shopping_cart:`, `:mobile_phone:`). This icon is displayed in project lists and throughout the application. You can use standard emoji short names (e.g., `:rocket:` for 🚀, `:shopping_cart:` for 🛒) or custom icon names created in the workspace.
          example: ':rocket:'
        guestAccess:
          type: boolean
          description: Whether organization members (guests) can access this project. Only applicable for external (client) projects. When enabled, members of the linked organization can view and interact with the project and its tasks.
          example: false
          default: false
        deadline:
          type: object
          description: Project deadline date in ISO 8601 format (e.g., "2024-12-31T23:59:59.000Z"). Optional target completion date for the project.
          example: '2024-12-31T23:59:59.000Z'
        defaultAccountableId:
          type: object
          description: UUID of the user who is accountable for the project by default. This user will be set as accountable on new tasks created in this project unless overridden. Use the GET /workspace/users endpoint to retrieve available users.
          example: 550e8400-e29b-41d4-a716-446655440004
        responsibleId:
          type: object
          description: UUID of the user who is responsible for the project (e.g., project manager, sales manager). Use the GET /workspace/users endpoint to retrieve available users.
          example: 550e8400-e29b-41d4-a716-446655440005
        users:
          description: Array of user UUIDs who are direct project members and have access to this project. These users can see the project, be assigned to project tasks, create or edit tasks, and log time on tasks in the project. Include every employee who should work on, be assigned to, or log time on this project; do not include only managers unless managers are the only people who need project access. At least one user must be assigned. Use the GET /workspace/users endpoint to retrieve available users.
          example:
            - 550e8400-e29b-41d4-a716-446655440006
            - 550e8400-e29b-41d4-a716-446655440007
          type: array
          items:
            type: string
        teams:
          description: Array of team UUIDs assigned to the project. All members of these teams will have access to the project. Use the GET /teams endpoint to retrieve available teams.
          example:
            - 550e8400-e29b-41d4-a716-446655440008
          type: array
          items:
            type: string
        taskTypes:
          description: Array of task type UUIDs that can be used in this project (e.g., Feature, Bug, Management, Server issue). At least one task type must be specified. Use GET /tasks/types or GET /administration/task-settings/types to retrieve available task types.
          example:
            - 550e8400-e29b-41d4-a716-446655440009
            - 550e8400-e29b-41d4-a716-44665544000a
          type: array
          items:
            type: string
        activities:
          description: Array of time activity UUIDs that can be used when logging time in this project (e.g., Development, Testing, Management, Research). At least one activity must be specified. Use GET /administration/task-settings/activities to retrieve available activities.
          example:
            - 550e8400-e29b-41d4-a716-44665544000b
            - 550e8400-e29b-41d4-a716-44665544000c
          type: array
          items:
            type: string
        customFields:
          type: object
          description: Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use GET /administration/project-settings/custom-fields to retrieve available project custom fields and their configurations.
          additionalProperties: true
          example:
            customField1: value1
            customField2: 123
        tags:
          description: Array of tag strings for categorizing and filtering projects. Tags can represent topics, teams, or work types. Tags make it easier to filter and organize projects in the project overview.
          example:
            - urgent
            - high-priority
          type: array
          items:
            type: string
      required:
        - name
        - type
        - valueGroup
        - categoryId
        - statusId
        - description
        - guestAccess
        - users
        - taskTypes
        - activities
        - customFields
    PatchProjectDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the project
          example: Website Redesign v2
        type:
          type: string
          description: Project type - determines if project is internal or client-facing
          enum:
            - Single
            - Support
          example: Single
        valueGroup:
          type: string
          description: Value group classification for the project
          enum:
            - DirectValue
            - IndirectValue
            - NonValue
            - Waste
          example: DirectValue
        categoryId:
          type: string
          description: ID of the project category. Use GET /projects/categories to retrieve available categories.
          example: 550e8400-e29b-41d4-a716-446655440000
        statusId:
          type: string
          description: ID of the project status. Use GET /projects/statuses to retrieve available statuses.
          example: 550e8400-e29b-41d4-a716-446655440001
        phaseId:
          type: object
          description: ID of the project phase. Use GET /projects/phases to retrieve available phases.
          example: 550e8400-e29b-41d4-a716-446655440002
        organizationId:
          type: object
          description: ID of the organization (required for client projects, must be null for internal projects). Use GET /organizations to retrieve available organizations.
          example: 550e8400-e29b-41d4-a716-446655440003
        description:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             Also accepts ProseMirror IDoc JSON (object or string).
          example: <h1>Updated Description</h1><p>This is the updated project description.</p>
        icon:
          type: string
          description: Icon/emoji for the project
          example: 🎯
        guestAccess:
          type: boolean
          description: Whether guest access is enabled (only applicable for client projects)
          example: true
        deadline:
          type: object
          description: Project deadline date
          example: '2024-12-31T23:59:59.000Z'
        defaultAccountableId:
          type: object
          description: ID of the default accountable user. Use User.id from GET /workspace/users.
          example: 550e8400-e29b-41d4-a716-446655440004
        responsibleId:
          type: object
          description: ID of the responsible user. Use User.id from GET /workspace/users.
          example: 550e8400-e29b-41d4-a716-446655440005
        users:
          description: Array of User.id values assigned to the project. Use GET /workspace/users to retrieve workspace users.
          example:
            - 550e8400-e29b-41d4-a716-446655440006
            - 550e8400-e29b-41d4-a716-446655440007
          type: array
          items:
            type: string
        teams:
          description: Array of team IDs assigned to the project. Use GET /teams to retrieve available teams.
          example:
            - 550e8400-e29b-41d4-a716-446655440008
          type: array
          items:
            type: string
        taskTypes:
          description: Array of task type IDs enabled for the project. Use GET /tasks/types or GET /administration/task-settings/types.
          example:
            - 550e8400-e29b-41d4-a716-446655440009
            - 550e8400-e29b-41d4-a716-44665544000a
          type: array
          items:
            type: string
        activities:
          description: Array of time activity IDs enabled for the project. Use GET /administration/task-settings/activities.
          example:
            - 550e8400-e29b-41d4-a716-44665544000b
            - 550e8400-e29b-41d4-a716-44665544000c
          type: array
          items:
            type: string
        customFields:
          type: object
          description: Project custom fields as key-value pairs. Keys must be project custom field IDs from GET /administration/project-settings/custom-fields.
          additionalProperties: true
          example:
            customField1: updated value
            customField2: 456
        tags:
          description: Array of tags
          example:
            - urgent
            - high-priority
            - review-needed
          type: array
          items:
            type: string
        enableExpressQuotations:
          type: boolean
          description: Whether express quotations are enabled for this project. Express quotations allow quick pricing of tasks without full project planning. Only applicable for external projects.
          example: false
      required:
        - customFields
    SupportContingentWithDetailsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the support contingent (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        projectId:
          type: string
          description: ID of the project this contingent belongs to (UUID)
          example: 550e8400-e29b-41d4-a716-446655440001
        title:
          type: string
          description: Title or name of the support contingent plan
          example: Maintenance flatrate
        fromDate:
          type: string
          description: Start date of the contingent period (ISO 8601 date format)
          example: '2025-01-01'
        toDate:
          type: string
          description: End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.
          example: '2025-12-31'
        frequency:
          type: number
          description: Frequency in months for the contingent period
          example: 12
        hours:
          type: number
          description: Total hours allocated for the contingent period
          example: 120
        transferable:
          type: boolean
          description: Whether unused hours can be transferred to the next period
          example: false
        isActive:
          type: boolean
          description: Whether the contingent is currently active (today is within fromDate and toDate)
          example: true
        availableHours:
          type: number
          description: Available hours for the current period (calculated based on transferable logic and consumed hours)
          example: 85.5
        consumedHours:
          type: number
          description: Consumed hours in the current period (from invoices)
          example: 34.5
        potentiallyUsedByTasks:
          type: number
          description: Hours that will be potentially used by unbilled tasks (calculated based on task assignment logic)
          example: 17.5
        availableByContract:
          type: number
          description: Available hours from the current period contract (baseHours - consumed in current period)
          example: 40
        transferableFromPrevious:
          type: number
          description: Transferable hours from previous periods (only applicable for transferable contingents)
          example: 129
        currentPeriodStart:
          type: string
          description: Start date of the current period (ISO 8601 date format)
          example: '2025-11-01'
        currentPeriodEnd:
          type: string
          description: End date of the current period (ISO 8601 date format)
          example: '2025-11-30'
        currentPeriodHasNoEndDate:
          type: boolean
          description: Flag indicating if the current period has no end date (for frequency=0 contingents with null toDate). When true, currentPeriodEnd represents the current date for display purposes, but the period is actually open-ended.
          example: false
        createdAt:
          type: string
          description: ISO 8601 timestamp when the contingent was created
          example: '2024-01-15T10:00:00Z'
        updatedAt:
          type: string
          description: ISO 8601 timestamp when the contingent was last updated
          example: '2024-01-20T14:30:00Z'
      required:
        - id
        - projectId
        - title
        - fromDate
        - frequency
        - hours
        - transferable
        - isActive
        - availableHours
        - consumedHours
        - potentiallyUsedByTasks
        - availableByContract
        - transferableFromPrevious
        - createdAt
        - updatedAt
    SupportContingentPeriodTaskDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the task (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        shortNumber:
          type: number
          description: Short number of the task
          example: 1234
        title:
          type: string
          description: Title of the task
          example: Fix bug in payment module
        hoursBilled:
          type: number
          description: Hours billed/consumed for this task
          example: 5.5
        invoiceNumber:
          type: object
          description: Invoice number if the task is billed, null if unbilled
          example: INV-2025-001
        isUnbilled:
          type: boolean
          description: Flag indicating if this task is unbilled (in progress)
          example: false
      required:
        - id
        - shortNumber
        - title
        - hoursBilled
    SupportContingentPeriodDto:
      type: object
      properties:
        periodStart:
          type: string
          description: Start date of the period (ISO 8601 date format)
          example: '2025-11-01'
        periodEnd:
          type: string
          description: End date of the period (ISO 8601 date format)
          example: '2025-11-30'
        hours:
          type: number
          description: Hours allocated for this period
          example: 40
        hoursFromPrevious:
          type: number
          description: Hours transferred from previous period (only applicable for transferable contingents)
          example: 10
        consumedHours:
          type: number
          description: Hours consumed in this period (from invoices)
          example: 15.5
        isCurrentPeriod:
          type: boolean
          description: Flag indicating if this is the current active period
          example: true
        hasNoEndDate:
          type: boolean
          description: Flag indicating if this period has no end date (for frequency=0 contingents with null toDate). When true, periodEnd represents the current date for display purposes, but the period is actually open-ended.
          example: false
        tasks:
          description: List of tasks billed or unbilled in this period
          type: array
          items:
            $ref: '#/components/schemas/SupportContingentPeriodTaskDto'
      required:
        - periodStart
        - periodEnd
        - hours
        - hoursFromPrevious
        - consumedHours
        - isCurrentPeriod
        - tasks
    SupportContingentWithBreakdownResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the support contingent (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        projectId:
          type: string
          description: ID of the project this contingent belongs to (UUID)
          example: 550e8400-e29b-41d4-a716-446655440001
        title:
          type: string
          description: Title or name of the support contingent plan
          example: Maintenance flatrate
        fromDate:
          type: string
          description: Start date of the contingent period (ISO 8601 date format)
          example: '2025-01-01'
        toDate:
          type: string
          description: End date of the contingent period (ISO 8601 date format). If not set, the contingent is active forever.
          example: '2025-12-31'
        frequency:
          type: number
          description: Frequency in months for the contingent period
          example: 12
        price:
          type: number
          description: Total price for the contingent period
          example: 10000
        hours:
          type: number
          description: Total hours allocated for the contingent period
          example: 120
        rate:
          type: number
          description: Calculated hourly rate (price / hours). Automatically computed.
          example: 83.33
        transferable:
          type: boolean
          description: Whether unused hours can be transferred to the next period
          example: false
        createdAt:
          type: string
          description: ISO 8601 timestamp when the contingent was created
          example: '2024-01-15T10:00:00Z'
        updatedAt:
          type: string
          description: ISO 8601 timestamp when the contingent was last updated
          example: '2024-01-20T14:30:00Z'
        periods:
          description: Period-by-period breakdown of the contingent, from start until present day
          type: array
          items:
            $ref: '#/components/schemas/SupportContingentPeriodDto'
      required:
        - id
        - projectId
        - title
        - fromDate
        - frequency
        - price
        - hours
        - rate
        - transferable
        - createdAt
        - updatedAt
        - periods
    ObjectTypeListItemDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        slug:
          type: string
        icon:
          type: object
          nullable: true
        sort:
          type: number
        createdAt:
          type: string
        instanceCount:
          type: number
        statusCount:
          type: number
      required:
        - id
        - name
        - slug
        - icon
        - sort
        - createdAt
        - instanceCount
        - statusCount
    CreateObjectStatusDto:
      type: object
      properties:
        name:
          type: string
        color:
          type: string
          description: Badge color (hex or token)
      required:
        - name
        - color
    UpdateObjectStatusDto:
      type: object
      properties:
        name:
          type: string
        color:
          type: string
      required:
        - name
        - color
    CreateObjectTypeDto:
      type: object
      properties:
        name:
          type: string
        slug:
          type: string
          description: URL slug; auto-generated from name if omitted
        icon:
          type: object
          nullable: true
      required:
        - name
    UpdateObjectTypeDto:
      type: object
      properties:
        name:
          type: string
        slug:
          type: string
        icon:
          type: object
          nullable: true
      required:
        - name
    ObjectTypeSummaryDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        slug:
          type: string
          nullable: true
        icon:
          type: object
          nullable: true
      required:
        - id
        - name
        - slug
        - icon
    ObjectStatusSummaryDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        color:
          type: string
      required:
        - id
        - name
        - color
    OrganizationSummaryInObjectDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        shortName:
          type: string
      required:
        - id
        - name
        - shortName
    ProjectSummaryInObjectDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        shortNumber:
          type: number
      required:
        - id
        - name
        - shortNumber
    ObjectListItemDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        externalId:
          type: object
          nullable: true
        tags:
          type: array
          items:
            type: string
        createdAt:
          type: string
        editedAt:
          type: string
        objectType:
          $ref: '#/components/schemas/ObjectTypeSummaryDto'
        status:
          $ref: '#/components/schemas/ObjectStatusSummaryDto'
        organization:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/OrganizationSummaryInObjectDto'
        project:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/ProjectSummaryInObjectDto'
      required:
        - id
        - name
        - externalId
        - tags
        - createdAt
        - editedAt
        - objectType
        - status
        - organization
        - project
    PaginatedObjectsResponseDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/ObjectListItemDto'
        total:
          type: number
        page:
          type: number
        pageSize:
          type: number
      required:
        - items
        - total
        - page
        - pageSize
    ObjectFileCategorySummaryDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
      required:
        - id
        - name
    ObjectFileResponseDto:
      type: object
      properties:
        id:
          type: string
          description: ObjectFile join row id (use for DELETE)
        category:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/ObjectFileCategorySummaryDto'
        description:
          type: object
          nullable: true
        createdAt:
          type: string
        uploadedBy:
          type: string
        file:
          type: object
      required:
        - id
        - category
        - description
        - createdAt
        - uploadedBy
        - file
    AttachObjectFileDto:
      type: object
      properties:
        fileId:
          type: string
          description: File id from POST /workspace/upload
        categoryId:
          type: object
          nullable: true
          description: Workspace object file category id
        description:
          type: object
          nullable: true
      required:
        - fileId
    AssignObjectsToOrganizationDto:
      type: object
      properties: {}
    UnassignObjectFromOrganizationDto:
      type: object
      properties: {}
    AssignObjectsToProjectDto:
      type: object
      properties: {}
    UnassignObjectFromProjectDto:
      type: object
      properties: {}
    ObjectJournalResponseDto:
      type: object
      properties:
        id:
          type: string
        objectId:
          type: string
        createdAt:
          type: string
        createdBy:
          type: string
        lastUpdated:
          type: string
        mood:
          type: string
          enum:
            - Sad
            - Neutral
            - Happy
        reminder:
          type: object
          nullable: true
        body:
          type: string
          description: Body as HTML
      required:
        - id
        - objectId
        - createdAt
        - createdBy
        - lastUpdated
        - mood
        - reminder
        - body
    CreateObjectJournalDto:
      type: object
      properties:
        body:
          type: string
          description: Journal entry body (HTML or Markdown). Converted to internal format for storage.
          example: <h1>Update</h1><p>Notes</p>
        mood:
          type: string
          enum:
            - Sad
            - Neutral
            - Happy
        reminder:
          type: string
          example: '2024-12-31'
      required:
        - body
    PatchObjectJournalDto:
      type: object
      properties:
        body:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             If omitted, body is unchanged.
        mood:
          type: string
          enum:
            - Sad
            - Neutral
            - Happy
        reminder:
          type: object
          nullable: true
          example: '2024-12-31'
    ObjectHistoryUserPublicDto:
      type: object
      properties:
        id:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        avatarId:
          type: object
          nullable: true
      required:
        - id
        - firstName
        - lastName
        - avatarId
    ObjectHistoryFieldChangePublicDto:
      type: object
      properties:
        fieldKey:
          type: string
        label:
          type: string
        oldText:
          type: string
        newText:
          type: string
        oldStatusColor:
          type: object
          nullable: true
          description: Hex color when fieldKey is statusId (old value)
        newStatusColor:
          type: object
          nullable: true
          description: Hex color when fieldKey is statusId (new value)
      required:
        - fieldKey
        - label
        - oldText
        - newText
    ObjectHistoryEntryPublicDto:
      type: object
      properties:
        id:
          type: string
        action:
          type: string
          enum:
            - Update
            - Create
            - Delete
            - Archive
            - Restore
            - Login
            - Logout
        createdAt:
          type: string
          description: ISO 8601 timestamp
        user:
          $ref: '#/components/schemas/ObjectHistoryUserPublicDto'
        changes:
          type: array
          items:
            $ref: '#/components/schemas/ObjectHistoryFieldChangePublicDto'
      required:
        - id
        - action
        - createdAt
        - user
        - changes
    PaginatedObjectHistoryResponseDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/ObjectHistoryEntryPublicDto'
        total:
          type: number
        page:
          type: number
        pageSize:
          type: number
      required:
        - items
        - total
        - page
        - pageSize
    TaskLinkedToObjectDto:
      type: object
      properties:
        id:
          type: string
        shortNumber:
          type: number
        title:
          type: string
      required:
        - id
        - shortNumber
        - title
    TasksByObjectResponseDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/TaskLinkedToObjectDto'
      required:
        - items
    LinkObjectToTaskPublicDto:
      type: object
      properties:
        objectId:
          type: string
          format: uuid
          description: Object instance UUID
        taskId:
          type: string
          format: uuid
          description: Task UUID
      required:
        - objectId
        - taskId
    LinkObjectToTaskPublicResponseDto:
      type: object
      properties:
        objectId:
          type: string
          format: uuid
        taskId:
          type: string
          format: uuid
      required:
        - objectId
        - taskId
    LinkObjectsBatchToTaskPublicDto:
      type: object
      properties:
        taskId:
          type: string
          format: uuid
          description: Task UUID
        objectIds:
          description: Object instance UUIDs (1–200; duplicates are ignored server-side)
          type: array
          items:
            type: string
      required:
        - taskId
        - objectIds
    LinkObjectsBatchPublicResponseDto:
      type: object
      properties:
        linked:
          type: number
          description: Number of new links created (already-linked pairs are skipped)
      required:
        - linked
    ObjectInstanceResponseDto:
      type: object
      properties:
        id:
          type: string
        objectType:
          $ref: '#/components/schemas/ObjectTypeSummaryDto'
        status:
          $ref: '#/components/schemas/ObjectStatusSummaryDto'
        organizationId:
          type: object
          nullable: true
        organization:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/OrganizationSummaryInObjectDto'
        projectId:
          type: object
          nullable: true
        project:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/ProjectSummaryInObjectDto'
        name:
          type: string
        externalId:
          type: object
          nullable: true
        customFields:
          type: object
        tags:
          type: array
          items:
            type: string
        createdAt:
          type: string
        editedAt:
          type: string
      required:
        - id
        - objectType
        - status
        - organizationId
        - organization
        - projectId
        - project
        - name
        - externalId
        - customFields
        - tags
        - createdAt
        - editedAt
    CreateObjectDto:
      type: object
      properties:
        objectTypeId:
          type: string
          description: Object type UUID
        organizationId:
          type: object
          nullable: true
          description: Organization UUID; omit or null for internal object
        projectId:
          type: object
          nullable: true
          description: Assigned project UUID
        statusId:
          type: string
          description: Status UUID (must belong to the object type)
        name:
          type: string
          description: Display name
        externalId:
          type: object
          nullable: true
          description: External system identifier
        customFields:
          type: object
          description: Custom field values keyed by field id
        tags:
          type: array
          items:
            type: string
      required:
        - objectTypeId
        - statusId
        - name
    UpdateObjectDto:
      type: object
      properties:
        organizationId:
          type: object
          nullable: true
        projectId:
          type: object
          nullable: true
        statusId:
          type: string
          description: Status UUID (must belong to the object type)
        name:
          type: string
        externalId:
          type: object
          nullable: true
        customFields:
          type: object
          description: Custom field values keyed by field id
        tags:
          type: array
          items:
            type: string
    GetDocumentSettingsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Workspace ID
          example: 123e4567-e89b-12d3-a456-426614174000
        documentFont:
          type: string
          description: 'Font family used for all documents generated by Leadtime. Available options: Inter (default), Degular, Roboto, Arimo, Open Sans, PT Sans, Oswald, EB Garamond, Permanent Marker, IBM Plex Mono.'
          example: Inter
          enum:
            - Inter
            - Degular
            - Roboto
            - Arimo
            - Open Sans
            - PT Sans
            - Oswald
            - EB Garamond
            - Permanent Marker
            - IBM Plex Mono
        documentLogoUrl:
          type: object
          description: Public URL to the workspace logo image. This logo appears on all generated documents. If null, the Leadtime logo is used by default. The URL is publicly accessible without authentication.
          example: https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000
          nullable: true
        projectDocumentEnableToc:
          type: boolean
          description: Whether to automatically include a table of contents at the beginning of project documents (quotes, specifications, etc.). When enabled, a TOC is generated based on document headings.
          example: true
        projectDocumentTitlePage:
          type: boolean
          description: Whether to automatically include a title page in project documents. When enabled, a cover page is added as the first page with project and organization information.
          example: true
        projectDocumentHeadingStyle:
          type: string
          description: 'Controls how headings are numbered and formatted in project documents. Options: `Normal` (no numbering), `Sequential` (1., 1.1., 1.1.1.), `SequentialFromSecondLevel` (top heading unnumbered), `SequentialWithParagraphs` (§ 1., § 1.1. - ideal for contracts).'
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
        specificationBackgroundUrl:
          type: object
          description: 'Public URL to the background image used on specification document cover pages. Recommended size: 820 × 600 pixels. Formats: PNG, JPG, or SVG. The URL is publicly accessible without authentication.'
          example: https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000
          nullable: true
        estimateBackgroundUrl:
          type: object
          description: 'Public URL to the background image used on estimate/proposal document cover pages. Recommended size: 820 × 600 pixels. Formats: PNG, JPG, or SVG. The URL is publicly accessible without authentication.'
          example: https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000
          nullable: true
        customEditorBackgroundUrl:
          type: object
          description: 'Public URL to the background image used on custom editor document cover pages (documents created with the template system). Recommended size: 820 × 600 pixels. Formats: PNG, JPG, or SVG. The URL is publicly accessible without authentication.'
          example: https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000
          nullable: true
        documentLanguage:
          type: string
          description: Default language code for project documents (e.g., "en", "de"). Used when document language is not specified.
          example: en
        offerTexts:
          description: Array of language-specific default offer texts. Each entry contains a language code and default intro/outro texts (returned as HTML).
          type: array
          items:
            type: object
      required:
        - id
        - documentFont
        - documentLogoUrl
        - projectDocumentEnableToc
        - projectDocumentTitlePage
        - projectDocumentHeadingStyle
        - specificationBackgroundUrl
        - estimateBackgroundUrl
        - customEditorBackgroundUrl
        - documentLanguage
        - offerTexts
    PatchDocumentSettingsDto:
      type: object
      properties:
        documentFont:
          type: string
          description: 'Font family to use for all documents. Must be one of: Inter, Degular, Roboto, Arimo, Open Sans, PT Sans, Oswald, EB Garamond, Permanent Marker, IBM Plex Mono. If not provided, current value is preserved.'
          example: Inter
          enum:
            - Inter
            - Degular
            - Roboto
            - Arimo
            - Open Sans
            - PT Sans
            - Oswald
            - EB Garamond
            - Permanent Marker
            - IBM Plex Mono
        documentLogoId:
          type: object
          description: UUID of the logo image file uploaded via POST /api/public/workspace/upload. This logo appears on all generated documents. Set to `null` to remove the logo (defaults to Leadtime logo). If not provided, current value is preserved.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        projectDocumentEnableToc:
          type: boolean
          description: Whether to automatically include a table of contents in project documents. When enabled, a TOC is generated based on document headings. If not provided, current value is preserved.
          example: true
        projectDocumentTitlePage:
          type: boolean
          description: Whether to automatically include a title page in project documents. When enabled, a cover page is added as the first page. If not provided, current value is preserved.
          example: true
        projectDocumentHeadingStyle:
          type: string
          description: 'Heading numbering style for project documents. Options: `Normal` (no numbering), `Sequential` (1., 1.1., 1.1.1.), `SequentialFromSecondLevel` (top heading unnumbered), `SequentialWithParagraphs` (§ 1., § 1.1.). If not provided, current value is preserved.'
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
        specificationBackgroundId:
          type: object
          description: 'UUID of the specification background image file uploaded via POST /api/public/workspace/upload. Used on specification document cover pages. Recommended size: 820 × 600 pixels. Set to `null` to remove. If not provided, current value is preserved.'
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        estimateBackgroundId:
          type: object
          description: 'UUID of the estimate/proposal background image file uploaded via POST /api/public/workspace/upload. Used on estimate document cover pages. Recommended size: 820 × 600 pixels. Set to `null` to remove. If not provided, current value is preserved.'
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        customEditorBackgroundId:
          type: object
          description: 'UUID of the custom editor background image file uploaded via POST /api/public/workspace/upload. Used on template-based document cover pages. Recommended size: 820 × 600 pixels. Set to `null` to remove. If not provided, current value is preserved.'
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        documentLanguage:
          type: string
          description: 'Default language code for project documents. Must be one of: en, de. If not provided, current value is preserved.'
          example: en
        offerTexts:
          description: Array of language-specific offer texts. Each entry must include a language code and intro/outro texts (HTML or Markdown). If not provided, current values are preserved.
          type: array
          items:
            type: object
    EstimateGridItemResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the estimate. UUID format.
          example: 123e4567-e89b-12d3-a456-426614174000
        type:
          type: string
          description: Type of estimate. PROJECT_DOCUMENT for detailed project estimates, EXPRESS_QUOTATION for quick ticket-based estimates.
          enum:
            - PROJECT_DOCUMENT
            - EXPRESS_QUOTATION
          example: PROJECT_DOCUMENT
        title:
          type: string
          description: Human-readable title of the estimate. Used for quick search and display purposes.
          example: Website Redesign Estimate
        organizationId:
          type: string
          description: Unique identifier of the organization (customer) this estimate belongs to. UUID format.
          example: 123e4567-e89b-12d3-a456-426614174001
        organizationName:
          type: string
          description: Display name of the organization. Provided for convenience to avoid additional lookups.
          example: Acme Corp
        projectId:
          type: string
          description: Unique identifier of the project this estimate is linked to. UUID format. Required for both estimate types.
          example: 123e4567-e89b-12d3-a456-426614174002
        projectName:
          type: string
          description: Display name of the project. Provided for convenience to avoid additional lookups.
          example: Website Redesign
        taskId:
          type: string
          description: Unique identifier of the task (ticket) this express quotation is linked to. Only present for EXPRESS_QUOTATION type estimates. UUID format.
          example: 123e4567-e89b-12d3-a456-426614174003
        taskShortNumber:
          type: number
          description: 'Short numeric identifier of the task. Only present for EXPRESS_QUOTATION type estimates. Used for display purposes (e.g., "Task #42").'
          example: 42
        createdAt:
          type: string
          description: 'ISO 8601 timestamp indicating when the estimate was created. Format: YYYY-MM-DDTHH:mm:ss.sssZ'
          example: '2024-11-06T12:00:00.000Z'
        status:
          type: string
          description: 'Current status of the estimate. Indicates where the estimate is in the sales process: Draft (being prepared), WaitingForApproval (pending internal approval), Final (approved), Pending (sent to customer), Accepted (customer accepted), Rejected (customer rejected), or Parked (temporarily set aside).'
          enum:
            - Draft
            - Final
            - WaitingForApproval
            - Pending
            - Accepted
            - Rejected
            - Parked
          example: Draft
        total:
          type: number
          description: Total price for express quotations. This is the sum of all price components (fixed + subscriptions + per-unit). Only present for EXPRESS_QUOTATION type estimates.
          example: 5000
        priceFixed:
          type: number
          description: Fixed price component representing one-time charges for services or products. This is a single payment amount, not recurring.
          example: 3000
        priceSubscriptions:
          type: number
          description: Subscription price component representing recurring charges (monthly, quarterly, semi-annually, or annually). The total of all subscription items in the estimate.
          example: 500
        pricePerUnit:
          type: number
          description: Per-unit price component representing charges based on quantity (e.g., price per user license, price per workspace, price per API call). The total of all per-unit items in the estimate.
          example: 100
        priceSubscriptionsDetail:
          type: object
          description: Detailed breakdown of subscription prices by product or service name. Key-value pairs where keys are product/service names and values are their subscription prices. Useful for understanding which items contribute to the total subscription price. Null if no subscription items exist.
          example:
            hosting: 200
            support: 300
        pricePerUnitDetail:
          type: object
          description: Detailed breakdown of per-unit prices by product or service name. Key-value pairs where keys are product/service names and values are their per-unit prices. Useful for understanding which items contribute to the total per-unit price. Null if no per-unit items exist.
          example:
            user-licenses: 50
            api-calls: 50
        createdById:
          type: string
          description: Unique identifier of the user who created this estimate. UUID format.
          example: 123e4567-e89b-12d3-a456-426614174004
        createdByName:
          type: string
          description: Full name of the user who created this estimate. Provided for convenience to avoid additional user lookups.
          example: John Doe
      required:
        - id
        - type
        - title
        - organizationId
        - projectId
        - createdAt
        - status
    EstimateGridResponseDto:
      type: object
      properties:
        items:
          description: Array of estimate items matching the query parameters. Each item represents either a project document estimate or an express quotation.
          type: array
          items:
            $ref: '#/components/schemas/EstimateGridItemResponseDto'
        total:
          type: number
          description: 'Total number of estimate items matching the query filters across all pages. Use this value to calculate total pages: Math.ceil(total / pageSize).'
          example: 150
        page:
          type: number
          description: Current page number (1-based). Used for pagination navigation.
          example: 1
        pageSize:
          type: number
          description: Number of items returned per page. Determines how many estimate items are included in the items array.
          example: 10
        totalPriceFixed:
          type: number
          description: Aggregated sum of all fixed price components across all filtered estimate items. This represents the total one-time revenue potential from fixed-price items. Calculated by summing priceFixed from all items in the filtered result set.
          example: 125000
        totalPriceSubscriptions:
          type: number
          description: Aggregated sum of all subscription price components across all filtered estimate items. This represents the total recurring revenue potential from subscription items. Calculated by summing priceSubscriptions from all items in the filtered result set.
          example: 45000
        totalPricePerUnit:
          type: number
          description: Aggregated sum of all per-unit price components across all filtered estimate items. This represents the total revenue potential from per-unit items. Calculated by summing pricePerUnit from all items in the filtered result set.
          example: 15000
      required:
        - items
        - total
        - page
        - pageSize
        - totalPriceFixed
        - totalPriceSubscriptions
        - totalPricePerUnit
    FeatureInfoDto:
      type: object
      properties:
        featureCode:
          type: string
          description: Feature code identifier. Identifies which workspace feature this information applies to (e.g., customFields, advancedReports, apiAccess).
          enum:
            - HELPDESK
            - API
            - AGENT
        label:
          type: string
          description: Human-readable display label for the feature. Localized based on workspace language settings.
        enabled:
          type: boolean
          description: Whether this feature is currently enabled and active for the workspace. Disabled features are not available for use.
        monthlyPrice:
          type: object
          description: Monthly subscription price for this feature in cents. Multiply by 100 to get the display amount. Null if feature is not available on monthly billing.
          nullable: true
        yearlyPrice:
          type: object
          description: Yearly subscription price for this feature in cents. Multiply by 100 to get the display amount. Null if feature is not available on yearly billing.
          nullable: true
        currency:
          type: string
          description: ISO 4217 currency code (e.g., USD, EUR, GBP). All prices are in this currency.
        description:
          type: string
          description: Optional description explaining what this feature provides and how it benefits the workspace.
      required:
        - featureCode
        - label
        - enabled
        - monthlyPrice
        - yearlyPrice
        - currency
    SubscriptionItemDto:
      type: object
      properties:
        productName:
          type: string
          description: Product name from Stripe. Identifies what is being billed (e.g., "Leadtime Pro Plan", "Additional Seats").
        quantity:
          type: number
          description: Quantity of this item in the subscription. For seat-based billing, this represents the number of seats.
        unitAmount:
          type: number
          description: Price per unit in cents. Multiply by 100 to get the display amount. This is the price for a single unit before quantity multiplication.
        currency:
          type: string
          description: ISO 4217 currency code (e.g., USD, EUR, GBP). All amounts for this item are in this currency.
        interval:
          type: object
          description: 'Billing interval for this item. Values: "month" for monthly billing, "year" for yearly billing. Null if not applicable.'
          nullable: true
        totalAmount:
          type: number
          description: Total amount for this line item in cents (unitAmount × quantity). Multiply by 100 to get the display amount.
      required:
        - productName
        - quantity
        - unitAmount
        - currency
        - interval
        - totalAmount
    SubscriptionDetailsDto:
      type: object
      properties:
        status:
          type: string
          description: 'Current subscription status from Stripe. Common values: "active" (subscription is active and paid), "trialing" (in trial period), "past_due" (payment failed), "canceled" (subscription canceled), "unpaid" (payment required).'
        currentPeriodStart:
          format: date-time
          type: string
          description: Start date of the current billing period. ISO 8601 format. This is when the current billing cycle began.
        currentPeriodEnd:
          format: date-time
          type: string
          description: End date of the current billing period. ISO 8601 format. This is when the current billing cycle ends and the next invoice will be generated.
        items:
          description: List of all items included in this subscription. Each item represents a product or service being billed (e.g., base plan, additional seats, add-ons).
          type: array
          items:
            $ref: '#/components/schemas/SubscriptionItemDto'
        dashboardUrl:
          type: string
          description: Direct URL to view and manage this subscription in the Stripe dashboard. Opens in a new window/tab. Only available for enterprise workspaces.
      required:
        - status
        - currentPeriodStart
        - currentPeriodEnd
        - items
        - dashboardUrl
    TaxInfoDto:
      type: object
      properties:
        description:
          type: string
          description: Human-readable description of the tax. Typically includes the tax type and jurisdiction (e.g., "VAT", "Sales Tax", "GST").
        amount:
          type: number
          description: Tax amount in cents. Multiply by 100 to get the display amount. This is the total tax for this specific tax line.
        percentage:
          type: number
          description: Tax rate percentage (e.g., 19.0 for 19% VAT). Optional - may not be available for all tax types.
      required:
        - description
        - amount
    InvoiceLineDto:
      type: object
      properties:
        description:
          type: string
          description: 'Human-readable description of what this line item represents. Examples: "Leadtime Pro Plan - Monthly", "Additional Seats (5)", "Setup Fee".'
        quantity:
          type: number
          description: Quantity of this item. For seat-based billing, this represents the number of seats. For fixed-price items, typically 1.
        unitAmount:
          type: object
          description: Price per unit in cents before quantity multiplication. Multiply by 100 to get the display amount. Null if this is a fixed total amount line item.
          nullable: true
        totalAmount:
          type: number
          description: Total amount for this line item in cents (unitAmount × quantity, including any discounts). Multiply by 100 to get the display amount.
        amountExcludingTax:
          type: number
          description: Total amount excluding tax in cents. Only present if tax is applied. Multiply by 100 to get the display amount.
        taxes:
          description: Tax information specific to this line item. Multiple tax rates may apply to a single line item in some jurisdictions.
          type: array
          items:
            $ref: '#/components/schemas/TaxInfoDto'
      required:
        - description
        - quantity
        - unitAmount
        - totalAmount
    TaxRateDto:
      type: object
      properties:
        displayName:
          type: string
          description: 'Human-readable name for this tax rate. Examples: "VAT", "Sales Tax", "GST".'
        percentage:
          type: number
          description: 'Tax rate as a percentage. Example: 19.0 represents 19% tax. Used to calculate tax amounts.'
      required:
        - displayName
        - percentage
    TaxLineDto:
      type: object
      properties:
        description:
          type: string
          description: Human-readable description of the tax line. Typically includes tax type and jurisdiction.
        amount:
          type: number
          description: Total tax amount for this tax line in cents. Multiply by 100 to get the display amount. This is the sum of all taxes at this rate.
        rate:
          description: Detailed tax rate information including display name and percentage. Optional - may not be available for all tax types.
          allOf:
            - $ref: '#/components/schemas/TaxRateDto'
      required:
        - description
        - amount
    NextInvoiceDto:
      type: object
      properties:
        periodStart:
          type: object
          description: Start date of the billing period this invoice covers. ISO 8601 format. Null if period information is not available.
          nullable: true
        periodEnd:
          type: object
          description: End date of the billing period this invoice covers. ISO 8601 format. Null if period information is not available.
          nullable: true
        dueDate:
          type: object
          description: Date when payment is due for this invoice. ISO 8601 format. Null if due date is not yet determined.
          nullable: true
        lines:
          description: All line items that will appear on the upcoming invoice. Includes base plan, seats, add-ons, and any other charges.
          type: array
          items:
            $ref: '#/components/schemas/InvoiceLineDto'
        subtotal:
          type: number
          description: Subtotal amount in cents before taxes and discounts. Multiply by 100 to get the display amount. Sum of all line item totals.
        subtotal_excluding_tax:
          type: number
          description: Subtotal excluding tax in cents. Only present if tax is applied. Multiply by 100 to get the display amount.
        taxLines:
          description: All tax lines that will appear on the invoice. Each line represents a different tax rate or tax type.
          type: array
          items:
            $ref: '#/components/schemas/TaxLineDto'
        tax:
          type: number
          description: Total tax amount in cents. Multiply by 100 to get the display amount. Sum of all tax line amounts.
        total:
          type: number
          description: Final total amount due in cents including all line items and taxes. Multiply by 100 to get the display amount. This is the amount that will be charged.
        total_excluding_tax:
          type: number
          description: Total amount excluding tax in cents. Only present if tax is applied. Multiply by 100 to get the display amount.
        currency:
          type: string
          description: ISO 4217 currency code (e.g., USD, EUR, GBP). All amounts are in this currency.
      required:
        - periodStart
        - periodEnd
        - dueDate
        - lines
        - subtotal
        - taxLines
        - tax
        - total
        - currency
    LatestInvoiceDto:
      type: object
      properties:
        id:
          type: string
          description: Unique Stripe invoice identifier. Use this to reference the invoice in Stripe API calls or support requests.
        number:
          type: string
          description: Human-readable invoice number. This is the invoice number displayed to customers and used for accounting purposes.
        status:
          type: string
          description: 'Current status of the invoice. Common values: "paid" (successfully paid), "open" (awaiting payment), "void" (canceled), "uncollectible" (payment failed).'
        amount:
          type: number
          description: Total amount due for this invoice in cents. Multiply by 100 to get the display amount. This is the amount that was or will be charged.
        currency:
          type: string
          description: ISO 4217 currency code (e.g., USD, EUR, GBP). All amounts for this invoice are in this currency.
        created:
          format: date-time
          type: string
          description: Date and time when the invoice was created. ISO 8601 format.
        dueDate:
          type: object
          description: Date when payment was or is due for this invoice. ISO 8601 format. Null if due date is not applicable.
          nullable: true
        paidAt:
          type: object
          description: Date and time when the invoice was successfully paid. ISO 8601 format. Null if invoice is not yet paid or payment failed.
          nullable: true
        viewUrl:
          type: string
          description: Direct URL to view this invoice in Stripe customer portal. Opens the invoice in a web browser. Use this to allow customers to view or download their invoices.
      required:
        - id
        - number
        - status
        - amount
        - currency
        - created
        - dueDate
        - paidAt
        - viewUrl
    SubscriptionStatusResponseDto:
      type: object
      properties:
        billingMode:
          type: string
          description: 'Workspace billing tier. Values: "free" (no paid subscription), "regular" (standard paid subscription), "enterprise" (custom enterprise agreement). Determines what features and billing details are available.'
          enum:
            - free
            - regular
            - enterprise
        billingStatus:
          type: string
          description: 'Current subscription status. Values: "active" (subscription is active and paid), "trialing" (in trial period), "past_due" (payment failed), "canceled" (subscription canceled), "unpaid" (payment required). Only present for paid workspaces.'
          enum:
            - trialing
            - active
            - past_due
            - canceled
        planCode:
          type: string
          description: Specific plan identifier. Identifies which subscription plan the workspace is on (e.g., "pro", "team", "enterprise"). Only present for paid workspaces.
          enum:
            - regular
        billingInterval:
          type: string
          description: 'Billing frequency. Values: "monthly" (billed every month), "yearly" (billed annually with potential discount). Only present for paid workspaces.'
          enum:
            - month
            - year
        features:
          type: array
          description: List of workspace features currently enabled. Each feature code represents a capability or add-on that is active (e.g., customFields, advancedReports, apiAccess). Empty array for free workspaces.
          items:
            type: string
            enum:
              - HELPDESK
              - API
              - AGENT
        featuresInfo:
          description: Detailed information about each enabled feature including pricing, labels, and descriptions. Useful for displaying feature lists with pricing in subscription management interfaces. Only present for paid workspaces.
          type: array
          items:
            $ref: '#/components/schemas/FeatureInfoDto'
        stripeCustomerId:
          type: object
          description: Stripe customer identifier. Used to reference this workspace in Stripe API calls. Null for free workspaces or if Stripe integration is not set up.
          nullable: true
        stripeSubscriptionId:
          type: object
          description: Stripe subscription identifier. Used to reference the active subscription in Stripe API calls. Null for free workspaces or if no active subscription exists.
          nullable: true
        trialEnd:
          type: object
          description: End date of the trial period. ISO 8601 format. Null if workspace is not in trial or trial has ended. After this date, subscription will convert to paid or cancel.
          nullable: true
        subscriptionDetails:
          description: Detailed subscription information from Stripe including status, billing period, and subscription items. Only available for enterprise workspaces with active Stripe subscriptions. Null for free or regular workspaces.
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/SubscriptionDetailsDto'
        nextInvoice:
          description: Preview of the upcoming invoice that will be generated at the next billing cycle. Includes line items, taxes, and totals. Only available for paid workspaces with active subscriptions. Null for free workspaces or if preview cannot be generated.
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/NextInvoiceDto'
        latestInvoices:
          description: List of up to 5 most recent invoices. Includes invoice numbers, amounts, payment status, and direct links to view invoices. Only available for paid workspaces. Empty array for free workspaces or trial workspaces without payment history.
          nullable: true
          type: array
          items:
            $ref: '#/components/schemas/LatestInvoiceDto'
        billableUsersCount:
          type: number
          description: Current number of billable users in the workspace. This is the count of users who are counted toward seat-based billing. Only present for paid workspaces.
        committedSeats:
          type: number
          description: Number of seats currently committed and being billed for. This is the base seat count in the subscription. Only present for paid workspaces.
        pendingSeats:
          type: object
          description: Number of additional seats scheduled to be added in the next billing period. Null if no seat changes are pending. Only present for paid workspaces.
          nullable: true
        pendingEffectiveAt:
          type: object
          description: Date when pending seat changes become effective. ISO 8601 format. Null if no seat changes are pending. Only present for paid workspaces.
          nullable: true
        cancelAtPeriodEnd:
          type: object
          description: Whether the subscription is scheduled to cancel at the end of the current billing period. True means subscription will cancel automatically, false means it will renew. Null if cancellation status is unknown.
          nullable: true
        cancellationEffectiveAt:
          type: object
          description: Date when the subscription cancellation becomes effective. ISO 8601 format. This is typically the end of the current billing period. Null if subscription is not scheduled to cancel.
          nullable: true
        cancellationRequestedAt:
          type: object
          description: Date when the cancellation was requested by the user. ISO 8601 format. Null if cancellation has not been requested.
          nullable: true
        cancellationReason:
          type: object
          description: Reason provided by the user when requesting cancellation. May include feedback about why they are canceling. Null if no reason was provided or cancellation was not user-initiated.
          nullable: true
        nextBillingInterval:
          type: string
          description: 'Billing interval that will take effect in the next billing cycle. Values: "monthly" or "yearly". Null if no interval change is scheduled. Only present when user has requested to switch between monthly and yearly billing.'
          enum:
            - month
            - year
          nullable: true
        stillAvailableFeatures:
          type: array
          description: List of features that will remain available after a billing interval change. Some features may be restricted when switching from yearly to monthly billing. Empty array if no interval change is scheduled or all features remain available.
          items:
            type: string
            enum:
              - HELPDESK
              - API
              - AGENT
        billingIntervalChangeScheduled:
          type: object
          description: Date when a scheduled billing interval change will take effect. ISO 8601 format. Null if no interval change is scheduled.
          nullable: true
        hasPaymentMethod:
          type: boolean
          description: Whether the workspace has a valid payment method on file with Stripe. True means payment can be processed automatically, false means payment method needs to be added. Only present for paid workspaces.
      required:
        - billingMode
    CreateInvoiceDto:
      type: object
      properties:
        potentialInvoiceId:
          type: string
          description: 'Potential invoice identifier in the format: `{type}::{projectId}::{dateFrom}::{dateTo}`. The type can be one of: Mixed, Subscription, TimeBased, ExpressQuotation, SingleProject, InterimPayment. Dates must be in YYYY-MM-DD format. Example: `SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31`. To get available potential invoices, use the GET /billing/potential-invoices endpoint.'
          example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
      required:
        - potentialInvoiceId
    InvoiceResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) of the created invoice
          example: 123e4567-e89b-12d3-a456-426614174000
        number:
          type: string
          description: 'Invoice number in the format: `{ORG}{PRJ}-{YY}{MM}-{NNN}`. This is the official sequential invoice number used for accounting and customer communication.'
          example: ACME01-2510-001
      required:
        - id
        - number
    InvoiceContactResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Contact ID (UUID) of the organization member. Use this ID when setting the contactId for a potential invoice via the PATCH endpoint.
          format: uuid
        name:
          type: string
          description: Full name of the contact (organization member). This is the person who will receive the invoice when it is sent via email.
          example: John Doe
        avatarId:
          type: object
          description: Avatar file ID (UUID) if the contact has a profile picture. Null if no avatar is set.
          nullable: true
          format: uuid
        email:
          type: object
          description: Email address of the contact. This is where the invoice will be sent when finalized. Null if the contact does not have an email address.
          nullable: true
          example: john.doe@example.com
      required:
        - id
        - name
        - avatarId
        - email
    InvoiceFixedSubscriptionDto:
      type: object
      properties:
        id:
          type: string
          description: Subscription billing ID
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: string
          description: Subscription title
          example: Monthly Support Package
        amount:
          type: number
          description: Subscription amount
          example: 500
      required:
        - id
        - title
        - amount
    InvoiceVariableSubscriptionDto:
      type: object
      properties:
        id:
          type: string
          description: Subscription billing ID
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: string
          description: Subscription title
          example: Per-User License
        amount:
          type: number
          description: Total amount
          example: 1000
        quantity:
          type: number
          description: Quantity (number of units)
          example: 10
        price:
          type: number
          description: Price per unit
          example: 100
      required:
        - id
        - title
        - amount
        - quantity
        - price
    InvoiceOneTimePaymentDto:
      type: object
      properties:
        id:
          type: string
          description: One-time payment ID
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: string
          description: Payment title
          example: Setup Fee
        amount:
          type: number
          description: Total amount
          example: 500
        quantity:
          type: number
          description: Quantity
          example: 1
        price:
          type: number
          description: Price per unit
          example: 500
      required:
        - id
        - title
        - amount
        - quantity
        - price
    InvoiceTaskDto:
      type: object
      properties:
        id:
          type: string
          description: Task ID
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: string
          description: Task title
          example: Implement user authentication
        spentTime:
          type: number
          description: Time spent in hours
          example: 8.5
        estimatedTime:
          type: number
          description: Estimated time in hours
          example: 8
        leadtimeFeeTime:
          type: number
          description: Leadtime fee time in hours
          example: 0.5
        billedTime:
          type: number
          description: Billed time in hours
          example: 8.5
        totalHours:
          type: number
          description: Total hours (billed + leadtime fee)
          example: 9
        amount:
          type: number
          description: Total amount
          example: 850
        taskTypeId:
          type: string
          description: Task type ID
          example: 123e4567-e89b-12d3-a456-426614174000
        taskShortNumber:
          type: number
          description: Task short number
          example: 123
      required:
        - id
        - title
        - spentTime
        - estimatedTime
        - leadtimeFeeTime
        - billedTime
        - totalHours
        - amount
        - taskTypeId
        - taskShortNumber
    InvoiceExpressQuotationItemOptionDto:
      type: object
      properties:
        title:
          type: string
          description: Option title
          example: Priority Support
        unitPrice:
          type: number
          description: Unit price
          example: 50
        totalPrice:
          type: number
          description: Total price
          example: 50
      required:
        - title
        - unitPrice
        - totalPrice
    InvoiceExpressQuotationItemDto:
      type: object
      properties:
        title:
          type: string
          description: Item title
          example: Additional Development Hours
        description:
          type: string
          description: Item description as HTML
          example: <p>Extra development work</p>
        unit:
          type: string
          description: Unit type
          example: Hours
        quantity:
          type: number
          description: Quantity
          example: 5
        unitPrice:
          type: number
          description: Unit price
          example: 100
        totalPrice:
          type: number
          description: Total price
          example: 500
        order:
          type: number
          description: Order
          example: 1
        options:
          description: Item options
          type: array
          items:
            $ref: '#/components/schemas/InvoiceExpressQuotationItemOptionDto'
      required:
        - title
        - description
        - unit
        - quantity
        - unitPrice
        - totalPrice
        - order
        - options
    InvoiceExpressQuotationDto:
      type: object
      properties:
        id:
          type: string
          description: Express quotation ID
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: string
          description: Quotation title
          example: Quick Add-on Development
        quotationId:
          type: string
          description: Quotation ID
          example: 123e4567-e89b-12d3-a456-426614174000
        amount:
          type: number
          description: Total amount
          example: 500
        acceptedAt:
          type: string
          description: Accepted date
          example: '2025-01-15T10:00:00Z'
        acceptedByName:
          type: string
          description: Accepted by user name
          example: John Doe
        taskShortNumber:
          type: number
          description: Task short number
          example: 123
        quotationShortNumber:
          type: number
          description: Quotation short number
          example: 456
        fullNumber:
          type: string
          description: Full quotation number
          example: EQ-456
        taskId:
          type: string
          description: Task ID
          example: 123e4567-e89b-12d3-a456-426614174000
        items:
          description: Quotation items
          type: array
          items:
            $ref: '#/components/schemas/InvoiceExpressQuotationItemDto'
      required:
        - id
        - title
        - quotationId
        - amount
        - acceptedAt
        - acceptedByName
        - taskShortNumber
        - quotationShortNumber
        - fullNumber
        - taskId
        - items
    InvoiceManualPositionDto:
      type: object
      properties:
        id:
          type: string
          description: Manual position ID
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: string
          description: Position title
          example: Third-party API License
        amount:
          type: number
          description: Total amount
          example: 200
        type:
          type: string
          description: Manual position type
          enum:
            - manual
            - project
            - interimPayment
            - interimPaymentDiscount
        projectManualPositionId:
          type: object
          description: Project manual position ID
          example: 123e4567-e89b-12d3-a456-426614174000
        discountAmount:
          type: number
          description: Discount amount
          example: 0
        discountType:
          type: string
          description: Discount type
          enum:
            - Fixed
            - Percentage
        discountValue:
          type: number
          description: Discount value
          example: 0
        categoryName:
          type: object
          description: Category name
          example: Licenses
        description:
          type: object
          description: Description as HTML
          example: <p>Annual license fee</p>
        interimPaymentId:
          type: object
          description: Interim payment ID
          example: 123e4567-e89b-12d3-a456-426614174000
      required:
        - id
        - title
        - amount
    InvoiceProductOptionValueDto:
      type: object
      properties:
        id:
          type: string
          description: Option value ID
          example: 123e4567-e89b-12d3-a456-426614174000
        originalValueId:
          type: object
          description: Original option value ID from catalog
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Option value name
          example: Large
        extraPriceFixed:
          type: object
          description: Extra price for this option value
          example: 50
        sort:
          type: number
          description: Sort order
          example: 1
      required:
        - id
        - name
        - sort
    InvoiceProductOptionDto:
      type: object
      properties:
        id:
          type: string
          description: Option ID
          example: 123e4567-e89b-12d3-a456-426614174000
        originalOptionId:
          type: object
          description: Original option ID from catalog
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Option name
          example: Size
        sort:
          type: number
          description: Sort order
          example: 1
        values:
          description: Available option values
          type: array
          items:
            $ref: '#/components/schemas/InvoiceProductOptionValueDto'
      required:
        - id
        - name
        - sort
        - values
    InvoiceProductDto:
      type: object
      properties:
        id:
          type: string
          description: Invoice product ID
          example: 123e4567-e89b-12d3-a456-426614174000
        originalProductId:
          type: object
          description: Original product ID from catalog
          example: 123e4567-e89b-12d3-a456-426614174000
        categoryId:
          type: string
          description: Product category ID
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Product name
          example: Website Development Package
        description:
          type: object
          description: Product description as HTML
          example: <p>Complete website development package</p>
        logoId:
          type: object
          description: Product logo file ID
          example: 123e4567-e89b-12d3-a456-426614174000
        sort:
          type: number
          description: Sort order
          example: 1
        basePriceFixed:
          type: number
          description: Base price before discounts
          example: 1000
        priceFixed:
          type: number
          description: Price after discounts
          example: 900
        fixedDiscountAmount:
          type: number
          description: Discount amount
          example: 100
        fixedDiscountType:
          type: string
          description: Discount type
          enum:
            - Fixed
            - Percentage
          example: Percentage
        fixedDiscountValue:
          type: number
          description: Discount value
          example: 10
        quantity:
          type: number
          description: Quantity
          example: 1
        totalPrice:
          type: number
          description: Total price (priceFixed * quantity)
          example: 900
        options:
          description: Product options
          type: array
          items:
            $ref: '#/components/schemas/InvoiceProductOptionDto'
      required:
        - id
        - categoryId
        - name
        - sort
        - basePriceFixed
        - priceFixed
        - fixedDiscountAmount
        - fixedDiscountType
        - fixedDiscountValue
        - quantity
        - totalPrice
        - options
    InvoiceComponentItemDto:
      type: object
      properties:
        id:
          type: string
          description: Component item ID
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Item name
          example: User Authentication
        description:
          type: string
          description: Item description as HTML
          example: <p>Implement user authentication system</p>
        type:
          type: string
          description: Item type
          enum:
            - Epic
            - WorkPackage
            - TodoList
            - TestSuite
          example: WorkPackage
        icon:
          type: object
          description: Item icon
          example: 🔐
        sort:
          type: number
          description: Sort order
          example: 1
        timeFrame:
          type: object
          description: Time frame in hours
          example: 40
        totalEstimate:
          type: object
          description: Total estimate in hours
          example: 40
        hourRate:
          type: number
          description: Hourly rate
          example: 100
        amount:
          type: number
          description: Total amount
          example: 4000
        parentId:
          type: object
          description: Parent item ID
          example: 123e4567-e89b-12d3-a456-426614174000
        children:
          description: Nested child items
          type: array
          items:
            $ref: '#/components/schemas/InvoiceComponentItemDto'
        originalItemId:
          type: object
          description: Original item ID from project component
          example: 123e4567-e89b-12d3-a456-426614174000
      required:
        - id
        - name
        - description
        - type
        - sort
        - hourRate
        - amount
    InvoiceComponentDto:
      type: object
      properties:
        id:
          type: string
          description: Invoice component ID
          example: 123e4567-e89b-12d3-a456-426614174000
        originalComponentId:
          type: object
          description: Original component ID from project
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Component name
          example: Backend Development
        description:
          type: string
          description: Component description as HTML
          example: <p>Backend API development</p>
        icon:
          type: object
          description: Component icon
          example: ⚙️
        sort:
          type: number
          description: Sort order
          example: 1
        totalTimeFrame:
          type: number
          description: Total time frame in hours
          example: 120
        hourRate:
          type: number
          description: Hourly rate
          example: 100
        amount:
          type: number
          description: Total amount after discount
          example: 12000
        amountWithoutDiscount:
          type: number
          description: Amount before discount
          example: 13000
        discountAmount:
          type: number
          description: Discount amount
          example: 1000
        discountType:
          type: string
          description: Discount type
          enum:
            - Fixed
            - Percentage
          example: Percentage
        items:
          description: Component items
          type: array
          items:
            $ref: '#/components/schemas/InvoiceComponentItemDto'
      required:
        - id
        - name
        - description
        - sort
        - totalTimeFrame
        - hourRate
        - amount
        - amountWithoutDiscount
        - discountAmount
        - discountType
        - items
    InvoiceSupportContingentChargeDto:
      type: object
      properties:
        id:
          type: string
          description: Invoice support contingent charge ID
          example: 123e4567-e89b-12d3-a456-426614174000
        supportContingentId:
          type: string
          description: Support contingent ID (UUID) that this charge is based on
          example: 123e4567-e89b-12d3-a456-426614174000
        title:
          type: string
          description: Title of the support contingent
          example: Monthly Support Retainer
        periodStart:
          type: string
          description: Billing period start date (ISO 8601 date string, YYYY-MM-DD format)
          example: '2025-08-01'
        periodEnd:
          type: string
          description: Billing period end date (ISO 8601 date string, YYYY-MM-DD format)
          example: '2025-08-31'
        frequency:
          type: number
          description: Billing frequency in months. Determines how often this contingent is charged (e.g., 1 = monthly, 3 = quarterly)
          example: 1
        hoursIncluded:
          type: number
          description: Number of hours included in this billing period
          example: 50
        price:
          type: number
          description: Total price for this billing period (in organization currency)
          example: 500
        hourRate:
          type: number
          description: Hourly rate used for calculating the price (in organization currency)
          example: 10
        transferable:
          type: boolean
          description: Whether unused hours from this period can be transferred to future periods
          example: true
      required:
        - id
        - supportContingentId
        - title
        - periodStart
        - periodEnd
        - frequency
        - hoursIncluded
        - price
        - hourRate
        - transferable
    InvoiceDetailsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Invoice ID (UUID)
          example: 123e4567-e89b-12d3-a456-426614174000
        number:
          type: string
          description: Invoice number
          example: ACME01-2510-001
        type:
          type: string
          description: Invoice type
          enum:
            - Mixed
            - Subscription
            - TimeBased
            - ExpressQuotation
            - SingleProject
            - InterimPayment
            - SupportContingentCharge
          example: Mixed
        includedTypes:
          type: array
          description: Included invoice types
          example:
            - Mixed
          items:
            type: number
            enum:
              - Mixed: Mixed
                Subscription: Subscription
                TimeBased: TimeBased
                ExpressQuotation: ExpressQuotation
                SingleProject: SingleProject
                InterimPayment: InterimPayment
                SupportContingentCharge: SupportContingentCharge
        projectId:
          type: string
          description: Project ID
          example: 123e4567-e89b-12d3-a456-426614174000
        createdAt:
          type: string
          description: Invoice creation date
          example: '2025-01-15T10:00:00Z'
        billedFrom:
          type: string
          description: Billing period start date
          example: '2025-01-01'
        billedTo:
          type: string
          description: Billing period end date
          example: '2025-01-31'
        status:
          type: string
          description: Invoice status
          enum:
            - New
            - Sent
            - Paid
            - Cancelled
          example: New
        statusChangedAt:
          type: object
          description: Status change date
          example: '2025-01-15T10:00:00Z'
        isOverdue:
          type: boolean
          description: Whether invoice is overdue
          example: false
        overDueDays:
          type: number
          description: Number of days overdue
          example: 0
        dueDate:
          type: object
          description: Payment due date
          example: '2025-02-15'
        paidAt:
          type: object
          description: Payment received date
          example: '2025-01-20T10:00:00Z'
        paidRuntime:
          type: object
          description: Payment runtime in days
          example: 5
        sentAt:
          type: object
          description: Invoice sent date
          example: '2025-01-15T10:00:00Z'
        sentBy:
          type: object
          description: User ID who sent the invoice
          example: 123e4567-e89b-12d3-a456-426614174000
        sentType:
          type: string
          description: How invoice was sent
          enum:
            - Manual
            - Email
          example: Email
        sentToContactId:
          type: object
          description: Contact ID who received the invoice
          example: 123e4567-e89b-12d3-a456-426614174000
        firstReminderAt:
          type: object
          description: First reminder sent date
          example: '2025-02-20T10:00:00Z'
        firstReminderContactId:
          type: object
          description: First reminder contact ID
          example: 123e4567-e89b-12d3-a456-426614174000
        firstReminderType:
          type: string
          description: How first reminder was sent
          enum:
            - Manual
            - Email
          example: Email
        firstReminderSentBy:
          type: object
          description: User ID who sent first reminder
          example: 123e4567-e89b-12d3-a456-426614174000
        secondReminderAt:
          type: object
          description: Second reminder sent date
          example: '2025-03-01T10:00:00Z'
        secondReminderContactId:
          type: object
          description: Second reminder contact ID
          example: 123e4567-e89b-12d3-a456-426614174000
        secondReminderType:
          type: string
          description: How second reminder was sent
          enum:
            - Manual
            - Email
          example: Email
        secondReminderSentBy:
          type: object
          description: User ID who sent second reminder
          example: 123e4567-e89b-12d3-a456-426614174000
        contactId:
          type: object
          description: Invoice contact ID
          example: 123e4567-e89b-12d3-a456-426614174000
        cancelationSentAt:
          type: object
          description: Cancellation sent date
          example: '2025-01-20T10:00:00Z'
        cancelationSentContactId:
          type: object
          description: Cancellation contact ID
          example: 123e4567-e89b-12d3-a456-426614174000
        cancelationSentType:
          type: string
          description: How cancellation was sent
          enum:
            - Manual
            - Email
          example: Email
        cancelationSentBy:
          type: object
          description: User ID who sent cancellation
          example: 123e4567-e89b-12d3-a456-426614174000
        hourRate:
          type: number
          description: Hourly rate used for time-based billing
          example: 100
        fixedSubscriptions:
          description: Fixed subscriptions
          type: array
          items:
            $ref: '#/components/schemas/InvoiceFixedSubscriptionDto'
        variableSubscriptions:
          description: Variable subscriptions
          type: array
          items:
            $ref: '#/components/schemas/InvoiceVariableSubscriptionDto'
        oneTimePayments:
          description: One-time payments
          type: array
          items:
            $ref: '#/components/schemas/InvoiceOneTimePaymentDto'
        tasksCount:
          type: number
          description: Number of tasks billed
          example: 10
        tasksHoursBilled:
          type: number
          description: Total hours billed from tasks
          example: 80.5
        tasksSum:
          type: number
          description: Total amount from tasks
          example: 8050
        tasks:
          description: Billed tasks
          type: array
          items:
            $ref: '#/components/schemas/InvoiceTaskDto'
        expressQuotations:
          description: Express quotations
          type: array
          items:
            $ref: '#/components/schemas/InvoiceExpressQuotationDto'
        manualRows:
          description: Manual billing positions
          type: array
          items:
            $ref: '#/components/schemas/InvoiceManualPositionDto'
        invoiceProducts:
          description: Invoice products
          type: array
          items:
            $ref: '#/components/schemas/InvoiceProductDto'
        invoiceProductsCount:
          type: number
          description: Number of products billed
          example: 5
        invoiceProductsSum:
          type: number
          description: Total amount from products
          example: 5000
        invoiceComponents:
          description: Invoice components
          type: array
          items:
            $ref: '#/components/schemas/InvoiceComponentDto'
        componentsCount:
          type: number
          description: Number of components billed
          example: 3
        componentsSum:
          type: number
          description: Total amount from components
          example: 12000
        supportContingentCharges:
          description: Support contingent charges billed on this invoice
          type: array
          items:
            $ref: '#/components/schemas/InvoiceSupportContingentChargeDto'
        supportContingentChargesCount:
          type: number
          description: Number of support contingent charges billed
          example: 1
        supportContingentChargesSum:
          type: number
          description: Total amount from support contingent charges
          example: 500
        leadtimeFeeIncluded:
          type: boolean
          description: Whether Leadtime fee is included
          example: true
        leadtimeFeePercent:
          type: number
          description: Leadtime fee percentage
          example: 10
        leadtimeFeeMethod:
          type: string
          description: Leadtime fee calculation method
          enum:
            - SeparateRow
            - TasksDistribution
          example: TasksDistribution
        leadtimeRowCustomTitle:
          type: object
          description: Custom title for Leadtime fee row
          example: Service Fee
        leadtimeFee:
          type: number
          description: Leadtime fee amount
          example: 500
        netto:
          type: number
          description: Net amount (before taxes and fees)
          example: 10000
        brutto:
          type: number
          description: Gross amount (after taxes)
          example: 11900
        reverseVat:
          type: boolean
          description: Whether reverse VAT charge applies
          example: false
        vatAmount:
          type: number
          description: VAT amount
          example: 1900
        vat:
          type: number
          description: VAT percentage
          example: 19
        fullTotal:
          type: number
          description: Full total including all fees
          example: 12400
        potentialOverdueReminderFee:
          type: number
          description: Potential overdue reminder fee
          example: 0
        potentialOverdueInterest:
          type: number
          description: Potential overdue interest
          example: 0
        overdueReminderFee:
          type: number
          description: Overdue reminder fee
          example: 0
        overdueInterestPercent:
          type: number
          description: Overdue interest percentage
          example: 0
        overdueInterestDays:
          type: number
          description: Overdue interest days
          example: 0
        overdueInterest:
          type: number
          description: Overdue interest amount
          example: 0
      required:
        - id
        - number
        - type
        - includedTypes
        - projectId
        - createdAt
        - billedFrom
        - billedTo
        - status
        - isOverdue
        - overDueDays
        - sentType
        - firstReminderType
        - secondReminderType
        - cancelationSentType
        - hourRate
        - fixedSubscriptions
        - variableSubscriptions
        - oneTimePayments
        - tasksCount
        - tasksHoursBilled
        - tasksSum
        - tasks
        - expressQuotations
        - manualRows
        - invoiceProducts
        - invoiceProductsCount
        - invoiceProductsSum
        - invoiceComponents
        - componentsCount
        - componentsSum
        - supportContingentCharges
        - supportContingentChargesCount
        - supportContingentChargesSum
        - leadtimeFeeIncluded
        - leadtimeFeePercent
        - leadtimeFeeMethod
        - leadtimeFee
        - netto
        - brutto
        - reverseVat
        - vatAmount
        - vat
        - fullTotal
        - potentialOverdueReminderFee
        - potentialOverdueInterest
        - overdueReminderFee
        - overdueInterestPercent
        - overdueInterestDays
        - overdueInterest
    SetInvoiceContactIdDto:
      type: object
      properties:
        id:
          type: string
          description: Invoice ID (UUID) to update the contact for
          example: 123e4567-e89b-12d3-a456-426614174000
          format: uuid
        contactId:
          type: object
          description: Contact ID (UUID) of the organization member who should receive the invoice. This must be a valid contact from the project's customer organization. Set to null to clear the contact. Use GET /billing/invoices/invoiceContacts/{projectId} to get available contacts.
          example: 123e4567-e89b-12d3-a456-426614174000
          format: uuid
          nullable: true
      required:
        - id
    SendInvoiceDto:
      type: object
      properties:
        id:
          type: string
          description: Invoice ID (UUID) to send. The invoice must have a contact with a valid email address set. The invoice will be sent via email as a PDF attachment.
          example: 123e4567-e89b-12d3-a456-426614174000
          format: uuid
      required:
        - id
    InvoiceSettingsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Invoice ID
          example: uuid-123
        invoiceLanguage:
          type: object
          description: Invoice language
          example: en
          nullable: true
        enableInvoiceReminderFee:
          type: object
          description: Enable invoice reminder fee
          example: true
          nullable: true
        invoiceReminderFee:
          type: object
          description: Invoice reminder fee
          example: 5.5
          nullable: true
        enableInvoiceInterest:
          type: object
          description: Enable invoice interest
          example: true
          nullable: true
        baseInvoiceInterest:
          type: object
          description: Base invoice interest
          example: 2.5
          nullable: true
        invoiceInterest:
          type: object
          description: Invoice interest
          example: 8
          nullable: true
        parentSettings:
          type: object
          description: Parent settings (from organization or workspace)
          additionalProperties: true
          example:
            invoiceLanguage: en
            enableInvoiceReminderFee: true
            invoiceReminderFee: 5.5
            enableInvoiceInterest: true
            invoiceInterest: 8
            baseInvoiceInterest: 2.5
        customTexts:
          type: object
          description: Custom invoice text templates (returned as HTML)
          properties:
            withBestRegards:
              type: string
              nullable: true
            invoiceGreeting:
              type: string
              nullable: true
            invoiceFooter:
              type: string
              nullable: true
            invoiceReminderFeeWarning:
              type: string
              nullable: true
            firstReminderGreeting:
              type: string
              nullable: true
            firstReminderFooter:
              type: string
              nullable: true
            secondReminderGreeting:
              type: string
              nullable: true
            secondReminderFooter:
              type: string
              nullable: true
            cancelationBody:
              type: string
              nullable: true
      required:
        - id
        - invoiceLanguage
        - enableInvoiceReminderFee
        - invoiceReminderFee
        - enableInvoiceInterest
        - baseInvoiceInterest
        - invoiceInterest
        - parentSettings
        - customTexts
    DefaultInvoiceTextsResponseDto:
      type: object
      properties:
        withBestRegards:
          type: object
          description: Default invoice text templates (returned as HTML)
          properties:
            withBestRegards:
              type: string
            invoiceGreeting:
              type: string
            invoiceFooter:
              type: string
            invoiceReminderFeeWarning:
              type: string
            firstReminderGreeting:
              type: string
            firstReminderFooter:
              type: string
            secondReminderGreeting:
              type: string
            secondReminderFooter:
              type: string
            cancelationBody:
              type: string
      required:
        - withBestRegards
    InvoiceCustomTextsDto:
      type: object
      properties:
        withBestRegards:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>Best regards,<br>Your Team</p>
          nullable: true
        invoiceGreeting:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>Dear Customer</p>
          nullable: true
        invoiceFooter:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>Thank you for your business</p>
          nullable: true
        invoiceReminderFeeWarning:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>A reminder fee will be applied if payment is not received</p>
          nullable: true
        firstReminderGreeting:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>First reminder for invoice payment</p>
          nullable: true
        firstReminderFooter:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>Please pay within 14 days</p>
          nullable: true
        secondReminderGreeting:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>Second reminder for invoice payment</p>
          nullable: true
        secondReminderFooter:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>Final notice before legal action</p>
          nullable: true
        cancelationBody:
          type: object
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your invoice text templates. Variables are represented as HTML spans:

            `invoiceNumber`, `projectName`, `projectShortName`, `clientCompanyName`, `contactPerson`, `invoiceNetto`, `invoiceTotal`, `fullTotal`, `accountNumber`, `dueDate`, `invoiceDate`, `reminderPenalty`, `interestPercent`, `senderNameAndPosition`, `senderName`, `senderPosition`, `overdueInterestPercent`, `overdueInterestDays`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="invoiceNumber">INV-2024-001</span>`, `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`
          example: <p>This invoice has been cancelled</p>
          nullable: true
    PatchInvoiceSettingsDto:
      type: object
      properties:
        invoiceDueDays:
          type: object
          description: Number of days until invoice is due (1-120)
          example: 30
          nullable: true
        enableInvoiceReminderFee:
          type: object
          description: Enable invoice reminder fee (null = use default)
          example: true
          nullable: true
        invoiceReminderFee:
          type: object
          description: Invoice reminder fee amount (0-1000)
          example: 5.5
          nullable: true
        enableInvoiceInterest:
          type: object
          description: Enable invoice interest (null = use default)
          example: true
          nullable: true
        baseInvoiceInterest:
          type: object
          description: Base invoice interest rate percentage (0-1000)
          example: 2.5
          nullable: true
        invoiceInterest:
          type: object
          description: Invoice interest rate percentage (0-1000)
          example: 8
          nullable: true
        invoiceLanguage:
          type: object
          description: Invoice language code
          example: en
          nullable: true
        customTexts:
          description: Custom invoice text templates. Content can be provided as HTML or Markdown and will be converted to internal format.
          allOf:
            - $ref: '#/components/schemas/InvoiceCustomTextsDto'
    InvoiceFileUrlResponseDto:
      type: object
      properties:
        url:
          type: string
          description: Workspace-authenticated URL for the generated invoice file. Send the Public API Bearer token when downloading it.
          example: https://app.example.com/api/public/workspace/files/file_123
        filename:
          type: string
          description: Filename of the generated invoice file
          example: invoice-ACME01-2510-001.pdf
      required:
        - url
        - filename
    PotentialInvoiceResponseDto:
      type: object
      properties:
        id:
          type: string
          description: 'Unique identifier for the potential invoice. Format: {type}::{projectId}::{dateFrom}::{dateTo}. This ID is used to reference the invoice in other endpoints.'
          example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
          format: string
        projectId:
          type: string
          description: Project ID (UUID) associated with this potential invoice. This is the project where the billable work was performed.
          example: 550e8400-e29b-41d4-a716-446655440000
          format: uuid
        orgId:
          type: string
          description: Organization ID (UUID) of the customer organization for this potential invoice. This is the organization that will be billed.
          example: 660e8400-e29b-41d4-a716-446655440001
          format: uuid
        dateKey:
          type: string
          description: 'Date key used for grouping potential invoices by month (format: YYYY-MM). Used for organizing invoices in the list view.'
          example: 2024-01
        dateFrom:
          type: string
          description: Start date of the billing period (ISO 8601 date string, YYYY-MM-DD format). This is when the billable period begins.
          example: '2024-01-01'
          format: date
        dateTo:
          type: string
          description: End date of the billing period (ISO 8601 date string, YYYY-MM-DD format). This is when the billable period ends.
          example: '2024-01-31'
          format: date
        total:
          type: number
          description: Total net amount for this potential invoice (before tax). This is the sum of all billable items in the invoice.
          example: 1250.5
        type:
          type: string
          description: Type of invoice indicating what kind of billable items are included. See InvoiceType enum for all possible values.
          enum:
            - Mixed
            - Subscription
            - TimeBased
            - ExpressQuotation
            - SingleProject
            - InterimPayment
            - SupportContingentCharge
          example: Mixed
      required:
        - id
        - projectId
        - orgId
        - dateKey
        - dateFrom
        - dateTo
        - total
        - type
    PotentialInvoiceDetailsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: 'Unique identifier for the potential invoice. Format: {type}::{projectId}::{dateFrom}::{dateTo}'
          example: SingleProject::0e9d033a-ab41-4212-8637-66c4e3b01fe2::2025-10-01::2025-10-31
        hasMeta:
          type: boolean
          description: Whether this potential invoice has custom metadata or settings applied. If true, the invoice has been customized (custom dates, contact, skipped items, etc.). If false, it shows the default calculated values.
          example: true
        periodFrom:
          type: string
          description: Actual service period start date (ISO 8601 date string, YYYY-MM-DD format). This is when the billable work actually began.
          example: '2025-10-01'
        periodTo:
          type: string
          description: Actual service period end date (ISO 8601 date string, YYYY-MM-DD format). This is when the billable work actually ended.
          example: '2025-10-31'
        type:
          type: string
          description: Primary invoice type indicating the main category of billable items. See InvoiceType enum for all possible values.
          enum:
            - Mixed
            - Subscription
            - TimeBased
            - ExpressQuotation
            - SingleProject
            - InterimPayment
            - SupportContingentCharge
        includedTypes:
          type: array
          description: Array of all invoice types included in this potential invoice. For Mixed type invoices, this will contain multiple types (e.g., TimeBased, Subscription, Products).
          items:
            type: string
            enum:
              - Mixed
              - Subscription
              - TimeBased
              - ExpressQuotation
              - SingleProject
              - InterimPayment
              - SupportContingentCharge
        projectId:
          type: string
          description: Project ID (UUID) where the billable work was performed
          format: uuid
        organizationId:
          type: string
          description: Organization ID (UUID) of the customer organization that will be billed
          format: uuid
        orgHourlyRate:
          type: number
          description: Hourly rate (in organization currency) used for calculating time-based billing. This rate is applied to logged hours on tasks.
        tax:
          type: number
          description: Tax amount calculated for this invoice. The tax rate is configured in workspace settings.
        applyReverseCharge:
          type: boolean
          description: Whether reverse charge VAT is applied. Reverse charge is used for B2B transactions within the EU where the customer is responsible for VAT.
          example: false
        contactId:
          type: object
          description: Contact ID (UUID) of the organization member who will receive the invoice when it is sent via email. Null if no contact has been assigned yet.
          nullable: true
          format: uuid
        billedFrom:
          type: string
          description: Custom billed from date (ISO 8601 date string, YYYY-MM-DD format). This is the start date that will appear on the final invoice document. Can differ from periodFrom if you want to show a different date range. Null if using default periodFrom.
          nullable: true
        billedTo:
          type: string
          description: Custom billed to date (ISO 8601 date string, YYYY-MM-DD format). This is the end date that will appear on the final invoice document. Can differ from periodTo if you want to show a different date range. Null if using default periodTo.
          nullable: true
        leadtimeFeePercent:
          type: number
          description: Leadtime service fee percentage. This is the percentage of the invoice total that will be charged as the Leadtime platform fee.
        addLeadtimeFeeToInvoice:
          type: boolean
          description: Whether to add the Leadtime service fee as a separate line item on the invoice. If false, the fee is not shown on the invoice but may still be calculated separately.
          example: false
        addLeadtimeFeeMethod:
          type: string
          description: Method for how the Leadtime fee should be added to the invoice. See AddLeadtimeFeeMethod enum for options (e.g., SeparateRow, DistributedToTasks).
          enum:
            - SeparateRow
            - TasksDistribution
        addLeadtimeFeeTasks:
          description: Array of task IDs (UUIDs) for distributing the Leadtime fee across specific tasks. Used when addLeadtimeFeeMethod is set to distribute the fee to tasks.
          type: array
          items:
            type: string
        addLeadtimeRowCustomTitle:
          type: object
          description: Custom title for the Leadtime fee row on the invoice. If null, a default title is used. This allows you to customize how the fee appears on the invoice document.
          nullable: true
        subscriptions:
          description: Array of subscription billing rows. These are recurring fees (monthly or periodic) that are due for billing. Each row includes billing type, payment type, price, quantity, and optional manual title.
          items:
            type: array
            items:
              type: potentialSubscriptionBillingRowDto
          type: array
        expressQuotations:
          description: Array of accepted express quotations. Express quotations are quick quotes created directly from tickets that have been accepted by the customer. Each quotation includes items, quantities, unit prices, and acceptance details.
          items:
            type: array
            items:
              type: potentialInvoiceExpressQuotationDto
          type: array
        tasks:
          description: Array of time-based tasks (tickets) with logged hours. These are completed tasks that have time logged and are ready for billing. Each task includes logged time, estimated time, billed time, and billing information.
          items:
            type: array
            items:
              type: potentialInvoiceTaskDto
          type: array
        products:
          description: Array of products from the product catalog. These are standardized products or services with fixed prices, quantities, options, and discounts. Product descriptions are returned as HTML.
          items:
            type: array
            items:
              type: potentialInvoiceProductDto
          type: array
        components:
          description: Array of project components with time estimates and hourly rates. Components are work packages from the project structure. Each component includes nested items, time frames, hourly rates, and amounts. Descriptions are returned as HTML.
          items:
            type: array
            items:
              type: potentialInvoiceComponentDto
          type: array
        manualPositions:
          description: Array of manual positions (custom billing items). These are manually added items like third-party costs, flat fees, interim payments, or other custom charges. Can include project manual positions or custom positions added during invoice review.
          items:
            type: array
            items:
              type: potentialInvoiceManualPositionDto
          type: array
        supportContingentCharges:
          description: Array of support contingent charges. These are retainer plan charges for Support-type projects, based on allocated hours and pricing for billing periods. Each charge includes the contingent title, period (start/end dates), frequency, hours included, price, hourly rate, and transferable flag.
          items:
            type: array
            items:
              type: potentialSupportContingentChargeDto
          type: array
        skippedTasks:
          description: Array of task IDs (UUIDs) that have been excluded from billing. These are tasks that would normally be billable but have been skipped (marked as "do not bill").
          type: array
          items:
            type: string
        skippedExpressQuotationTasks:
          description: Array of express quotation task IDs (UUIDs) that have been excluded from billing. These are accepted express quotations that have been skipped and will not appear on the invoice.
          type: array
          items:
            type: string
      required:
        - id
        - hasMeta
        - periodFrom
        - periodTo
        - type
        - includedTypes
        - projectId
        - organizationId
        - orgHourlyRate
        - tax
        - applyReverseCharge
        - contactId
        - billedFrom
        - billedTo
        - leadtimeFeePercent
        - addLeadtimeFeeToInvoice
        - addLeadtimeFeeMethod
        - addLeadtimeFeeTasks
        - addLeadtimeRowCustomTitle
        - subscriptions
        - expressQuotations
        - tasks
        - products
        - components
        - manualPositions
        - supportContingentCharges
        - skippedTasks
        - skippedExpressQuotationTasks
    PatchPotentialInvoiceDto:
      type: object
      properties:
        billedFrom:
          type: string
          description: Billed from date (ISO 8601 date string, YYYY-MM-DD format). If provided, updates the start date that will appear on the final invoice document. This can differ from the actual service period start date (periodFrom). Omit this field to leave unchanged. Accepts date-only format (e.g., "2025-10-01") or ISO timestamp.
          example: '2025-10-01'
          format: date
        billedTo:
          type: string
          description: Billed to date (ISO 8601 date string, YYYY-MM-DD format). If provided, updates the end date that will appear on the final invoice document. This can differ from the actual service period end date (periodTo). Omit this field to leave unchanged. Accepts date-only format (e.g., "2025-10-31") or ISO timestamp.
          example: '2025-10-31'
          format: date
        contactId:
          type: object
          description: Contact ID (organization member UUID) who will receive the invoice when it is sent via email. If provided, updates the invoice recipient. Set to null to clear the contact and remove the recipient assignment. Omit this field to leave unchanged. The contact must belong to the project customer organization (validated automatically).
          example: 550e8400-e29b-41d4-a716-446655440000
          nullable: true
    SetTaskBilledTimeDto:
      type: object
      properties:
        value:
          type: number
          description: Billed time in hours. This is the amount of time that will be charged to the customer, which may differ from the actual time spent. Must be a non-negative number.
          example: 12.5
          minimum: 0
      required:
        - value
    SetTaskDetailsDto:
      type: object
      properties:
        billedTime:
          type: number
          description: Billed time in hours. This is the amount of time that will be charged to the customer, which may differ from the actual time spent. Can be null to clear the billed time. Must be a non-negative number if provided.
          example: 12.5
          nullable: true
          minimum: 0
        billedTitle:
          type: object
          description: Custom billed title for the task. This title will appear on the invoice instead of the task title. Can be null to use the original task title. If provided, must be a non-empty string.
          example: Integration of BIM tools with custom workflow adjustments
          nullable: true
    SkipFromBillingDto:
      type: object
      properties:
        skipFromBilling:
          type: boolean
          description: Whether to skip this task from billing. Set to true to exclude the task from the invoice, or false to include it again.
          example: true
      required:
        - skipFromBilling
    AddManualPositionDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the manual position. This ID is used to reference the position in update and delete operations.
          example: 550e8400-e29b-41d4-a716-446655440000
          format: uuid
        title:
          type: string
          description: Title or description of the manual position (e.g., "Workshop travel expenses", "Translation services")
          example: Workshop travel expenses
        price:
          type: number
          description: Net price (before tax) for this manual position in the workspace currency
          example: 150
      required:
        - id
        - title
        - price
    UpdateManualPositionDto:
      type: object
      properties:
        title:
          type: string
          description: Title or description of the manual position
          example: Workshop travel expenses
        price:
          type: number
          description: Net price (before tax) for this manual position in the workspace currency
          example: 150
      required:
        - title
        - price
    SaveManualPositionsSortDto:
      type: object
      properties:
        items:
          description: 'Array of manual position IDs in the desired sort order. Only custom manual positions (type: manual) can be reordered.'
          example:
            - 550e8400-e29b-41d4-a716-446655440000
            - 660e8400-e29b-41d4-a716-446655440001
          items:
            type: array
            items:
              type: string
          type: array
      required:
        - items
    SaveSubscriptionSortDto:
      type: object
      properties:
        items:
          description: Array of subscription billing IDs (billingId) in the desired sort order. The subscriptions will appear on the invoice in this order. All subscription billing IDs from the potential invoice should be included.
          example:
            - 550e8400-e29b-41d4-a716-446655440000
            - 660e8400-e29b-41d4-a716-446655440001
          items:
            type: array
            items:
              type: string
          type: array
      required:
        - items
    ChangeQuantityDto:
      type: object
      properties:
        value:
          type: number
          description: Quantity value for the subscription billing row. For per-unit pricing subscriptions, this sets how many units to bill. Set to null or omit to clear a custom quantity and revert to default. Must be a non-negative integer if provided.
          example: 5
          nullable: true
          minimum: 0
    HelpdeskSettingsApiResponse:
      type: object
      properties:
        workspace:
          type: object
          description: Workspace-wide helpdesk settings including the global enabled flag and default task creation settings. These settings serve as the base configuration for all helpdesk operations in the workspace.
          example:
            isHelpdeskEnabled: true
            defaultSettings:
              taskProjectId: project-uuid-123
              taskTypeId: type-uuid-456
              taskStatusId: status-uuid-789
              taskAssignedId: user-uuid-101
              taskPriority: Normal
              sendDefaultReply: true
              defaultReplyBody: <p>Thank you for contacting us.</p>
        emailAccounts:
          description: List of all email accounts in the workspace with their helpdesk settings. Each account can have account-specific overrides that override workspace defaults. Accounts without specific settings will use workspace defaults.
          example:
            - emailAccountId: account-uuid-123
              emailAddress: support@example.com
              emailDisplayName: Support Team
              isEnabled: true
              taskProjectId: null
              taskTypeId: type-uuid-456
              taskStatusId: null
              taskAssignedId: null
              taskAccountableId: null
              taskTagsIds: null
              taskPriority: null
              sendDefaultReply: null
              defaultReplyBody: null
          type: array
          items:
            type: object
      required:
        - workspace
        - emailAccounts
    UpdateHelpdeskSettingsDto:
      type: object
      properties:
        defaultTaskPriority:
          type: string
          description: 'Default task priority for all helpdesk tasks. This priority will be assigned to tasks created from emails unless overridden at the email account or organization level. Valid values: Urgent, High, Normal, Low.'
          enum:
            - Low
            - Normal
            - High
          example: Normal
        defaultTaskTypeId:
          type: string
          description: Default task type ID that will be assigned to all helpdesk tasks. This determines the category/type of tasks created from emails. Must be a valid task type ID from your workspace.
          example: type-uuid-123
        defaultTaskStatusId:
          type: string
          description: Default task status ID for newly created helpdesk tasks. This determines the initial status of tasks created from emails. Must be a valid task status ID from your workspace.
          example: status-uuid-456
        defaultReplyBody:
          type: string
          description: |-
            HTML template for automatic replies sent to new email senders. This template is used when `sendDefaultReply` is enabled.

            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Helpdesk Variables

            You can use the following variables in your helpdesk templates. Variables are represented as HTML spans:

            **taskNumber**: The generated task number for the helpdesk ticket

            **Example HTML**: `<span data-type="variable" data-id="taskNumber">#12345</span>`

            **Note:** The template supports variables that are automatically replaced with actual values (e.g., `taskNumber` becomes the actual task number).
          example: <p>Thank you for contacting us. Your ticket number is <span data-type="variable" data-id="taskNumber">#12345</span>.</p>
        sendDefaultReply:
          type: boolean
          description: Whether to automatically send replies to new email senders. When enabled, the system will send the `defaultReplyBody` template as a reply to initial emails from senders who haven't contacted helpdesk before. Replies are only sent for first-time senders, not for replies to existing conversations.
          example: true
        autoAssignUserId:
          type: string
          description: Optional user ID to automatically assign all helpdesk tasks to. If provided, all tasks created from emails will be assigned to this user. If omitted or null, tasks will be created without automatic assignment. Must be a valid user ID from your workspace.
          example: user-uuid-789
      required:
        - defaultTaskPriority
        - defaultTaskTypeId
        - defaultTaskStatusId
        - defaultReplyBody
        - sendDefaultReply
    EmailAccountHelpdeskStatusResponse:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the email account. Use this ID to reference the account in other API calls.
          example: account-uuid-123
        email:
          type: string
          description: Email address of the account. This is the address that receives helpdesk emails.
          example: support@example.com
        displayName:
          type: string
          description: Display name for the email account. This is the friendly name shown in the UI and in email headers.
          example: Support Team
        helpdeskEnabled:
          type: boolean
          description: Whether helpdesk is currently enabled for this email account. When enabled, emails sent to this account will be automatically processed and converted to tasks. When disabled, emails are ignored.
          example: true
        settings:
          type: object
          description: Account-specific helpdesk settings that override workspace defaults. Only present when helpdesk is enabled for this account. Null fields inherit from workspace defaults. If helpdesk is disabled, this field will be undefined.
          example:
            emailAccountId: account-uuid-123
            emailAddress: support@example.com
            emailDisplayName: Support Team
            isEnabled: true
            taskProjectId: null
            taskTypeId: type-uuid-456
            taskStatusId: null
            taskAssignedId: null
            taskAccountableId: null
            taskTagsIds: null
            taskPriority: Normal
            sendDefaultReply: true
            defaultReplyBody: <p>Thank you for contacting us.</p>
      required:
        - id
        - email
        - displayName
        - helpdeskEnabled
    UpdateEmailAccountHelpdeskSettingsDto:
      type: object
      properties:
        defaultTaskPriority:
          type: string
          description: 'Task priority override for this email account. If provided, this priority will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default priority. Valid values: Urgent, High, Normal, Low.'
          enum:
            - Low
            - Normal
            - High
          example: High
        defaultTaskTypeId:
          type: string
          description: Task type ID override for this email account. If provided, this task type will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default task type. Must be a valid task type ID from your workspace.
          example: type-uuid-override
        defaultTaskStatusId:
          type: string
          description: Task status ID override for this email account. If provided, this task status will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default task status. Must be a valid task status ID from your workspace.
          example: status-uuid-override
        defaultReplyBody:
          type: string
          description: |-
            HTML template override for automatic replies sent from this email account. If provided, this template will be used instead of the workspace default. If omitted or null, the account will inherit the workspace default reply template.

            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Helpdesk Variables

            You can use the following variables in your helpdesk templates. Variables are represented as HTML spans:

            **taskNumber**: The generated task number for the helpdesk ticket

            **Example HTML**: `<span data-type="variable" data-id="taskNumber">#12345</span>`

            **Note:** Supports the same variables as workspace templates (e.g., `taskNumber`).
          example: <p>Thank you for contacting us. Your ticket number is <span data-type="variable" data-id="taskNumber">#12345</span>.</p>
        sendDefaultReply:
          type: object
          description: 'Auto-reply behavior override for this email account. This field supports 3-state inheritance: `null` (inherit from workspace default), `true` (enable auto-reply for this account), `false` (disable auto-reply for this account). This allows fine-grained control over auto-reply behavior per account.'
          example: true
        autoAssignUserId:
          type: string
          description: Auto-assign user override for this email account. If provided, tasks created from emails to this account will be assigned to this user. If omitted or null, the account will inherit the workspace default assignment (or no assignment if workspace default is also null). Set to an empty string to remove the override and use workspace default. Must be a valid user ID from your workspace.
          example: user-uuid-override
    OrganizationHelpdeskSettingsApiResponse:
      type: object
      properties:
        settings:
          type: object
          description: Organization-level helpdesk settings. These settings allow organizations to override workspace defaults for their email routing. Organization settings have higher priority than workspace defaults and can apply to all emails or only emails from organization members.
          example:
            organizationId: org-uuid-123
            organizationName: Acme Corp
            emailAccountSettings: []
        emailAccounts:
          description: List of email accounts with organization-specific helpdesk settings. Each account shows organization-level overrides and includes `effectiveDefaults` which shows the final merged settings (organization + workspace defaults). Organization settings override workspace defaults when emails are processed from organization members (or all senders if mode='all').
          example:
            - emailAccountId: account-uuid-123
              emailAddress: support@example.com
              emailDisplayName: Support Team
              organizationMode: onlyMembers
              addressContains: acme.com
              isEnabled: true
              taskProjectId: null
              taskTypeId: null
              taskStatusId: null
              taskAssignedId: null
              taskAccountableId: null
              taskTagsIds: null
              taskPriority: null
              sendDefaultReply: null
              defaultReplyBody: null
              effectiveDefaults:
                taskProjectId: project-uuid-123
                taskTypeId: type-uuid-456
                taskStatusId: status-uuid-789
                taskPriority: Normal
                sendDefaultReply: true
                defaultReplyBody: <p>Default reply</p>
                addressContains: acme.com
          type: array
          items:
            type: object
      required:
        - settings
        - emailAccounts
    UpdateOrganizationHelpdeskSettingsDto:
      type: object
      properties:
        mode:
          type: string
          description: 'Organization mode determines when these settings apply. `onlyMembers`: Settings apply only when the email sender is a member of this organization. `all`: Settings apply to ALL emails sent to the account, regardless of sender (highest priority). When set to `all`, these settings override everything including workspace defaults and other organization settings.'
          enum:
            - onlyMembers
            - all
          example: onlyMembers
        defaultTaskPriority:
          type: string
          description: 'Default task priority for helpdesk tasks created from emails that match this organization''s settings. This overrides workspace defaults when organization settings apply. Valid values: Urgent, High, Normal, Low.'
          enum:
            - Low
            - Normal
            - High
          example: Normal
        defaultTaskTypeId:
          type: string
          description: Default task type ID for helpdesk tasks created from emails that match this organization's settings. This overrides workspace defaults when organization settings apply. Must be a valid task type ID from your workspace.
          example: type-uuid-123
        defaultTaskStatusId:
          type: string
          description: Default task status ID for helpdesk tasks created from emails that match this organization's settings. This overrides workspace defaults when organization settings apply. Must be a valid task status ID from your workspace.
          example: status-uuid-456
        defaultReplyBody:
          type: string
          description: |-
            HTML template for automatic replies sent to email senders when organization settings apply. This overrides workspace defaults when organization settings are used.

            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Helpdesk Variables

            You can use the following variables in your helpdesk templates. Variables are represented as HTML spans:

            **taskNumber**: The generated task number for the helpdesk ticket

            **Example HTML**: `<span data-type="variable" data-id="taskNumber">#12345</span>`

            **Note:** Supports the same variables as workspace templates (e.g., `taskNumber`).
          example: <p>Thank you for contacting us. Your ticket number is <span data-type="variable" data-id="taskNumber">#12345</span>.</p>
        sendDefaultReply:
          type: boolean
          description: Whether to automatically send replies to new email senders when organization settings apply. When enabled, the system will send the `defaultReplyBody` template as a reply to initial emails from senders who match this organization's settings.
          example: true
        autoAssignUserId:
          type: string
          description: Optional user ID to automatically assign helpdesk tasks to when organization settings apply. If provided, tasks created from emails that match this organization's settings will be assigned to this user. If omitted or null, tasks will use workspace default assignment or no assignment. Must be a valid user ID from your workspace.
          example: user-uuid-789
      required:
        - mode
        - defaultTaskPriority
        - defaultTaskTypeId
        - defaultTaskStatusId
        - defaultReplyBody
        - sendDefaultReply
    AppearanceSettingsResponseDto:
      type: object
      properties:
        mainWorkspaceColor:
          type: string
          description: Main workspace color theme
          enum:
            - DEFAULT
            - BLACK
            - RED
            - ROSE
            - MAGENTA
            - GREEN
            - BLUE
            - YELLOW
            - VIOLET
          example: BLUE
        logoUrl:
          type: object
          description: URL to the workspace logo for default theme
          example: https://app.leadtime.io/api/files/public/123e4567-e89b-12d3-a456-426614174000
          nullable: true
        logoDarkThemeUrl:
          type: object
          description: URL to the workspace logo for dark theme
          example: https://app.leadtime.io/api/files/public/123e4567-e89b-12d3-a456-426614174001
          nullable: true
      required:
        - mainWorkspaceColor
    PatchAppearanceSettingsDto:
      type: object
      properties:
        mainWorkspaceColor:
          type: string
          description: Main workspace color theme
          enum:
            - DEFAULT
            - BLACK
            - RED
            - ROSE
            - MAGENTA
            - GREEN
            - BLUE
            - YELLOW
            - VIOLET
          example: BLUE
        logoId:
          type: object
          description: File ID for the workspace logo (default theme). Upload file via POST /workspace/upload first.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        logoDarkThemeId:
          type: object
          description: File ID for the workspace logo (dark theme). Upload file via POST /workspace/upload first.
          example: 123e4567-e89b-12d3-a456-426614174001
          nullable: true
    GetBasicSettingsDto:
      type: object
      properties:
        id:
          type: string
          description: Workspace ID
        brandColor:
          type: string
          description: 'Brand color in hex format (e.g., #FF5733). Used for workspace branding and internal project colors throughout the application.'
          example: '#FF5733'
        defaultLanguage:
          type: string
          description: 'Default language code for the workspace. Supported values: "en" (English) or "de" (German). This applies to new users; each user can override this in their profile.'
          example: en
        countryOfOperation:
          type: string
          description: 'ISO 3166-1 alpha-2 country code (2 uppercase letters) for the country where the company operates. Example: "US" for United States, "DE" for Germany.'
          example: US
        timezone:
          type: string
          description: 'IANA timezone identifier for the workspace. Defines the timezone used for all time-related information. Example: "America/New_York", "Europe/Berlin", "Asia/Tokyo".'
          example: America/New_York
        attendanceEnabled:
          type: boolean
          description: Whether attendance tracking (clock in/out) is enabled for employees. When enabled, employees must actively clock in and out. When disabled, working hours are calculated automatically based on contract times.
        timeColorBadPercent:
          type: number
          description: Lower threshold percentage (1-100) for marking time tracking as "bad" or incomplete. Times below this percentage are marked red on the team calendar. Must be less than timeColorGoodPercent.
          example: 30
        timeColorGoodPercent:
          type: number
          description: Upper threshold percentage (1-100) for marking time tracking as "good" or complete. Times above this percentage are marked green on the team calendar. Must be greater than timeColorBadPercent.
          example: 70
        defaultTimeFrom:
          format: date-time
          type: string
          description: Default work start time for employees. Used as a preset when employees clock in. ISO 8601 datetime format. Must be before defaultTimeTo if both are provided.
        defaultTimeTo:
          format: date-time
          type: string
          description: Default work end time for employees. Used as a preset when employees clock out. ISO 8601 datetime format. Must be after defaultTimeFrom if both are provided.
        timeTrackerAnnoyanceInMin:
          type: number
          description: 'Time tracker reminder interval in minutes (5-120). After this many minutes of inactivity, the system reminds users to resume time tracking. Example: 15 means reminders every 15 minutes.'
          example: 15
        sprintPeriodDays:
          type: number
          description: 'Sprint duration in days. Defines how long each sprint period lasts. Commonly used values: 7 (one week), 14 (two weeks), or 30 (one month).'
          example: 14
        sprintStartDay:
          type: number
          description: Sprint start day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period begins.
          example: 1
        sprintEndDay:
          type: number
          description: Sprint end day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period ends. If not provided, calculated from sprintPeriodDays.
          example: 5
        journalNotificationUserIds:
          description: Array of user IDs (UUIDs) who should receive notifications when journal entries are created or updated. Journal entries document events, notes, or observations for employees, projects, or organizations.
          type: array
          items:
            type: string
      required:
        - id
        - defaultLanguage
        - attendanceEnabled
        - sprintPeriodDays
        - sprintStartDay
    PatchBasicSettingsDto:
      type: object
      properties:
        brandColor:
          type: string
          description: 'Brand color in hex format (e.g., #FF5733). Used for workspace branding and internal project colors throughout the application.'
          example: '#FF5733'
        defaultLanguage:
          type: string
          description: 'Default language code for the workspace. Supported values: "en" (English) or "de" (German). This applies to new users; each user can override this in their profile.'
          example: en
        countryOfOperation:
          type: string
          description: 'ISO 3166-1 alpha-2 country code (2 uppercase letters) for the country where the company operates. Example: "US" for United States, "DE" for Germany.'
          example: US
        timezone:
          type: string
          description: 'IANA timezone identifier for the workspace. Defines the timezone used for all time-related information. Example: "America/New_York", "Europe/Berlin", "Asia/Tokyo".'
          example: America/New_York
        attendanceEnabled:
          type: boolean
          description: Whether attendance tracking (clock in/out) is enabled for employees. When enabled, employees must actively clock in and out. When disabled, working hours are calculated automatically based on contract times.
        timeColorBadPercent:
          type: number
          description: Lower threshold percentage (1-100) for marking time tracking as "bad" or incomplete. Times below this percentage are marked red on the team calendar. Must be less than timeColorGoodPercent.
          example: 30
        timeColorGoodPercent:
          type: number
          description: Upper threshold percentage (1-100) for marking time tracking as "good" or complete. Times above this percentage are marked green on the team calendar. Must be greater than timeColorBadPercent.
          example: 70
        defaultTimeFrom:
          format: date-time
          type: string
          description: Default work start time for employees. Used as a preset when employees clock in. ISO 8601 datetime format. Must be before defaultTimeTo if both are provided.
        defaultTimeTo:
          format: date-time
          type: string
          description: Default work end time for employees. Used as a preset when employees clock out. ISO 8601 datetime format. Must be after defaultTimeFrom if both are provided.
        timeTrackerAnnoyanceInMin:
          type: number
          description: 'Time tracker reminder interval in minutes (5-120). After this many minutes of inactivity, the system reminds users to resume time tracking. Example: 15 means reminders every 15 minutes.'
          example: 15
        sprintPeriodDays:
          type: number
          description: 'Sprint duration in days. Defines how long each sprint period lasts. Commonly used values: 7 (one week), 14 (two weeks), or 30 (one month).'
          example: 14
        sprintStartDay:
          type: number
          description: Sprint start day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period begins.
          example: 1
        sprintEndDay:
          type: number
          description: Sprint end day of the week (1-7, where 1=Monday, 2=Tuesday, ..., 7=Sunday). Defines when each sprint period ends. If not provided, calculated from sprintPeriodDays.
          example: 5
        journalNotificationUserIds:
          description: Array of user IDs (UUIDs) who should receive notifications when journal entries are created or updated. Journal entries document events, notes, or observations for employees, projects, or organizations.
          type: array
          items:
            type: string
    HolidayCountryDto:
      type: object
      properties:
        code:
          type: string
          description: Country or region code in ISO 3166-1 alpha-2 format. For states/provinces, uses format "COUNTRY-STATE" (e.g., "US-CA" for California).
          example: US
        name:
          type: string
          description: Localized country or region name in the requested language. Names are provided in the language specified by the `language` query parameter.
          example: United States
      required:
        - code
        - name
    HolidaySettingsResponseDto:
      type: object
      properties:
        defaultHolidayCode:
          type: string
          description: The default holiday country/region code for the workspace. This code is automatically applied to all new employees. Individual employees can override this in their profile settings.
          example: US
        availableCountries:
          description: Complete list of all available holiday countries and states that can be used for holiday configurations. Use these codes when setting default holiday settings or individual employee holiday rules.
          type: array
          items:
            $ref: '#/components/schemas/HolidayCountryDto'
      required:
        - defaultHolidayCode
        - availableCountries
    UpdateHolidaySettingsDto:
      type: object
      properties:
        defaultHolidayCode:
          type: string
          description: 'The default holiday country/region code to set for the workspace. Must be one of the available codes from `GET /holidays/countries`. Examples: "US" (United States), "DE" (Germany), "US-CA" (California), "AT" (Austria). This setting only affects new employees - existing employees keep their current holiday settings.'
          example: US
      required:
        - defaultHolidayCode
    HolidayDayDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the holiday day. Only present for holidays in custom or derived years.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: The name of the holiday (e.g., "Christmas Day", "New Year's Day", "Company Outing").
          example: Christmas Day
        date:
          type: string
          description: The computed date of the holiday in ISO 8601 format. For Fixed holidays, this is the specific date. For Rule-based holidays, this is the date computed from the rule for the given year.
          example: '2024-12-25T00:00:00.000Z'
        type:
          type: string
          description: 'The type of holiday: "Fixed" for specific dates, "Rule" for computed dates based on rules.'
          enum:
            - Fixed
            - Rule
          example: Fixed
        rule:
          type: string
          description: 'Rule string for rule-based holidays (only present for Rule type). Examples: "25 december", "easter monday", "first monday in september". The date is automatically computed from this rule for each year.'
          example: 25 december
        substitute:
          type: boolean
          description: Whether this is a substitute holiday (a replacement for a holiday that falls on a weekend). Substitute holidays are automatically created when official holidays fall on non-working days.
          example: false
      required:
        - id
        - name
        - date
        - type
        - substitute
    HolidayYearResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the holiday year. Only present for custom or derived years. Default years do not have an ID as they cannot be directly modified.
          example: 550e8400-e29b-41d4-a716-446655440000
        year:
          type: number
          description: The year this holiday configuration applies to.
          example: 2024
        country:
          type: string
          description: Country or region code for this holiday configuration (e.g., "US", "DE", "US-CA").
          example: US
        type:
          type: string
          description: |-
            The type of holiday configuration:
            - `"default"`: Official holidays from the holiday data source (cannot be modified)
            - `"custom"`: Workspace-specific configuration (can be modified)
            - `"derived"`: Configuration inherited from a previous custom year (can be modified)
          enum:
            - default
            - custom
            - derived
          example: default
        derivedFrom:
          type: number
          description: The year this configuration was derived from. Only present for derived type configurations. Indicates which custom year this configuration was copied from.
          example: 2023
        validTill:
          type: object
          description: The last year this configuration applies to. If `null`, the configuration applies indefinitely to all future years. Only present for custom or derived years. Controls how the configuration propagates to future years.
          example: 2025
        holidays:
          description: Complete list of all holidays for this year, including both fixed and rule-based holidays. Holidays are sorted by date and include computed dates for rule-based holidays.
          type: array
          items:
            $ref: '#/components/schemas/HolidayDayDto'
      required:
        - year
        - country
        - type
        - holidays
    CreateHolidayYearDto:
      type: object
      properties:
        year:
          type: number
          description: The year to create a custom holiday configuration for. Must be 1900 or later. If a custom year already exists for this year/country combination, it will be deleted and recreated with default holidays.
          example: 2024
        country:
          type: string
          description: Country or region code (e.g., "US", "DE", "US-CA"). Must be one of the available codes from `GET /holidays/countries`.
          example: US
        validTill:
          type: object
          description: The last year this configuration applies to. If `null`, the configuration applies indefinitely to all future years. If set to a specific year (e.g., 2026), the configuration applies from the specified year through that year. Future years within this range will automatically be created as "derived" configurations. You can update this later using `PUT /holidays/years/:yearId`.
          example: 2026
      required:
        - year
        - country
    UpdateHolidayYearDto:
      type: object
      properties:
        validTill:
          type: object
          description: The last year this configuration applies to. If `null`, the configuration applies indefinitely to all future years. If set to a specific year (e.g., 2026), the configuration applies from the year's start year through that year. Future years within this range will automatically be created as "derived" configurations. Years beyond `validTill` will use default holidays.
          example: 2026
      required:
        - validTill
    CreateHolidayDayDto:
      type: object
      properties:
        yearId:
          type: string
          description: The UUID of the custom holiday year this holiday belongs to. Must be a custom year (created via `POST /holidays/years`). You cannot add holidays to default years.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: The name of the holiday. Should be descriptive and clear (e.g., "Company Outing", "Festivus", "Team Building Day").
          example: Christmas Day
        type:
          type: string
          description: Holiday type. Only "Fixed" is allowed when creating new holidays. Rule-based holidays can only be edited, not created, as they come from default holiday definitions.
          enum:
            - Fixed
          example: Fixed
        date:
          type: string
          description: The specific date for the holiday in ISO 8601 format (e.g., "2024-12-25" or "2024-06-15"). The date should be in the format YYYY-MM-DD. The time portion is ignored.
          example: '2024-12-25'
      required:
        - yearId
        - name
        - type
        - date
    HolidayDayResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the holiday day.
          example: 550e8400-e29b-41d4-a716-446655440000
        yearId:
          type: string
          description: The UUID of the holiday year this holiday belongs to.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: The name of the holiday.
          example: Christmas Day
        type:
          type: string
          description: 'The type of holiday: "Fixed" for specific dates, "Rule" for computed dates based on rules.'
          enum:
            - Fixed
            - Rule
          example: Fixed
        date:
          type: string
          description: The date of the holiday in ISO 8601 format. Present for Fixed holidays. For Rule-based holidays, this is the computed date based on the rule for the year.
          example: '2024-12-25T00:00:00.000Z'
        rule:
          type: string
          description: Rule string for rule-based holidays. Only present when `type` is "Rule". The date is automatically computed from this rule for each year.
          example: 25 december
      required:
        - id
        - yearId
        - name
        - type
    UpdateHolidayDayDto:
      type: object
      properties:
        name:
          type: string
          description: The name of the holiday. Can be changed to rename the holiday (e.g., "Christmas" → "Holiday Break").
          example: Christmas Day
        type:
          type: string
          description: 'The type of holiday: "Fixed" for specific dates, "Rule" for computed dates based on rules. You can convert between types, but converting from Fixed to Rule requires providing a `rule` field.'
          enum:
            - Fixed
            - Rule
          example: Fixed
        date:
          type: string
          description: The specific date for fixed holidays in ISO 8601 format (e.g., "2024-12-25"). Required when `type` is "Fixed". The date should be in the format YYYY-MM-DD. The time portion is ignored.
          example: '2024-12-25'
        rule:
          type: string
          description: 'Rule string for rule-based holidays (only for Rule type). Examples: "25 december", "easter monday", "first monday in september". If not provided when switching to Rule type, the existing rule will be preserved (if available). If converting from Fixed to Rule without an existing rule, you must provide this field.'
          example: 25 december
      required:
        - name
        - type
    CustomIconResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) of the custom icon
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Name of the custom icon (e.g., `:company_logo:`, `:team_sales:`). This is the identifier used to reference the icon throughout the workspace.
          example: company_logo
        imageId:
          type: string
          description: File ID (UUID) of the icon image file
          example: 123e4567-e89b-12d3-a456-426614174000
        imageUrl:
          type: string
          description: Full public URL to access and display the icon image. Use this URL to display the icon in your application.
          example: https://app.workc.com/api/files/public/123e4567-e89b-12d3-a456-426614174000
        authorId:
          type: string
          description: ID (UUID) of the user who created this custom icon
          example: 123e4567-e89b-12d3-a456-426614174000
        createdAt:
          format: date-time
          type: string
          description: ISO 8601 date and time when the custom icon was created
          example: '2024-01-15T10:30:00Z'
        updatedAt:
          format: date-time
          type: string
          description: ISO 8601 date and time when the custom icon was last updated (image replacement)
          example: '2024-01-15T10:30:00Z'
      required:
        - id
        - name
        - imageId
        - imageUrl
        - authorId
        - createdAt
        - updatedAt
    CreateCustomIconDto:
      type: object
      properties:
        name:
          type: string
          description: Unique name for the custom icon. Must be alphanumeric characters and underscores only. Cannot conflict with default emoji names. Typically formatted with colons (e.g., `:company_logo:`, `:team_sales:`, `:lt_editor:`). This name is used to reference the icon throughout the workspace.
          example: company_logo
        imageId:
          type: string
          description: File ID (UUID) of the uploaded icon image. First upload the image file (PNG or SVG format) using POST /api/public/workspace/upload, then use the returned file ID here.
          example: 123e4567-e89b-12d3-a456-426614174000
      required:
        - name
        - imageId
    UpdateCustomIconDto:
      type: object
      properties:
        imageId:
          type: string
          description: 'File ID (UUID) of the new icon image. First upload the new image file (PNG or SVG format) using POST /api/public/workspace/upload, then use the returned file ID here. Note: The icon name cannot be changed after creation - only the image can be updated.'
          example: 123e4567-e89b-12d3-a456-426614174000
      required:
        - imageId
    TimebookingsGridResponseDto:
      type: object
      properties:
        items:
          type: array
          description: Array of time booking entries matching the filter criteria. Each item represents a single time log entry with details about when, who, what, and how much time was logged.
          items:
            type: object
            properties:
              id:
                type: string
                description: Unique identifier of the time booking entry
                example: 550e8400-e29b-41d4-a716-446655440000
              taskId:
                type: string
                nullable: true
                description: UUID of the task this time was logged to. Null if time was logged directly to a project.
                example: 550e8400-e29b-41d4-a716-446655440001
              projectId:
                type: string
                description: UUID of the project this time was logged to. Always present.
                example: 550e8400-e29b-41d4-a716-446655440002
              userId:
                type: string
                description: UUID of the user (employee) who logged this time
                example: 550e8400-e29b-41d4-a716-446655440003
              date:
                type: string
                format: date-time
                description: Date when the work was performed (ISO 8601 format)
                example: '2025-01-15T00:00:00.000Z'
              hours:
                type: number
                description: Number of hours logged (decimal format, e.g., 8.5 for 8 hours 30 minutes)
                example: 8.5
              comment:
                type: string
                nullable: true
                description: Optional comment describing the work performed
                example: Development work on feature X
              activityId:
                type: string
                description: UUID of the activity type (e.g., Development, Design, Management)
                example: 550e8400-e29b-41d4-a716-446655440004
        total:
          type: number
          description: 'Total number of time booking entries matching the filter criteria across all pages. Use this to calculate total pages: Math.ceil(total / pageSize)'
          example: 150
        totalHours:
          type: number
          description: Sum of all hours across all filtered time booking entries. This is useful for reporting and calculating total time spent. The value is the sum of the hours field from all matching entries.
          example: 1250.5
        page:
          type: number
          description: Current page number (1-based). The first page is page 1.
          example: 1
        pageSize:
          type: number
          description: Number of items per page. This is the page size you requested (or the default if not specified). The actual number of items in the items array may be less than this if you are on the last page.
          example: 100
        totalChildren:
          type: number
          description: Total number of child items when grouping is enabled. With strict raw-record pagination, this equals total. Only present when grouping is used.
          example: 250
        groupMeta:
          type: array
          description: 'Group metadata when grouping is enabled (e.g. group=day, group=week, group=month). Each entry describes a group on the current page: groupKey, pageFragmentCount, fullGroupCount, pageFragmentSum, fullGroupSum, and continuation flags. Order matches group appearance in items.'
          items:
            type: object
            properties:
              groupKey:
                type: string
                example: '2026-02-19'
              pageFragmentCount:
                type: number
                example: 12
              fullGroupCount:
                type: number
                example: 12
              pageFragmentSum:
                type: number
                example: 114.3
              fullGroupSum:
                type: number
                example: 114.3
              continuedFromPrevious:
                type: boolean
                example: false
              continuesToNext:
                type: boolean
                example: false
      required:
        - items
        - total
        - totalHours
        - page
        - pageSize
    CreateTimeBookingDto:
      type: object
      properties:
        type:
          type: string
          description: Type of time booking. Use "task" to log time to a specific task, or "project" to log time directly to a project. Task-based bookings are linked to both the task and its parent project. Project-based bookings are only linked to the project.
          example: task
          enum:
            - task
            - project
        id:
          type: string
          description: UUID of the task to log time to. Required when type is "task". The task must exist in your workspace and be linked to a project. Use GET /tasks/grid or GET /tasks/{identifier}.
          example: 550e8400-e29b-41d4-a716-446655440001
        projectId:
          type: string
          description: UUID of the project to log time to. Required when type is "project". The project must exist in your workspace. Use GET /projects.
          example: 550e8400-e29b-41d4-a716-446655440002
        activityId:
          type: string
          description: UUID of the activity type that describes the work performed. Activities are defined in your workspace settings (e.g., "Development", "Design", "Management", "Research"). Activities help categorize time for reporting and analysis. Use GET /administration/task-settings/activities.
          example: 550e8400-e29b-41d4-a716-446655440004
        hours:
          type: number
          description: Number of hours logged. Must be a positive number. Can be a decimal (e.g., 2.5 for 2 hours 30 minutes). If you also provide minutes, they will be added to this value. Required if minutes is not provided or is 0.
          example: 2.5
        minutes:
          type: number
          description: 'Additional minutes to add to the hours. Must be between 0 and 59. These minutes are converted and added to the hours value (e.g., hours: 2, minutes: 30 = 2.5 hours total). Optional - if not provided, defaults to 0.'
          example: 30
        comment:
          type: string
          description: Optional comment describing what was accomplished during this time. Useful for documentation and later reference. Can be searched in the time tracking grid.
          example: Worked on feature implementation
        date:
          type: string
          description: Date when the work was performed. Must be in ISO 8601 date format (YYYY-MM-DD). This is the date the time is attributed to, not necessarily when the booking was created. You can log time retroactively for past dates.
          example: '2025-01-15'
          format: date
      required:
        - type
        - activityId
        - hours
        - date
    PatchTimeBookingDto:
      type: object
      properties:
        type:
          type: string
          description: Type of time booking. Change to "task" to link to a specific task, or "project" for project-only booking. If changing type, make sure to provide the corresponding ID field (id for task, projectId for project). If omitted, the existing type is preserved.
          example: task
          enum:
            - task
            - project
        id:
          type: string
          description: UUID of the task to link this time booking to. Required if type is "task". Use this to move time from one task to another, or to change from project-based to task-based booking. If omitted and type is not changed, the existing task link is preserved. Use GET /tasks/grid or GET /tasks/{identifier}.
          example: 550e8400-e29b-41d4-a716-446655440001
        projectId:
          type: string
          description: UUID of the project to link this time booking to. Required if type is "project". Use this to move time from one project to another, or to change from task-based to project-based booking. If omitted and type is not changed, the existing project link is preserved. Use GET /projects.
          example: 550e8400-e29b-41d4-a716-446655440002
        activityId:
          type: string
          description: UUID of the activity type. Change this to recategorize the work type. If omitted, the existing activity is preserved. Use GET /administration/task-settings/activities.
          example: 550e8400-e29b-41d4-a716-446655440004
        hours:
          type: number
          description: Number of hours to update. Must be a positive number. Can be a decimal (e.g., 3.5 for 3 hours 30 minutes). If you also provide minutes, they will be added to this value. If omitted, the existing hours value is preserved.
          example: 3
        minutes:
          type: number
          description: Additional minutes to add to the hours. Must be between 0 and 59. These minutes are converted and added to the hours value. If omitted, defaults to 0 (only the hours value is used).
          example: 0
        comment:
          type: string
          description: Comment text. Update this to change or add notes about the work performed. If omitted, the existing comment is preserved. To clear a comment, provide an empty string.
          example: Updated comment
        date:
          type: string
          description: Date when the work was performed. Must be in ISO 8601 date format (YYYY-MM-DD). Use this to correct the date if the time was logged to the wrong day. If omitted, the existing date is preserved.
          example: '2025-01-15'
          format: date
    TimeTrackerIntervalDto:
      type: object
      properties:
        start:
          type: string
          description: Interval start time in ISO 8601 format (UTC). This is when the time tracking period began.
          example: '2024-01-15T10:00:00Z'
        end:
          type: string
          description: Interval end time in ISO 8601 format (UTC). For active (running) intervals, this is set to the current time. For completed intervals, this marks when tracking stopped. Intervals that span midnight are automatically ended at end of day.
          example: '2024-01-15T11:30:00Z'
      required:
        - start
        - end
    TimeTrackerResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the time tracker (UUID)
          example: 550e8400-e29b-41d4-a716-446655440000
        timeTrackingType:
          type: string
          description: 'Type of time tracking: Task (tracking time on a specific task) or Project (tracking time at project level). Determines whether taskId is required.'
          enum:
            - Task
            - Project
          example: Task
        projectId:
          type: object
          description: UUID of the project this tracker is associated with. Can be null if tracking general work.
          example: 550e8400-e29b-41d4-a716-446655440001
          nullable: true
        taskId:
          type: object
          description: UUID of the task this tracker is associated with. Only used when timeTrackingType is Task. Can be null for project-level tracking.
          example: 550e8400-e29b-41d4-a716-446655440002
          nullable: true
        activityId:
          type: object
          description: UUID of the activity category (e.g., Development, Testing, Management). Used for categorizing work in reports. Can be null.
          example: 550e8400-e29b-41d4-a716-446655440003
          nullable: true
        comment:
          type: object
          description: Optional comment or description of the work being tracked. Helps provide context when reviewing time entries later.
          example: Working on feature implementation
          nullable: true
        correction:
          type: number
          description: Manual time adjustment in minutes. Positive values add time, negative values subtract time. Useful for correcting tracking errors or adding time that was not tracked. Default is 0.
          example: 0
        createdAt:
          type: string
          description: Timestamp when the tracker was created, in ISO 8601 format (UTC)
          example: '2024-01-15T10:00:00Z'
        intervals:
          description: Array of time intervals that make up the total tracked time. Each interval represents a continuous period of tracking (start to end). If an interval has no end, it is currently active. Intervals are automatically ended at end of day if they span midnight.
          type: array
          items:
            $ref: '#/components/schemas/TimeTrackerIntervalDto'
        isRunning:
          type: boolean
          description: Whether the tracker currently has an active (running) interval. True if any interval has no end time, meaning time is being tracked right now.
          example: true
        hours:
          type: number
          description: Total tracked time in hours, calculated from all intervals plus any correction. Minimum value is 0.0166667 (1 minute).
          example: 2.5
        minutesPadded:
          type: string
          description: Minutes portion of the tracked time, formatted as a zero-padded string (e.g., "05", "30", "59")
          example: '30'
        hoursPadded:
          type: string
          description: Hours portion of the tracked time, formatted as a zero-padded string (e.g., "02", "12", "00")
          example: '02'
        autoEnded:
          type: boolean
          description: Whether any interval was automatically ended at the end of the day. This happens when a running interval started on a previous day and was not manually stopped.
          example: false
        isOld:
          type: boolean
          description: Whether the tracker contains intervals from previous days. True if any interval start time is not from today.
          example: false
      required:
        - id
        - timeTrackingType
        - projectId
        - taskId
        - activityId
        - comment
        - correction
        - createdAt
        - intervals
        - isRunning
        - hours
        - minutesPadded
        - hoursPadded
        - autoEnded
        - isOld
    TrackerListResponseDto:
      type: object
      properties:
        trackers:
          description: Array of all time trackers belonging to the authenticated user, sorted by creation time. Each tracker includes calculated fields like isRunning, hours, and formatted time strings.
          type: array
          items:
            $ref: '#/components/schemas/TimeTrackerResponseDto'
        lastInteraction:
          type: string
          description: Timestamp of the last interaction with any tracker (create, update, pause, resume, etc.) in ISO 8601 format (UTC). Used to track user activity. Can be null if no trackers exist.
          example: '2024-01-15T13:00:00Z'
          nullable: true
      required:
        - trackers
        - lastInteraction
    TimeTrackerCreateDto:
      type: object
      properties:
        timeTrackingType:
          type: string
          description: 'Required. Type of time tracking: Task (for tracking time on a specific task) or Project (for tracking time at project level). Determines whether taskId should be provided.'
          enum:
            - Task
            - Project
          example: Task
        projectId:
          type: string
          description: Optional. UUID of the project to associate with this tracker. If provided, the tracked time will be linked to this project.
          example: 550e8400-e29b-41d4-a716-446655440001
        taskId:
          type: string
          description: Optional. UUID of the task to associate with this tracker. Only used when timeTrackingType is Task. If timeTrackingType is Task, you should typically provide a taskId.
          example: 550e8400-e29b-41d4-a716-446655440002
        activityId:
          type: string
          description: Optional. UUID of the activity category (e.g., Development, Testing, Management). Used to categorize the type of work being tracked for reporting purposes.
          example: 550e8400-e29b-41d4-a716-446655440003
        comment:
          type: string
          description: Optional. Free-text comment describing the work being tracked. Helps provide context when reviewing time entries later.
          example: Working on feature implementation
        correction:
          type: number
          description: Optional. Manual time adjustment in minutes. Positive values add time (e.g., if you started tracking late), negative values subtract time. Default is 0.
          example: 0
      required:
        - timeTrackingType
    TimeTrackerUpdateDto:
      type: object
      properties:
        timeTrackingType:
          type: string
          description: Optional. Change the time tracking type between Task and Project. If changed, ensure taskId is appropriate for the new type.
          enum:
            - Task
            - Project
          example: Task
        projectId:
          type: object
          description: Optional. Update or remove the project association. Provide a UUID to change the project, or null to remove the association. Only provided fields are updated.
          example: 550e8400-e29b-41d4-a716-446655440001
          nullable: true
        taskId:
          type: object
          description: Optional. Update or remove the task association. Provide a UUID to change the task, or null to remove the association. Only used when timeTrackingType is Task.
          example: 550e8400-e29b-41d4-a716-446655440002
          nullable: true
        activityId:
          type: object
          description: Optional. Update or remove the activity category. Provide a UUID to change the activity, or null to remove it.
          example: 550e8400-e29b-41d4-a716-446655440003
          nullable: true
        comment:
          type: object
          description: Optional. Update or remove the comment. Provide a string to update the comment, or null to remove it.
          example: Updated comment
          nullable: true
        correction:
          type: number
          description: Optional. Adjust the tracked time by providing a correction in minutes. Positive values add time, negative values subtract time. This affects the total hours calculation.
          example: 5
    GetCompanySettingsDto:
      type: object
      properties:
        companyName:
          type: string
          description: Official business name of the company. Used in documents, invoices, contracts, and projects throughout the system.
          example: Acme Corporation
        companyLegalForm:
          type: string
          description: Legal form or structure of the company (e.g., GmbH, AG, LLC, Inc., sole proprietorship). Used in legal documents and official communications.
          example: GmbH
        companyStreet:
          type: string
          description: Street name of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: 123 Main St
        companyZip:
          type: string
          description: ZIP or postal code of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: '10115'
        companyCity:
          type: string
          description: City of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: Berlin
        companyCountry:
          type: string
          description: Country code (ISO 3166-1 alpha-2 format, e.g., "DE", "US") of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: DE
        companyHouseNumber:
          type: string
          description: House number of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: '42'
        companyPhone:
          type: string
          description: Official company phone number. Used for contact information in documents and communications.
          example: +49 30 12345678
        companyEmail:
          type: string
          description: Main business contact email address. Used for contact information in documents and communications.
          example: contact@acme.com
        companyWebsite:
          type: string
          description: Company website URL (optional). Used for contact information in documents and communications.
          example: https://acme.com
        companyFax:
          type: string
          description: Company fax number (optional). Used for contact information in documents and communications. For companies that still use fax.
          example: +49 30 87654321
        companyTaxNumber:
          type: string
          description: Tax identification number. Required for invoices and tax documents. Format varies by country (e.g., "DE123456789" for Germany).
          example: DE123456789
        companyRegistrationNumber:
          type: string
          description: Business registration number or commercial registry entry number. Format varies by country (e.g., "HRB 12345" for German companies).
          example: HRB 12345
        companyRegistrationCourt:
          type: string
          description: 'Responsible registration court where the company is registered. Example: "Amtsgericht Berlin" for German companies.'
          example: Amtsgericht Berlin
        companyShortDescription:
          type: string
          description: 'Short description of what the company does. Optional field to describe the company''s business activities. Example: "Manufacturer of Leadtime – an ERP system for software providers."'
        invoiceAccountNumber:
          type: string
          description: Bank account number in IBAN format used for invoices. This account number appears on invoices for payment purposes.
          example: DE89370400440532013000
      required:
        - companyName
    PatchCompanySettingsDto:
      type: object
      properties:
        companyName:
          type: string
          description: Official business name of the company. Used in documents, invoices, contracts, and projects throughout the system.
          example: Acme Corporation
        companyLegalForm:
          type: string
          description: Legal form or structure of the company (e.g., GmbH, AG, LLC, Inc., sole proprietorship). Used in legal documents and official communications.
          example: GmbH
        companyStreet:
          type: string
          description: Street name of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: 123 Main St
        companyZip:
          type: string
          description: ZIP or postal code of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: '10115'
        companyCity:
          type: string
          description: City of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: Berlin
        companyCountry:
          type: string
          description: Country code (ISO 3166-1 alpha-2 format, e.g., "DE", "US") of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: DE
        companyHouseNumber:
          type: string
          description: House number of the company address. Part of the complete address used on invoices, contracts, and other documents.
          example: '42'
        companyPhone:
          type: string
          description: Official company phone number. Used for contact information in documents and communications.
          example: +49 30 12345678
        companyEmail:
          type: string
          description: Main business contact email address. Used for contact information in documents and communications. Must be valid email format.
          example: contact@acme.com
        companyWebsite:
          type: string
          description: Company website URL (optional). Used for contact information in documents and communications. Must be valid URL format.
          example: https://acme.com
        companyFax:
          type: string
          description: Company fax number (optional). Used for contact information in documents and communications. For companies that still use fax.
          example: +49 30 87654321
        companyTaxNumber:
          type: string
          description: Tax identification number. Required for invoices and tax documents. Format varies by country (e.g., "DE123456789" for Germany).
          example: DE123456789
        companyRegistrationNumber:
          type: string
          description: Business registration number or commercial registry entry number. Format varies by country (e.g., "HRB 12345" for German companies).
          example: HRB 12345
        companyRegistrationCourt:
          type: string
          description: 'Responsible registration court where the company is registered. Example: "Amtsgericht Berlin" for German companies.'
          example: Amtsgericht Berlin
        companyShortDescription:
          type: string
          description: 'Short description of what the company does. Optional field to describe the company''s business activities. Example: "Manufacturer of Leadtime – an ERP system for software providers."'
        invoiceAccountNumber:
          type: string
          description: Bank account number in IBAN format used for invoices. This account number appears on invoices for payment purposes.
          example: DE89370400440532013000
    OrganizationMemberJournalResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for this journal entry
          example: 550e8400-e29b-41d4-a716-446655440000
        memberId:
          type: string
          description: UUID of the organization member (external contact) this journal entry is associated with. This identifies which external contact the entry is about.
          example: 550e8400-e29b-41d4-a716-446655440000
        createdAt:
          type: string
          description: 'ISO 8601 timestamp indicating when this journal entry was created. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., "2024-01-15T10:30:00Z").'
          example: '2024-01-15T10:30:00Z'
        createdBy:
          type: string
          description: UUID of the user who created this journal entry. This identifies who wrote the entry, which is useful for tracking authorship and accountability.
          example: 550e8400-e29b-41d4-a716-446655440000
        lastUpdated:
          type: string
          description: 'ISO 8601 timestamp indicating when this journal entry was last modified. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., "2024-01-16T14:20:00Z"). This is automatically updated whenever the entry is edited via the PATCH endpoint.'
          example: '2024-01-16T14:20:00Z'
        mood:
          type: string
          description: 'Emotional indicator for the journal entry. Values: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). This helps categorize and filter entries by emotional context.'
          enum:
            - Sad
            - Neutral
            - Happy
          example: Neutral
        reminder:
          type: object
          description: 'Optional reminder date in ISO 8601 format (YYYY-MM-DD). Set when creating or updating the entry to track follow-up dates for goals, agreements, or actions. Returns `null` if no reminder is set. Example: "2024-12-31" for December 31, 2024.'
          example: '2024-12-31'
          nullable: true
        body:
          type: string
          description: Journal entry content in HTML format. The content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications. Contains the rich text content including headings, paragraphs, lists, links, and formatting as originally provided.
          example: <h1>Member Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>
      required:
        - id
        - memberId
        - createdAt
        - createdBy
        - lastUpdated
        - mood
        - reminder
        - body
    CreateOrganizationMemberJournalDto:
      type: object
      properties:
        memberId:
          type: string
          description: UUID of the organization member (external contact) this journal entry is about. The member must exist in the workspace and be accessible to the current user. Organization members can only create entries for their own member record unless they have the `Organizations.writeJournal` permission.
          example: 550e8400-e29b-41d4-a716-446655440000
        body:
          type: string
          description: |-
            Rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             Use this field to document conversations, observations, agreements, or any notes about the organization member.
          example: <h1>Member Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>
        mood:
          type: string
          description: 'Emotional indicator for the journal entry. Use this to quickly categorize the tone or sentiment of the interaction. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). Defaults to `Neutral` if not provided. This helps filter and analyze journal entries by emotional context.'
          enum:
            - Sad
            - Neutral
            - Happy
          example: Neutral
        reminder:
          type: string
          description: 'Optional reminder date in ISO 8601 format (YYYY-MM-DD). Use this to set a follow-up date for reviewing goals, agreements, or taking action related to this journal entry. The reminder date can be used to filter entries and trigger notifications. Example: "2024-12-31" for December 31, 2024. If not provided, no reminder is set.'
          example: '2024-12-31'
      required:
        - memberId
        - body
    PatchOrganizationMemberJournalDto:
      type: object
      properties:
        memberId:
          type: string
          description: UUID of the organization member to associate this journal entry with. If provided, changes which member this entry is about. The new member must exist and be accessible. If the new member is the current user's own member record, `visibleToSubject` will be automatically set to `true`. If omitted, the existing member association is preserved.
          example: 550e8400-e29b-41d4-a716-446655440000
        body:
          type: string
          description: |-
            Updated rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             If omitted, the existing body content remains unchanged.
          example: <h1>Updated Member Status</h1><p>Follow-up meeting scheduled for next week.</p>
        mood:
          type: string
          description: 'Updated emotional indicator for the journal entry. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). If omitted, the existing mood value remains unchanged.'
          enum:
            - Sad
            - Neutral
            - Happy
          example: Happy
        reminder:
          type: object
          description: Updated reminder date in ISO 8601 format (YYYY-MM-DD). Provide a date string (e.g., "2024-12-31") to set or update the reminder. Provide `null` to clear an existing reminder. If omitted entirely, the existing reminder remains unchanged. Use reminders to track follow-up dates for goals, agreements, or actions related to this journal entry.
          example: '2024-12-31'
          nullable: true
    OrganizationMemberResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Organization member ID
          example: 123e4567-e89b-12d3-a456-426614174000
        organizationId:
          type: string
          description: Organization ID
          example: 123e4567-e89b-12d3-a456-426614174000
        userId:
          type: string
          description: Associated user ID
          example: 123e4567-e89b-12d3-a456-426614174000
        firstName:
          type: string
          description: First name
          example: John
        lastName:
          type: string
          description: Last name
          example: Doe
        email:
          type: string
          description: Email address
          example: john.doe@example.com
        roleId:
          type: object
          description: User role ID that determines the member's access permissions in Leadtime. Can be `null` if the member does not have login access.
          example: workspace-id_guest_
          nullable: true
        isActive:
          type: boolean
          description: Whether member is active
          example: true
        canLogin:
          type: boolean
          description: Whether member can login to the system
          example: true
        gender:
          type: string
          description: Gender
          enum:
            - Male
            - Female
          example: Male
          nullable: true
        title:
          type: object
          description: Title/salutation
          example: Dr.
          nullable: true
        degree:
          type: object
          description: Academic degree
          example: Ph.D.
          nullable: true
        position:
          type: object
          description: Position/role in organization
          example: Manager
          nullable: true
        addressStreet:
          type: object
          description: Street address
          example: Main Street
          nullable: true
        addressZip:
          type: object
          description: ZIP/postal code
          example: '12345'
          nullable: true
        addressCity:
          type: object
          description: City
          example: New York
          nullable: true
        addressCountry:
          type: object
          description: Country
          example: USA
          nullable: true
        addressHouseNumber:
          type: object
          description: House number
          example: 123A
          nullable: true
        phone:
          type: object
          description: Phone number
          example: '+1234567890'
          nullable: true
        birthDate:
          type: object
          description: Date of birth in ISO 8601 date format (YYYY-MM-DD). Returns `null` if not set.
          example: '1990-01-15'
          nullable: true
        socialNetworkLink:
          type: object
          description: Social network profile link
          example: https://linkedin.com/in/johndoe
          nullable: true
        influenceLevel:
          type: string
          description: Influence level classification
          enum:
            - Low
            - Medium
            - High
            - Dominant
          nullable: true
        attitudeLevel:
          type: string
          description: Attitude level classification
          enum:
            - Enthusiastic
            - WellMeaning
            - Neutral
            - Opposing
          nullable: true
        personalityType:
          type: string
          description: Personality type classification
          enum:
            - Dominant
            - Conscientious
            - Steady
            - Initiative
          nullable: true
        avatarId:
          type: object
          description: Avatar configuration ID. Use /api/avatars/public/{avatarId}.svg to render it.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        avatarUrl:
          type: object
          description: Public URL to the member's generated avatar. Returns `null` if no avatar is configured. The URL can be used directly in image tags or for display purposes.
          example: https://example.com/api/avatars/public/123e4567-e89b-12d3-a456-426614174000.svg
          nullable: true
        createdAt:
          type: string
          description: ISO 8601 timestamp indicating when the organization member was created.
          example: '2024-01-15T10:30:00.000Z'
        updatedAt:
          type: string
          description: ISO 8601 timestamp indicating when the organization member was last updated.
          example: '2024-01-20T15:45:00.000Z'
      required:
        - id
        - organizationId
        - userId
        - firstName
        - lastName
        - email
        - roleId
        - isActive
        - canLogin
        - gender
        - title
        - degree
        - position
        - addressStreet
        - addressZip
        - addressCity
        - addressCountry
        - addressHouseNumber
        - phone
        - birthDate
        - socialNetworkLink
        - influenceLevel
        - attitudeLevel
        - personalityType
        - avatarId
        - avatarUrl
        - createdAt
        - updatedAt
    CreateOrganizationMemberDto:
      type: object
      properties:
        organizationId:
          type: string
          description: UUID of the organization this member belongs to. The organization must exist in your workspace.
          example: 123e4567-e89b-12d3-a456-426614174000
        firstName:
          type: string
          description: First name
          example: John
        lastName:
          type: string
          description: Last name
          example: Doe
        email:
          type: string
          description: Email address of the member. This email will be used for the associated user account and for sending invitation emails if login access is enabled. Must be a valid email format.
          example: john.doe@example.com
        roleId:
          type: string
          description: User role ID that determines the member's access permissions in Leadtime. Common roles include guest roles (e.g., `workspace-id_guest_`) for external users. This role is applied to the automatically created user account.
          example: workspace-id_guest_
        isActive:
          type: boolean
          description: Whether the member is active in the system. Inactive members are hidden from normal views but remain in the database. Defaults to `true`.
          example: true
          default: true
        canLogin:
          type: boolean
          description: Whether the member can log in to Leadtime as a guest user. If `true`, a user account is created with Active status. In ready workspaces, an invitation email is sent immediately. During initial workspace setup, invitation emails are suppressed until setup is finished. If `false` or omitted, the member exists as a contact only with an Inactive user account. Defaults to `false`.
          example: true
        gender:
          type: string
          description: Gender
          enum:
            - Male
            - Female
          example: Male
        title:
          type: string
          description: Title/salutation (e.g., Dr., Prof.)
          example: Dr.
        degree:
          type: string
          description: Academic degree
          example: Ph.D.
        position:
          type: string
          description: Job title or position within the organization (e.g., "Managing Director", "Project Manager", "Head of Sales").
          example: Manager
        addressStreet:
          type: string
          description: Street address
          example: Main Street
        addressZip:
          type: string
          description: ZIP/postal code
          example: '12345'
        addressCity:
          type: string
          description: City
          example: New York
        addressCountry:
          type: string
          description: Country
          example: USA
        addressHouseNumber:
          type: string
          description: House number
          example: 123A
        phone:
          type: string
          description: Phone number
          example: '+1234567890'
        birthDate:
          format: date-time
          type: string
          description: 'Birth date (ISO 8601 date format: YYYY-MM-DD)'
          example: '1990-01-15'
        socialNetworkLink:
          type: string
          description: Social network profile link
          example: https://linkedin.com/in/johndoe
        influenceLevel:
          type: string
          description: Influence level classification
          enum:
            - Low
            - Medium
            - High
            - Dominant
        attitudeLevel:
          type: string
          description: Attitude level classification
          enum:
            - Enthusiastic
            - WellMeaning
            - Neutral
            - Opposing
        personalityType:
          type: string
          description: Personality type classification
          enum:
            - Dominant
            - Conscientious
            - Steady
            - Initiative
        avatarId:
          type: string
          description: Avatar configuration ID. During the transition, an image file ID from POST /workspace/upload is also accepted and converted to an uploaded avatar configuration. Omit to create the default generated avatar.
          example: 123e4567-e89b-12d3-a456-426614174000
        avatarConfig:
          type: object
          description: Generated avatar configuration. If provided, it takes precedence over avatarId.
          example:
            style: initials
            options:
              seed: Jane Doe
      required:
        - organizationId
        - firstName
        - lastName
        - email
        - roleId
        - isActive
    PatchOrganizationMemberDto:
      type: object
      properties:
        firstName:
          type: string
          description: First name. If provided, this change will automatically sync to the associated user account.
          example: John
        lastName:
          type: string
          description: Last name. If provided, this change will automatically sync to the associated user account.
          example: Doe
        email:
          type: string
          description: Email address. If provided, this change will automatically sync to the associated user account. Must be a valid email format.
          example: john.doe@example.com
        roleId:
          type: string
          description: User role ID that determines access permissions. If provided, this change will automatically sync to the associated user account, updating their permissions in Leadtime.
          example: workspace-id_staff_
        isActive:
          type: boolean
          description: Whether the member is active in the system. Inactive members are hidden from normal views but remain in the database.
          example: true
        canLogin:
          type: boolean
          description: 'Whether the member can log in to Leadtime. Changing this value will update the associated user account: setting to `true` activates the user. In ready workspaces, an invitation email is sent automatically. During initial workspace setup, invitation emails are suppressed until setup is finished. Setting to `false` deactivates the user and clears the invite token.'
          example: true
        gender:
          type: string
          description: 'Gender of the member. Used for personalized greetings in letters and communications. Available values: `Male`, `Female`.'
          enum:
            - Male
            - Female
          example: Male
        title:
          type: string
          description: Title or salutation prefix (e.g., "Dr.", "Prof.", "Mr.", "Mrs."). Used in formal communications and letters.
          example: Dr.
        degree:
          type: string
          description: Academic degree or qualification (e.g., "Ph.D.", "MBA", "M.Sc."). Used in formal communications.
          example: Ph.D.
        position:
          type: string
          description: Job title or position within the organization (e.g., "Managing Director", "Project Manager", "Head of Sales").
          example: Senior Manager
        addressStreet:
          type: string
          description: Street name for the member's address. Part of the complete address information.
          example: Main Street
        addressZip:
          type: string
          description: ZIP or postal code for the member's address. Part of the complete address information.
          example: '12345'
        addressCity:
          type: string
          description: City for the member's address. Part of the complete address information.
          example: New York
        addressCountry:
          type: string
          description: Country for the member's address. Part of the complete address information.
          example: USA
        addressHouseNumber:
          type: string
          description: House or building number for the member's address. Part of the complete address information.
          example: 123A
        phone:
          type: string
          description: Phone number for contacting the member. Can include country code prefix (e.g., +1 for US).
          example: '+1234567890'
        birthDate:
          format: date-time
          type: string
          description: Date of birth in ISO 8601 date format (YYYY-MM-DD). Used for birthday reminders and age calculations.
          example: '1990-01-15'
        socialNetworkLink:
          type: string
          description: URL to a social network profile (e.g., LinkedIn, Twitter, XING). Used for relationship management and contact information.
          example: https://linkedin.com/in/johndoe
        influenceLevel:
          type: string
          description: 'Classification of the member''s level of influence within their organization. Used for relationship management and strategic planning. Available values: `Low`, `Medium`, `High`, `Dominant`.'
          enum:
            - Low
            - Medium
            - High
            - Dominant
        attitudeLevel:
          type: string
          description: 'Classification of the member''s attitude towards your company. Used for relationship management and communication strategy. Available values: `Enthusiastic`, `WellMeaning`, `Neutral`, `Opposing`.'
          enum:
            - Enthusiastic
            - WellMeaning
            - Neutral
            - Opposing
        personalityType:
          type: string
          description: 'Personality type classification based on behavioral patterns. Used for tailoring communication and interaction approaches. Available values: `Dominant`, `Conscientious`, `Steady`, `Initiative`.'
          enum:
            - Dominant
            - Conscientious
            - Steady
            - Initiative
        avatarId:
          type: string
          description: 'Avatar configuration ID. During the transition, an image file ID from POST /workspace/upload is also accepted and converted to an uploaded avatar configuration. To remove: set to `null` or empty string. If not provided, the avatar remains unchanged.'
          example: 123e4567-e89b-12d3-a456-426614174000
        avatarConfig:
          type: object
          description: Generated avatar configuration. If provided, it takes precedence over avatarId.
          example:
            style: initials
            options:
              seed: Jane Doe
    OrgLetterAdressType:
      type: string
      enum:
        - Organization
        - Member
      description: The address type used for this letter. "Organization" means the organization's main address was used, "Member" means a member's private address was used.
    LetterResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) of the organization letter
          example: 123e4567-e89b-12d3-a456-426614174000
        organizationId:
          type: string
          description: UUID of the organization this letter belongs to. Use this to link the letter to the correct organization.
          example: 123e4567-e89b-12d3-a456-426614174001
        memberId:
          type: object
          description: UUID of the organization member (contact person) this letter is addressed to. Will be null if the letter is addressed to the organization itself rather than a specific member.
          example: 123e4567-e89b-12d3-a456-426614174002
          nullable: true
        subject:
          type: string
          description: The subject or title of the letter as displayed in listings and on the document.
          example: Invoice Reminder
        dateOfLetter:
          type: string
          description: The date shown on the letter in ISO 8601 format (YYYY-MM-DDTHH:mm:ss.sssZ). This is the date that appears on the letter document itself, which may differ from the creation date.
          example: '2024-01-15T00:00:00.000Z'
          format: date-time
        addressType:
          description: The address type used for this letter. "Organization" means the organization's main address was used, "Member" means a member's private address was used.
          example: Organization
          allOf:
            - $ref: '#/components/schemas/OrgLetterAdressType'
        body:
          type: string
          description: The letter body content in HTML format. This has been converted from the internal IDoc storage format to HTML for easy display and processing. Includes all formatting, template variables (as HTML spans), and rich text elements.
          example: <h1>Dear Customer</h1><p>This is a reminder...</p>
        createdAt:
          type: string
          description: ISO 8601 timestamp (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when the letter was first created in the system.
          example: '2024-01-10T10:30:00.000Z'
          format: date-time
        lastUpdated:
          type: string
          description: ISO 8601 timestamp (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when the letter was last modified. This is automatically updated whenever any field of the letter is changed.
          example: '2024-01-12T14:20:00.000Z'
          format: date-time
        createdBy:
          type: string
          description: UUID of the user who created this letter. Use this to track authorship and for audit purposes.
          example: 123e4567-e89b-12d3-a456-426614174003
      required:
        - id
        - organizationId
        - memberId
        - subject
        - dateOfLetter
        - addressType
        - body
        - createdAt
        - lastUpdated
        - createdBy
    CreateLetterDto:
      type: object
      properties:
        organizationId:
          type: string
          description: The UUID of the organization this letter belongs to. The organization must exist in your workspace and be accessible with your permissions.
          example: 123e4567-e89b-12d3-a456-426614174000
        memberId:
          type: object
          description: Optional UUID of the organization member (contact person) this letter is addressed to. If provided, the member must belong to the specified organization. Leave empty or set to null for letters addressed to the organization itself.
          example: 123e4567-e89b-12d3-a456-426614174001
          nullable: true
        subject:
          type: string
          description: The subject or title of the letter. This appears in letter listings and as the document title when exporting to PDF.
          example: Invoice Reminder
          minLength: 1
        dateOfLetter:
          type: string
          description: 'The date shown on the letter (ISO 8601 date format: YYYY-MM-DD). This can be backdated if needed. The date is displayed on the letter document and used for sorting and filtering.'
          example: '2024-01-15'
          format: date
        addressType:
          description: Determines which address is used on the letter. Use "Organization" for the organization's main address, or "Member" for the member's private address (requires memberId to be set).
          example: Organization
          allOf:
            - $ref: '#/components/schemas/OrgLetterAdressType'
        body:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your organization letter body. Variables are represented as HTML spans:

            `clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`, `<span data-type="variable" data-id="contactPerson">John Doe</span>`
          example: '<h1>Dear Customer</h1><p>This is a reminder for invoice #12345...</p><p><span data-type="variable" data-id="signature"></span></p>'
      required:
        - organizationId
        - subject
        - dateOfLetter
        - addressType
        - body
    PatchLetterDto:
      type: object
      properties:
        organizationId:
          type: string
          description: 'Optional: The UUID of the organization this letter belongs to. If provided, changes which organization the letter is linked to. The organization must exist and be accessible in your workspace.'
          example: 123e4567-e89b-12d3-a456-426614174000
        memberId:
          type: object
          description: 'Optional: The UUID of the organization member this letter is addressed to. If provided, links the letter to that member (member must belong to the specified or current organization). Set to null to clear the member association. Omit this field to leave it unchanged.'
          example: 123e4567-e89b-12d3-a456-426614174001
          nullable: true
        subject:
          type: string
          description: 'Optional: The subject or title of the letter. If provided, updates the letter subject. Omit to leave unchanged.'
          example: Updated Invoice Reminder
        dateOfLetter:
          type: string
          description: 'Optional: The date shown on the letter (ISO 8601 date format: YYYY-MM-DD). If provided, updates the letter date. Omit to leave unchanged.'
          example: '2024-01-15'
          format: date
        addressType:
          description: 'Optional: The address type for the letter. Use "Organization" for the organization''s main address, or "Member" for the member''s private address. If provided, updates the address type. Omit to leave unchanged.'
          example: Member
          allOf:
            - $ref: '#/components/schemas/OrgLetterAdressType'
        body:
          type: string
          description: |-
            Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **variable**
            Type: inline
            Document/template variable token (span with data-type="variable" and data-id).
            - id: Variable key, often scoped (e.g. currentUser.firstName, project.name). Only use keys that exist for the document type you are in; product docs list the allowed variable ids. Do not invent new variable names in HTML for production templates.
            Atts:
            - id: null
            ```html
            <span data-type="variable" data-id="null/some-value" id="null/some-value"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```


            ## Available Template Variables

            You can use the following variables in your organization letter body. Variables are represented as HTML spans:

            `clientCompanyName`, `contactPerson`, `senderNameAndPosition`, `senderName`, `senderPosition`, `todayDate`, `dearCustomer`, `signature`

            **Example HTML**: `<span data-type="variable" data-id="clientCompanyName">ACME Corp</span>`, `<span data-type="variable" data-id="contactPerson">John Doe</span>`
          example: <p>Updated content with <span data-type="variable" data-id="signature"></span></p>
    OrganizationCustomFieldTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German)
          example: en
        name:
          type: string
          description: Translated name of the custom field in this language. Used for multilingual display in organization forms.
          example: Priority Level
        description:
          type: string
          description: Translated description of the custom field in this language. Provides context about what information should be entered.
          example: Priority level for the organization
      required:
        - language
    OrganizationCustomFieldSelectOptionTranslationDto:
      type: object
      properties:
        language:
          type: string
          description: ISO 639-1 language code for the translation (e.g., "en" for English, "de" for German)
          example: en
        name:
          type: string
          description: Translated display name for the select option in this language. Shown to users when selecting from dropdown options.
          example: High Priority
      required:
        - language
        - name
    OrganizationCustomFieldSelectOptionDto:
      type: object
      properties:
        id:
          type: string
          description: Optional unique identifier for the select option. If not provided, the value will be used as the identifier. Required when updating existing options.
          example: option-1
        value:
          type: string
          description: Display value for the select option. This is the value stored when the option is selected. Must be unique within the field.
          example: High Priority
        translations:
          description: Array of translations for multilingual support. Each translation contains a language code and translated display name. The system automatically shows the correct language based on user preferences.
          example:
            - language: en
              name: High Priority
            - language: de
              name: Hohe Priorität
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldSelectOptionTranslationDto'
      required:
        - value
        - translations
    OrganizationCustomFieldResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for the custom field. Used to reference this field when setting values on organizations.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Display name of the custom field. Shown as the field label in organization forms.
          example: Priority Level
        description:
          type: string
          description: Description providing context about what information should be entered in this field.
          example: Priority level for the organization
        sort:
          type: number
          description: Numeric sort order for display purposes. Lower numbers appear first in organization forms. Determines the order in which fields are displayed.
          example: 1
        type:
          type: string
          description: 'Field type enum value indicating the input type. Valid values: Text, Textarea, Number, Date, Checkbox, Select, MultiSelect.'
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        entity:
          type: string
          description: Entity type identifier. Always "Organization" for organization custom fields. Used internally to scope fields to specific entity types.
          example: Organization
        translations:
          description: Complete array of translations for the field name. Each translation contains the language code and localized name.
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldTranslationDto'
        selectOptions:
          description: Array of select options for Select and MultiSelect field types. Each option contains an ID, value, and translations. Empty array for other field types.
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldSelectOptionDto'
      required:
        - id
        - name
        - description
        - sort
        - type
        - entity
        - translations
        - selectOptions
    CreateOrganizationCustomFieldDto:
      type: object
      properties:
        id:
          type: string
          description: Optional unique identifier for the custom field. Only needed when updating an existing field. If omitted, a new field will be created.
        name:
          type: string
          description: Display name for the custom field. Shown as the field label in organization forms (e.g., "Priority Level", "Region", "Industry").
          example: Priority Level
        description:
          type: string
          description: Optional description providing context about what information should be entered in this field. Helps users understand the purpose of the field.
          example: Priority level for the organization
        type:
          type: string
          description: 'Field type enum value that determines what kind of input is used. Valid values: Text (single-line), Textarea (multi-line), Number, Date, Checkbox, Select (single choice), MultiSelect (multiple choices).'
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        selectOptions:
          description: Array of select options. REQUIRED for Select and MultiSelect field types. Each option contains a value and translations. Not used for other field types.
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldSelectOptionDto'
        translations:
          description: Array of translations for the field name. Each translation contains a language code and translated name. The system automatically displays the correct language based on user preferences.
          example:
            - language: en
              name: Priority Level
            - language: de
              name: Prioritätsstufe
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldTranslationDto'
        translationsDescriptions:
          description: Optional array of translations for the field description. Each translation contains a language code and translated description. Used for multilingual help text.
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldTranslationDto'
      required:
        - name
        - type
        - translations
    UpdateOrganizationCustomFieldDto:
      type: object
      properties:
        name:
          type: string
          description: Name of the custom field
          example: Priority Level
        description:
          type: string
          description: Description of the custom field
          example: Priority level for the organization
        type:
          type: string
          description: Type of the custom field
          enum:
            - Text
            - Textarea
            - Number
            - Date
            - Checkbox
            - Select
            - MultiSelect
            - Currency
            - Url
          example: Select
        selectOptions:
          description: Select options (required for Select type fields)
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldSelectOptionDto'
        translations:
          description: Translations for the custom field name
          example:
            - language: en
              name: Priority Level
            - language: de
              name: Prioritätsstufe
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldTranslationDto'
        translationsDescriptions:
          description: Translations for the custom field description
          type: array
          items:
            $ref: '#/components/schemas/OrganizationCustomFieldTranslationDto'
      required:
        - name
        - type
        - translations
    SortOrganizationCustomFieldsDto:
      type: object
      properties:
        ids:
          description: Array of custom field IDs in the desired order
          example:
            - field-1
            - field-2
            - field-3
          type: array
          items:
            type: string
      required:
        - ids
    OrganizationResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Organization ID
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: Organization name
          example: Acme Corporation
        icon:
          type: object
          description: Icon identifier
          example: building
        shortName:
          type: object
          description: Short identifier
          example: ACME
        legalForm:
          type: object
          description: Legal form
          example: GmbH
        type:
          type: string
          description: Organization type
          enum:
            - Customer
            - Prospect
            - Target
            - Partner
            - Supplier
            - ServiceProvider
            - Investor
            - Competitor
            - GovernmentAgency
            - EducationalInstitution
          example: Customer
        color:
          type: string
          description: Organization color (hex code)
          example: '#FF5733'
        isActive:
          type: boolean
          description: Whether organization is active
          example: true
        startOfCollaboration:
          type: string
          description: Start of collaboration date
          format: date
          example: '2024-01-01'
        addressStreet:
          type: object
          description: Street address
          example: 123 Main St
        addressZip:
          type: object
          description: ZIP/Postal code
          example: '10001'
        addressCity:
          type: object
          description: City
          example: New York
        addressCountry:
          type: object
          description: Country
          example: USA
        addressHouseNumber:
          type: object
          description: House number
          example: 42A
        taxNumber:
          type: object
          description: Tax identification number
          example: DE123456789
        registrationNumber:
          type: object
          description: Registration number
          example: HRB 12345
        registrationCourt:
          type: object
          description: Registration court
          example: Munich District Court
        shortDescription:
          type: object
          description: Short description
          example: Leading software development company
        phoneNumber:
          type: object
          description: Phone number
          example: '+1234567890'
        faxNumber:
          type: object
          description: Fax number
          example: '+1234567891'
        email:
          type: object
          description: Email address
          example: contact@acme.com
        website:
          type: object
          description: Website URL
          example: https://acme.com
        logoUrl:
          type: object
          description: Logo URL (publicly accessible)
          example: https://app.example.com/api/files/public/123e4567-e89b-12d3-a456-426614174000
        hourRate:
          type: object
          description: Hourly rate
          example: 150
        invoiceDueDays:
          type: object
          description: Invoice due days
          example: 30
        enableInvoiceReminderFee:
          type: object
          description: Enable invoice reminder fee
          example: true
        invoiceReminderFee:
          type: object
          description: Invoice reminder fee amount
          example: 5
        enableInvoiceInterest:
          type: object
          description: Enable invoice interest
          example: false
        baseInvoiceInterest:
          type: object
          description: Base invoice interest rate
          example: 3
        invoiceInterest:
          type: object
          description: Invoice interest rate
          example: 5
        invoiceLanguage:
          type: object
          description: Invoice language code
          example: en
        projectDocumentEnableToc:
          type: object
          description: Enable table of contents in project documents
          example: true
        projectDocumentTitlePage:
          type: object
          description: Show title page in project documents
          example: true
        projectDocumentHeadingStyle:
          type: object
          description: Heading style for project documents
          example: classic
        projectDocumentDefaultContactUserId:
          type: object
          description: Default contact user ID for project documents
          example: 123e4567-e89b-12d3-a456-426614174000
        createdAt:
          type: string
          description: Creation timestamp
          example: '2024-01-01T00:00:00.000Z'
        updatedAt:
          type: string
          description: Last update timestamp
          example: '2024-01-15T12:30:00.000Z'
        parentOrganizationId:
          type: object
          description: Parent organization ID (for hierarchical organization structure)
          example: 123e4567-e89b-12d3-a456-426614174000
        parentOrganizationName:
          type: object
          description: Parent organization name
          example: B. Braun
        customFields:
          type: object
          description: Custom field values as key-value pairs. Keys match custom field IDs configured in workspace settings. Values match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields.
          additionalProperties: true
          example:
            customField1: value1
            customField2: 123
        tags:
          description: Array of tag UUIDs assigned to this organization. Tags enable cross-project organization and filtering.
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 223e4567-e89b-12d3-a456-426614174001
          type: array
          items:
            type: string
      required:
        - id
        - name
        - type
        - color
        - isActive
        - createdAt
        - updatedAt
    CreateOrganizationDto:
      type: object
      properties:
        name:
          type: string
          description: Full legal name of the organization (required)
          example: Acme Corporation
        icon:
          type: string
          description: Icon identifier for visual representation in lists and projects. Must be in the format `:icon_name:` (e.g., `:building:`, `:office:`, `:factory:`). Use standard emoji short names or custom icon names created in the workspace.
          example: ':building:'
        shortName:
          type: string
          description: Short abbreviation for the organization (3-5 characters, alphanumeric only). Used in projects and lists for quick identification. If not provided, will be auto-generated from the organization name.
          example: ACME
          maxLength: 5
        legalForm:
          type: string
          description: Legal company form or structure (e.g., "GmbH", "AG", "LLC", "Inc.", "Ltd.")
          example: GmbH
        type:
          type: string
          description: 'Type of organization defining the business relationship. Options: Customer (existing business partner), Prospect (potential customer), Supplier (external provider), Partner (strategic partner), ServiceProvider, Investor, Competitor, GovernmentAgency, EducationalInstitution, Target (target customer group).'
          enum:
            - Customer
            - Prospect
            - Target
            - Partner
            - Supplier
            - ServiceProvider
            - Investor
            - Competitor
            - GovernmentAgency
            - EducationalInstitution
          example: Customer
        color:
          type: string
          description: Hex color code used to visually distinguish this organization in lists, projects, and throughout the application (required)
          example: '#FF5733'
        startOfCollaboration:
          type: string
          description: 'Date when the business relationship with this organization started. Format: ISO 8601 date (YYYY-MM-DD).'
          format: date
          example: '2024-01-01'
        addressStreet:
          type: string
          description: Street address
          example: 123 Main St
        addressZip:
          type: string
          description: ZIP/Postal code
          example: '10001'
        addressCity:
          type: string
          description: City
          example: New York
        addressCountry:
          type: string
          description: Country
          example: USA
        addressHouseNumber:
          type: string
          description: House number
          example: 42A
        taxNumber:
          type: string
          description: Tax identification number
          example: DE123456789
        registrationNumber:
          type: string
          description: Registration number
          example: HRB 12345
        registrationCourt:
          type: string
          description: Registration court
          example: Munich District Court
        shortDescription:
          type: string
          description: Short description of the organization
          example: Leading software development company
        phoneNumber:
          type: string
          description: Phone number
          example: '+1234567890'
        faxNumber:
          type: string
          description: Fax number
          example: '+1234567891'
        email:
          type: string
          description: Email address
          example: contact@acme.com
        website:
          type: string
          description: Website URL
          example: https://acme.com
        logoId:
          type: string
          description: 'UUID of the uploaded logo file. To upload a logo: 1) Call POST /api/public/workspace/upload to upload the file, 2) Use the returned file ID here. The logo will appear in organization lists and project views.'
          example: 123e4567-e89b-12d3-a456-426614174000
        hourRate:
          type: number
          description: Customer-specific hourly rate used for all billable time entries for this organization. Overrides workspace default. Used when creating invoices.
          example: 150
        invoiceDueDays:
          type: number
          description: 'Number of days after invoice date until payment is due. Valid range: 1-120. Overrides workspace default for this organization.'
          example: 30
        enableInvoiceReminderFee:
          type: boolean
          description: Whether to charge a reminder fee for overdue invoices. Overrides workspace default. Set to true to enable, false to disable.
          example: true
        invoiceReminderFee:
          type: number
          description: 'Amount charged as a reminder fee for overdue invoices. Valid range: 0-1000. Only applies if enableInvoiceReminderFee is true.'
          example: 5
        enableInvoiceInterest:
          type: boolean
          description: Whether to charge interest on overdue invoices. Overrides workspace default. Set to true to enable, false to disable.
          example: false
        baseInvoiceInterest:
          type: number
          description: Base interest rate percentage (0-1000) used as the foundation for calculating default interest. Typically set by central banks (e.g., ECB, Bundesbank) and changes every six months. The final interest rate is baseInvoiceInterest + invoiceInterest.
          example: 3
        invoiceInterest:
          type: number
          description: 'Interest rate percentage (0-1000) added to baseInvoiceInterest to calculate the total default interest rate. According to BGB §288: typically +5% for private customers, +9% for businesses. Final rate = baseInvoiceInterest + invoiceInterest.'
          example: 5
        invoiceLanguage:
          type: string
          description: Language code used for generating invoices and reminders for this organization (e.g., "en", "de", "fr"). Overrides workspace default.
          example: en
        projectDocumentEnableToc:
          type: boolean
          description: Whether to automatically include a table of contents in project documents (quotes, requirement documents, contracts) for this organization. Overrides workspace default.
          example: true
        projectDocumentTitlePage:
          type: boolean
          description: Whether to include a title/cover page in project documents for this organization. Overrides workspace default.
          example: true
        projectDocumentHeadingStyle:
          type: string
          description: Formatting style for headings in project documents (e.g., "Sequential", "Normal"). Controls the hierarchy and appearance of document headings. Overrides workspace default.
          example: classic
        projectDocumentDefaultContactUserId:
          type: string
          description: UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents (quotes, order confirmations, etc.) created for this organization.
          example: 123e4567-e89b-12d3-a456-426614174000
        createProject:
          type: boolean
          description: Create a default project for this organization (internal use only)
          example: false
        parentOrganizationId:
          type: string
          description: UUID of the parent organization. Only top-level organizations (without a parent) can be selected as parents. Enables one-level hierarchical organization structure.
          example: 123e4567-e89b-12d3-a456-426614174000
        customFields:
          type: object
          description: Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields and their configurations.
          additionalProperties: true
          example:
            customField1: value1
            customField2: 123
        tags:
          description: Array of tag UUIDs to categorize this organization. Tags enable cross-project organization and filtering. Use an empty array to remove all tags.
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 223e4567-e89b-12d3-a456-426614174001
          type: array
          items:
            type: string
      required:
        - name
        - type
        - color
    UpdateOrganizationDto:
      type: object
      properties:
        name:
          type: string
          description: Full legal name of the organization (required)
          example: Acme Corporation
        icon:
          type: string
          description: Icon identifier for visual representation in lists and projects. Must be in the format `:icon_name:` (e.g., `:building:`, `:office:`, `:factory:`). Use standard emoji short names or custom icon names created in the workspace.
          example: ':building:'
        shortName:
          type: string
          description: Short abbreviation for the organization (3-5 characters, alphanumeric only). Used in projects and lists for quick identification. If not provided, will be auto-generated from the organization name.
          example: ACME
          maxLength: 5
        legalForm:
          type: string
          description: Legal company form or structure (e.g., "GmbH", "AG", "LLC", "Inc.", "Ltd.")
          example: GmbH
        type:
          type: string
          description: 'Type of organization defining the business relationship. Options: Customer (existing business partner), Prospect (potential customer), Supplier (external provider), Partner (strategic partner), ServiceProvider, Investor, Competitor, GovernmentAgency, EducationalInstitution, Target (target customer group).'
          enum:
            - Customer
            - Prospect
            - Target
            - Partner
            - Supplier
            - ServiceProvider
            - Investor
            - Competitor
            - GovernmentAgency
            - EducationalInstitution
          example: Customer
        color:
          type: string
          description: Hex color code used to visually distinguish this organization in lists, projects, and throughout the application (required)
          example: '#FF5733'
        startOfCollaboration:
          type: string
          description: 'Date when the business relationship with this organization started. Format: ISO 8601 date (YYYY-MM-DD).'
          format: date
          example: '2024-01-01'
        addressStreet:
          type: string
          description: Street address
          example: 123 Main St
        addressZip:
          type: string
          description: ZIP/Postal code
          example: '10001'
        addressCity:
          type: string
          description: City
          example: New York
        addressCountry:
          type: string
          description: Country
          example: USA
        addressHouseNumber:
          type: string
          description: House number
          example: 42A
        taxNumber:
          type: string
          description: Tax identification number
          example: DE123456789
        registrationNumber:
          type: string
          description: Registration number
          example: HRB 12345
        registrationCourt:
          type: string
          description: Registration court
          example: Munich District Court
        shortDescription:
          type: string
          description: Short description of the organization
          example: Leading software development company
        phoneNumber:
          type: string
          description: Phone number
          example: '+1234567890'
        faxNumber:
          type: string
          description: Fax number
          example: '+1234567891'
        email:
          type: string
          description: Email address
          example: contact@acme.com
        website:
          type: string
          description: Website URL
          example: https://acme.com
        logoId:
          type: string
          description: 'UUID of the uploaded logo file. To upload a logo: 1) Call POST /api/public/workspace/upload to upload the file, 2) Use the returned file ID here. The logo will appear in organization lists and project views.'
          example: 123e4567-e89b-12d3-a456-426614174000
        hourRate:
          type: number
          description: Customer-specific hourly rate used for all billable time entries for this organization. Overrides workspace default. Used when creating invoices.
          example: 150
        invoiceDueDays:
          type: number
          description: 'Number of days after invoice date until payment is due. Valid range: 1-120. Overrides workspace default for this organization.'
          example: 30
        enableInvoiceReminderFee:
          type: boolean
          description: Whether to charge a reminder fee for overdue invoices. Overrides workspace default. Set to true to enable, false to disable.
          example: true
        invoiceReminderFee:
          type: number
          description: 'Amount charged as a reminder fee for overdue invoices. Valid range: 0-1000. Only applies if enableInvoiceReminderFee is true.'
          example: 5
        enableInvoiceInterest:
          type: boolean
          description: Whether to charge interest on overdue invoices. Overrides workspace default. Set to true to enable, false to disable.
          example: false
        baseInvoiceInterest:
          type: number
          description: Base interest rate percentage (0-1000) used as the foundation for calculating default interest. Typically set by central banks (e.g., ECB, Bundesbank) and changes every six months. The final interest rate is baseInvoiceInterest + invoiceInterest.
          example: 3
        invoiceInterest:
          type: number
          description: 'Interest rate percentage (0-1000) added to baseInvoiceInterest to calculate the total default interest rate. According to BGB §288: typically +5% for private customers, +9% for businesses. Final rate = baseInvoiceInterest + invoiceInterest.'
          example: 5
        invoiceLanguage:
          type: string
          description: Language code used for generating invoices and reminders for this organization (e.g., "en", "de", "fr"). Overrides workspace default.
          example: en
        projectDocumentEnableToc:
          type: boolean
          description: Whether to automatically include a table of contents in project documents (quotes, requirement documents, contracts) for this organization. Overrides workspace default.
          example: true
        projectDocumentTitlePage:
          type: boolean
          description: Whether to include a title/cover page in project documents for this organization. Overrides workspace default.
          example: true
        projectDocumentHeadingStyle:
          type: string
          description: Formatting style for headings in project documents (e.g., "Sequential", "Normal"). Controls the hierarchy and appearance of document headings. Overrides workspace default.
          example: classic
        projectDocumentDefaultContactUserId:
          type: string
          description: UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents (quotes, order confirmations, etc.) created for this organization.
          example: 123e4567-e89b-12d3-a456-426614174000
        createProject:
          type: boolean
          description: Create a default project for this organization (internal use only)
          example: false
        parentOrganizationId:
          type: string
          description: UUID of the parent organization. Only top-level organizations (without a parent) can be selected as parents. Enables one-level hierarchical organization structure.
          example: 123e4567-e89b-12d3-a456-426614174000
        customFields:
          type: object
          description: Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields and their configurations.
          additionalProperties: true
          example:
            customField1: value1
            customField2: 123
        tags:
          description: Array of tag UUIDs to categorize this organization. Tags enable cross-project organization and filtering. Use an empty array to remove all tags.
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 223e4567-e89b-12d3-a456-426614174001
          type: array
          items:
            type: string
      required:
        - name
        - type
        - color
    OrganizationOfferTextsDto:
      type: object
      properties:
        offerIntroText:
          type: object
          description: Custom intro text for offers (HTML or Markdown). Overrides workspace default. Set to null to clear.
          nullable: true
        offerOutroText:
          type: object
          description: Custom outro text for offers (HTML or Markdown). Overrides workspace default. Set to null to clear.
          nullable: true
    GetOrganizationDocumentSettingsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Organization ID
          example: 123e4567-e89b-12d3-a456-426614174000
        projectDocumentEnableToc:
          type: object
          description: Whether to automatically include a table of contents in project documents for this organization. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable.
          example: true
          nullable: true
        projectDocumentTitlePage:
          type: object
          description: Whether to include a title/cover page in project documents for this organization. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable.
          example: false
          nullable: true
        projectDocumentHeadingStyle:
          type: string
          description: Formatting style for headings in project documents (e.g., Sequential, Normal). Controls the hierarchy and appearance of document headings. Set to `null` to inherit the workspace default, or provide a specific style to override.
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
          nullable: true
        projectDocumentDefaultContactUserId:
          type: object
          description: UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents created for this organization. Set to `null` to clear the default contact.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        projectDocumentEnableTocDefault:
          type: boolean
          description: Workspace default setting for enabling table of contents in project documents. This value is used when the organization-specific setting is `null`.
          example: true
        projectDocumentTitlePageDefault:
          type: boolean
          description: Workspace default setting for showing title/cover page in project documents. This value is used when the organization-specific setting is `null`.
          example: true
        projectDocumentHeadingStyleDefault:
          type: string
          description: Workspace default heading style for project documents. This value is used when the organization-specific setting is `null`.
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Normal
        documentLanguage:
          type: object
          description: Document language override for this organization. Overrides workspace default. Null if not set (inherits workspace default).
          example: de
          nullable: true
        documentLanguageDefault:
          type: string
          description: Workspace default document language. This value is used when the organization-specific setting is `null`.
          example: en
        offerTexts:
          type: object
          description: Offer text overrides for this organization (returned as HTML). Overrides workspace defaults. Null if not set (inherits workspace defaults).
          nullable: true
        offerTextsWorkspaceDefault:
          description: Workspace default offer texts (returned as HTML). These are used when organization-specific texts are not set.
          allOf:
            - $ref: '#/components/schemas/OrganizationOfferTextsDto'
      required:
        - id
        - projectDocumentEnableToc
        - projectDocumentTitlePage
        - projectDocumentHeadingStyle
        - projectDocumentDefaultContactUserId
        - projectDocumentEnableTocDefault
        - projectDocumentTitlePageDefault
        - projectDocumentHeadingStyleDefault
        - documentLanguage
        - documentLanguageDefault
        - offerTexts
        - offerTextsWorkspaceDefault
    PatchOrganizationDocumentSettingsDto:
      type: object
      properties:
        projectDocumentEnableToc:
          type: object
          description: Whether to automatically include a table of contents in project documents. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable. Omit this field to leave it unchanged.
          example: true
          nullable: true
        projectDocumentTitlePage:
          type: object
          description: Whether to include a title/cover page in project documents. Set to `null` to inherit the workspace default, `true` to enable, or `false` to disable. Omit this field to leave it unchanged.
          example: false
          nullable: true
        projectDocumentHeadingStyle:
          type: string
          description: Formatting style for headings in project documents (e.g., Sequential, Normal). Set to `null` to inherit the workspace default, or provide a specific style to override. Omit this field to leave it unchanged.
          enum:
            - Normal
            - Sequential
            - SequentialFromSecondLevel
            - SequentialWithParagraphs
          example: Sequential
          nullable: true
        projectDocumentDefaultContactUserId:
          type: object
          description: UUID of the organization member (contact person) to set as the default recipient for new project documents. Set to `null` to clear the default contact. Omit this field to leave it unchanged.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        documentLanguage:
          type: object
          description: Document language override for this organization. Overrides workspace default. Set to null to inherit workspace default. Omit this field to leave it unchanged.
          example: de
        offerTexts:
          description: Offer text overrides for this organization. Overrides workspace defaults. Set to null to inherit workspace defaults. Omit this field to leave it unchanged.
          allOf:
            - $ref: '#/components/schemas/OrganizationOfferTextsDto'
    PatchOrganizationDto:
      type: object
      properties:
        name:
          type: string
          description: Full legal name of the organization (required)
          example: Acme Corporation
        icon:
          type: string
          description: Icon identifier for visual representation in lists and projects. Must be in the format `:icon_name:` (e.g., `:building:`, `:office:`, `:factory:`). Use standard emoji short names or custom icon names created in the workspace.
          example: ':building:'
        shortName:
          type: string
          description: Short abbreviation for the organization (3-5 characters, alphanumeric only). Used in projects and lists for quick identification. If not provided, will be auto-generated from the organization name.
          example: ACME
          maxLength: 5
        legalForm:
          type: string
          description: Legal company form or structure (e.g., "GmbH", "AG", "LLC", "Inc.", "Ltd.")
          example: GmbH
        type:
          type: string
          description: 'Type of organization defining the business relationship. Options: Customer (existing business partner), Prospect (potential customer), Supplier (external provider), Partner (strategic partner), ServiceProvider, Investor, Competitor, GovernmentAgency, EducationalInstitution, Target (target customer group).'
          enum:
            - Customer
            - Prospect
            - Target
            - Partner
            - Supplier
            - ServiceProvider
            - Investor
            - Competitor
            - GovernmentAgency
            - EducationalInstitution
          example: Customer
        color:
          type: string
          description: Hex color code used to visually distinguish this organization in lists, projects, and throughout the application (required)
          example: '#FF5733'
        startOfCollaboration:
          type: string
          description: 'Date when the business relationship with this organization started. Format: ISO 8601 date (YYYY-MM-DD).'
          format: date
          example: '2024-01-01'
        addressStreet:
          type: string
          description: Street address
          example: 123 Main St
        addressZip:
          type: string
          description: ZIP/Postal code
          example: '10001'
        addressCity:
          type: string
          description: City
          example: New York
        addressCountry:
          type: string
          description: Country
          example: USA
        addressHouseNumber:
          type: string
          description: House number
          example: 42A
        taxNumber:
          type: string
          description: Tax identification number
          example: DE123456789
        registrationNumber:
          type: string
          description: Registration number
          example: HRB 12345
        registrationCourt:
          type: string
          description: Registration court
          example: Munich District Court
        shortDescription:
          type: string
          description: Short description of the organization
          example: Leading software development company
        phoneNumber:
          type: string
          description: Phone number
          example: '+1234567890'
        faxNumber:
          type: string
          description: Fax number
          example: '+1234567891'
        email:
          type: string
          description: Email address
          example: contact@acme.com
        website:
          type: string
          description: Website URL
          example: https://acme.com
        logoId:
          type: string
          description: 'UUID of the uploaded logo file. To upload a logo: 1) Call POST /api/public/workspace/upload to upload the file, 2) Use the returned file ID here. The logo will appear in organization lists and project views.'
          example: 123e4567-e89b-12d3-a456-426614174000
        hourRate:
          type: number
          description: Customer-specific hourly rate used for all billable time entries for this organization. Overrides workspace default. Used when creating invoices.
          example: 150
        invoiceDueDays:
          type: number
          description: 'Number of days after invoice date until payment is due. Valid range: 1-120. Overrides workspace default for this organization.'
          example: 30
        enableInvoiceReminderFee:
          type: boolean
          description: Whether to charge a reminder fee for overdue invoices. Overrides workspace default. Set to true to enable, false to disable.
          example: true
        invoiceReminderFee:
          type: number
          description: 'Amount charged as a reminder fee for overdue invoices. Valid range: 0-1000. Only applies if enableInvoiceReminderFee is true.'
          example: 5
        enableInvoiceInterest:
          type: boolean
          description: Whether to charge interest on overdue invoices. Overrides workspace default. Set to true to enable, false to disable.
          example: false
        baseInvoiceInterest:
          type: number
          description: Base interest rate percentage (0-1000) used as the foundation for calculating default interest. Typically set by central banks (e.g., ECB, Bundesbank) and changes every six months. The final interest rate is baseInvoiceInterest + invoiceInterest.
          example: 3
        invoiceInterest:
          type: number
          description: 'Interest rate percentage (0-1000) added to baseInvoiceInterest to calculate the total default interest rate. According to BGB §288: typically +5% for private customers, +9% for businesses. Final rate = baseInvoiceInterest + invoiceInterest.'
          example: 5
        invoiceLanguage:
          type: string
          description: Language code used for generating invoices and reminders for this organization (e.g., "en", "de", "fr"). Overrides workspace default.
          example: en
        projectDocumentEnableToc:
          type: boolean
          description: Whether to automatically include a table of contents in project documents (quotes, requirement documents, contracts) for this organization. Overrides workspace default.
          example: true
        projectDocumentTitlePage:
          type: boolean
          description: Whether to include a title/cover page in project documents for this organization. Overrides workspace default.
          example: true
        projectDocumentHeadingStyle:
          type: string
          description: Formatting style for headings in project documents (e.g., "Sequential", "Normal"). Controls the hierarchy and appearance of document headings. Overrides workspace default.
          example: classic
        projectDocumentDefaultContactUserId:
          type: string
          description: UUID of the organization member (contact person) who will be automatically set as the recipient for new project documents (quotes, order confirmations, etc.) created for this organization.
          example: 123e4567-e89b-12d3-a456-426614174000
        createProject:
          type: boolean
          description: Create a default project for this organization (internal use only)
          example: false
        parentOrganizationId:
          type: string
          description: UUID of the parent organization. Only top-level organizations (without a parent) can be selected as parents. Enables one-level hierarchical organization structure.
          example: 123e4567-e89b-12d3-a456-426614174000
        customFields:
          type: object
          description: Custom field values as key-value pairs. Keys should match custom field IDs configured in workspace settings. Values must match the field type (string, number, boolean, date, etc.). Use the GET /administration/organization-settings/custom-fields endpoint to retrieve available custom fields and their configurations.
          additionalProperties: true
          example:
            customField1: value1
            customField2: 123
        tags:
          description: Array of tag UUIDs to categorize this organization. Tags enable cross-project organization and filtering. Use an empty array to remove all tags.
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 223e4567-e89b-12d3-a456-426614174001
          type: array
          items:
            type: string
    OrganizationInvoiceSettingsResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Organization ID
          example: uuid-123
        hourRate:
          type: object
          description: Hourly rate
          example: 75
          nullable: true
        invoiceDueDays:
          type: object
          description: Invoice due days
          example: 30
          nullable: true
        enableInvoiceReminderFee:
          type: object
          description: Enable invoice reminder fee
          example: true
          nullable: true
        invoiceReminderFee:
          type: object
          description: Invoice reminder fee
          example: 5.5
          nullable: true
        enableInvoiceInterest:
          type: object
          description: Enable invoice interest
          example: true
          nullable: true
        baseInvoiceInterest:
          type: object
          description: Base invoice interest
          example: 2.5
          nullable: true
        invoiceInterest:
          type: object
          description: Invoice interest
          example: 8
          nullable: true
        invoiceLanguage:
          type: object
          description: Invoice language
          example: en
          nullable: true
        customTexts:
          type: object
          description: Custom invoice text templates (returned as HTML)
          properties:
            withBestRegards:
              type: string
              nullable: true
            invoiceGreeting:
              type: string
              nullable: true
            invoiceFooter:
              type: string
              nullable: true
            invoiceReminderFeeWarning:
              type: string
              nullable: true
            firstReminderGreeting:
              type: string
              nullable: true
            firstReminderFooter:
              type: string
              nullable: true
            secondReminderGreeting:
              type: string
              nullable: true
            secondReminderFooter:
              type: string
              nullable: true
            cancelationBody:
              type: string
              nullable: true
      required:
        - id
        - hourRate
        - invoiceDueDays
        - enableInvoiceReminderFee
        - invoiceReminderFee
        - enableInvoiceInterest
        - baseInvoiceInterest
        - invoiceInterest
        - invoiceLanguage
        - customTexts
    PatchOrganizationInvoiceSettingsDto:
      type: object
      properties:
        hourRate:
          type: object
          description: Hourly rate for the organization
          example: 75
          nullable: true
        invoiceDueDays:
          type: object
          description: Number of days until invoice is due (1-120)
          example: 30
          nullable: true
        enableInvoiceReminderFee:
          type: object
          description: Enable invoice reminder fee (null = use default)
          example: true
          nullable: true
        invoiceReminderFee:
          type: object
          description: Invoice reminder fee amount (0-1000)
          example: 5.5
          nullable: true
        enableInvoiceInterest:
          type: object
          description: Enable invoice interest (null = use default)
          example: true
          nullable: true
        baseInvoiceInterest:
          type: object
          description: Base invoice interest rate percentage (0-1000)
          example: 2.5
          nullable: true
        invoiceInterest:
          type: object
          description: Invoice interest rate percentage (0-1000)
          example: 8
          nullable: true
        invoiceLanguage:
          type: object
          description: Invoice language code
          example: en
          nullable: true
        customTexts:
          description: Custom invoice text templates. Content can be provided as HTML or Markdown and will be converted to internal format.
          allOf:
            - $ref: '#/components/schemas/InvoiceCustomTextsDto'
    TeamUserDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the user (UUID format). Use this ID when assigning users to teams or referencing team members.
          example: 123e4567-e89b-12d3-a456-426614174000
        firstName:
          type: string
          description: The user first name as stored in their employee profile.
          example: John
        lastName:
          type: string
          description: The user last name as stored in their employee profile.
          example: Doe
        avatarId:
          type: object
          description: Unique identifier for the user avatar image file (UUID format). This ID can be used to retrieve the avatar image through the file API. Null if the user has not uploaded an avatar.
          example: 123e4567-e89b-12d3-a456-426614174001
          nullable: true
      required:
        - id
        - firstName
        - lastName
        - avatarId
    TeamResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the team (UUID format). Use this ID to reference the team in update, delete, or project assignment operations.
          example: 123e4567-e89b-12d3-a456-426614174000
        name:
          type: string
          description: The display name of the team. This name appears in team lists, project assignments, and throughout the application.
          example: Development Team
        icon:
          type: string
          description: 'Emoji icon used for visual identification of the team. This icon appears next to the team name in lists and can be used to quickly identify teams. Common examples include: "🧑‍🤝‍🧑" (people holding hands), "💻" (laptop), "🎨" (artist palette).'
          example: 🧑‍🤝‍🧑
        usersCount:
          type: number
          description: Total number of users currently assigned to the team. This count matches the length of the users array and is provided for convenience.
          example: 5
        users:
          description: Complete list of all users who are members of this team. Each user object includes their ID, name, and avatar information. Use the user IDs from this array when you need to reference team members.
          type: array
          items:
            $ref: '#/components/schemas/TeamUserDto'
      required:
        - id
        - name
        - icon
        - usersCount
        - users
    CreateTeamDto:
      type: object
      properties:
        name:
          type: string
          description: 'The display name for the team. Choose a descriptive name that clearly identifies the team purpose or department. Examples: "Development Team", "Marketing", "Customer Support", "Sales Team". This name will appear in team lists and project assignments.'
          example: Development Team
        icon:
          type: string
          description: Emoji icon for visual identification of the team. This icon appears next to the team name throughout the application. Choose any emoji that represents the team (e.g., "🧑‍🤝‍🧑" for teams, "💻" for development, "🎨" for design). The icon must be a non-empty string.
          example: 🧑‍🤝‍🧑
        users:
          description: Array of user IDs (UUIDs) to assign as initial team members. At least one user must be provided. Use User.id values from GET /workspace/users; users should be existing employees in the workspace. You can add or remove members later using the update endpoint.
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 123e4567-e89b-12d3-a456-426614174001
          type: array
          items:
            type: string
      required:
        - name
        - icon
        - users
    PatchTeamDto:
      type: object
      properties:
        name:
          type: string
          description: New display name for the team. If provided, replaces the existing team name. If omitted, the team name remains unchanged.
          example: Updated Team Name
        icon:
          type: string
          description: New emoji icon for the team. If provided, replaces the existing team icon. If omitted, the team icon remains unchanged.
          example: 💻
        users:
          description: Complete array of user IDs (UUIDs) that should be members of the team. If provided, this array replaces the entire team membership (it is not merged with existing members). You must include all users you want in the team, including existing members you want to keep. At least one user must be included. If omitted, the team membership remains unchanged. Use User.id values from GET /workspace/users; users should be existing employees in the workspace.
          example:
            - 123e4567-e89b-12d3-a456-426614174000
            - 123e4567-e89b-12d3-a456-426614174002
          type: array
          items:
            type: string
    DeclineVacationDto:
      type: object
      properties:
        reason:
          type: string
          description: Optional reason explaining why the vacation request was declined or cancelled. This reason will be stored with the vacation record and visible in the vacation history. Providing a reason helps employees understand the decision and improves transparency in the approval process.
          example: Insufficient coverage during this period
    EmployeeJournalResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier (UUID) for this journal entry
          example: 550e8400-e29b-41d4-a716-446655440000
        employeeId:
          type: string
          description: UUID of the employee this journal entry is associated with. This identifies which employee the entry is about.
          example: 550e8400-e29b-41d4-a716-446655440000
        createdAt:
          type: string
          description: 'ISO 8601 timestamp indicating when this journal entry was created. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., "2024-01-15T10:30:00Z").'
          example: '2024-01-15T10:30:00Z'
        createdBy:
          type: string
          description: UUID of the user who created this journal entry. This identifies who wrote the entry, which is useful for tracking authorship and accountability.
          example: 550e8400-e29b-41d4-a716-446655440000
        lastUpdated:
          type: string
          description: 'ISO 8601 timestamp indicating when this journal entry was last modified. Format: YYYY-MM-DDTHH:mm:ssZ (e.g., "2024-01-16T14:20:00Z"). This is automatically updated whenever the entry is edited via the PATCH endpoint.'
          example: '2024-01-16T14:20:00Z'
        mood:
          type: string
          description: 'Emotional indicator for the journal entry. Values: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). This helps categorize and filter entries by emotional context.'
          enum:
            - Sad
            - Neutral
            - Happy
          example: Neutral
        reminder:
          type: object
          description: 'Optional reminder date in ISO 8601 format (YYYY-MM-DD). Set when creating or updating the entry to track follow-up dates for goals, agreements, or actions. Returns `null` if no reminder is set. Example: "2024-12-31" for December 31, 2024.'
          example: '2024-12-31'
          nullable: true
        body:
          type: string
          description: Journal entry content in HTML format. The content is automatically converted from the internal document format (IDoc) to HTML for easy display in web applications. Contains the rich text content including headings, paragraphs, lists, links, and formatting as originally provided.
          example: <h1>Employee Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>
      required:
        - id
        - employeeId
        - createdAt
        - createdBy
        - lastUpdated
        - mood
        - reminder
        - body
    CreateEmployeeJournalDto:
      type: object
      properties:
        employeeId:
          type: string
          description: UUID of the employee this journal entry is about. The employee must exist in the workspace and be accessible to the current user. Employees can only create entries for their own employee record unless they have the `Employees.edit` permission.
          example: 550e8400-e29b-41d4-a716-446655440000
        body:
          type: string
          description: |-
            Rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             Use this field to document conversations, observations, agreements, or any notes about the employee.
          example: <h1>Employee Update</h1><p>Had a productive meeting today. Discussed project timeline and next steps.</p>
        mood:
          type: string
          description: 'Emotional indicator for the journal entry. Use this to quickly categorize the tone or sentiment of the interaction. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). Defaults to `Neutral` if not provided. This helps filter and analyze journal entries by emotional context.'
          enum:
            - Sad
            - Neutral
            - Happy
          example: Neutral
        reminder:
          type: string
          description: 'Optional reminder date in ISO 8601 format (YYYY-MM-DD). Use this to set a follow-up date for reviewing goals, agreements, or taking action related to this journal entry. The reminder date can be used to filter entries and trigger notifications. Example: "2024-12-31" for December 31, 2024. If not provided, no reminder is set.'
          example: '2024-12-31'
      required:
        - employeeId
        - body
    PatchEmployeeJournalDto:
      type: object
      properties:
        employeeId:
          type: string
          description: UUID of the employee to associate this journal entry with. If provided, changes which employee this entry is about. The new employee must exist and be accessible. If the new employee is the current user's own employee record, `visibleToSubject` will be automatically set to `true`. If omitted, the existing employee association is preserved.
          example: 550e8400-e29b-41d4-a716-446655440000
        body:
          type: string
          description: |-
            Updated rich text content for the journal entry in HTML or Markdown format. The content is automatically converted to the internal document format (IDoc) for storage. Supports headings, paragraphs, lists, links, and formatting. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer.

            For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably.

            Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>.

            In HTML you can use:
            ## Node Types and Marks

            ### Nodes

            **paragraph**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers.
            Atts:
            - extraPlaceholder: null
            ```html
            <p class="paragraph-base" extraplaceholder="null/some-value"></p>
            ```

            **heading**
            Type: block
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Atts:
            - level: 1
            ```html
            <h1 class="heading-base"></h1>
            ```

            **bulletList**
            Type: block list
            Content: listItem+
            Atts: none
            ```html
            <ul class="list-base"><li></li></ul>
            ```

            **hardBreak**
            Type: inline
            Atts: none
            ```html
            <br>
            ```

            **horizontalRule**
            Type: block
            Atts: none
            ```html
            <hr class="hr-base">
            ```

            **orderedList**
            Type: block list
            Content: listItem+
            Atts:
            - start: 1
            - type: null
            ```html
            <ol class="order-list-base" type="null/some-value"><li></li></ol>
            ```

            **listItem**
            Content: (paragraph|list)* (paragraphs and/or lists, in any order)
            Atts: none
            ```html
            <li></li>
            ```

            **blockquote**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts: none
            ```html
            <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote>
            ```

            **table**
            Type: block
            Content: tableRow+
            Atts: none
            ```html
            <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table>
            ```

            **tableRow**
            Content: (tableCell | tableHeader)*
            Atts: none
            ```html
            <tr></tr>
            ```

            **tableHeader**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th>
            ```

            **tableCell**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Atts:
            - colspan: 1
            - rowspan: 1
            - colwidth: null
            - align: null
            ```html
            <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td>
            ```

            **callout**
            Type: block
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout".
            Atts:
            - icon: :information_source:
            ```html
            <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div>
            ```

            **appImage**
            Type: block
            Block for an image file already uploaded in Leadtime.
            - fileId: Id of the stored image. Must be a real uploaded file; do not invent.
            - filename: Display name; should match the file.
            - width, align, size: Layout as in the editor. If no file id is available, obtain one via upload/API before outputting this node with a fake id.
            Atts:
            - fileId: 
            - filename: 
            - width: 500
            - align: left
            - size: 0
            ```html
            <div data-type="appImage" fileid="" filename="" width="500" align="left" size="0"></div>
            ```

            **collapse**
            Type: block
            Content: collapseTitle collapseBody (a collapse title node, then a collapse body)
            Expandable/collapsible section. Required structure: one collapseTitle (summary, inline) then one collapseBody (blocks) as direct children, matching data-type= collapseTitle / collapseBody tags under <collapse>. Do not use title/body nodes outside of a collapse.
            Atts: none
            ```html
            <collapse><collapse-title></collapse-title><collapse-body><p class="paragraph-base"></p></collapse-body></collapse>
            ```

            **collapseBody**
            Content: block+ (one or more block child nodes (e.g. paragraph, list, …))
            Expandable body of a collapse. Only use as sibling of collapseTitle inside a collapse; holds the block content that shows when expanded.
            Atts: none
            ```html
            <collapse-body><p class="paragraph-base"></p></collapse-body>
            ```

            **collapseTitle**
            Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…)
            Clickable title row of a collapse block. Only use inside a collapse that also has collapseBody; content is the summary line shown when collapsed.
            Atts: none
            ```html
            <collapse-title></collapse-title>
            ```

            **emoji**
            Type: inline
            Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render.
            Atts:
            - icon: :smile:
            ```html
            <span data-type="emoji" data-icon=":smile:"></span>
            ```

            ### Marks

            **link**
            Atts:
            - href: null
            - target: _blank
            - rel: noopener noreferrer nofollow
            - class: null
            - title: null
            ```html
            <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a>
            ```

            **bold**
            Atts: none
            ```html
            <strong>bold text example</strong>
            ```

            **code**
            Atts: none
            ```html
            <code>code text example</code>
            ```

            **italic**
            Atts: none
            ```html
            <em>italic text example</em>
            ```

            **strike**
            Atts: none
            ```html
            <s>strike text example</s>
            ```

            **underline**
            Atts: none
            ```html
            <u>underline text example</u>
            ```
             If omitted, the existing body content remains unchanged.
          example: <h1>Updated Employee Status</h1><p>Follow-up meeting scheduled for next week.</p>
        mood:
          type: string
          description: 'Updated emotional indicator for the journal entry. Options: `Happy` (positive interaction), `Neutral` (standard/neutral interaction), or `Sad` (negative or concerning interaction). If omitted, the existing mood value remains unchanged.'
          enum:
            - Sad
            - Neutral
            - Happy
          example: Happy
        reminder:
          type: object
          description: Updated reminder date in ISO 8601 format (YYYY-MM-DD). Provide a date string (e.g., "2024-12-31") to set or update the reminder. Provide `null` to clear an existing reminder. If omitted entirely, the existing reminder remains unchanged. Use reminders to track follow-up dates for goals, agreements, or actions related to this journal entry.
          example: '2024-12-31'
          nullable: true
    CalendarFeedConfigResponseDto:
      type: object
      properties:
        url:
          type: string
          description: HTTPS URL of the employee’s Leadtime calendar export (.ics). Subscribe in external calendar apps.
      required:
        - url
    ExternalCalendarFeedResponseDto:
      type: object
      properties:
        id:
          type: string
        label:
          type: string
        sourceUrl:
          type: string
        enabled:
          type: boolean
        lastFetchedAt:
          type: object
          nullable: true
        lastError:
          type: object
          nullable: true
        createdAt:
          type: string
        editedAt:
          type: string
      required:
        - id
        - sourceUrl
        - enabled
        - createdAt
        - editedAt
    CreateEmployeeExternalCalendarFeedDto:
      type: object
      properties:
        label:
          type: string
        sourceUrl:
          type: string
          description: HTTPS iCalendar (.ics) URL from the external provider.
        enabled:
          type: boolean
      required:
        - sourceUrl
    PatchEmployeeExternalCalendarFeedDto:
      type: object
      properties:
        label:
          type: string
        sourceUrl:
          type: string
        enabled:
          type: boolean
    EmployeeResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the employee (UUID format)
          example: emp_123
        firstName:
          type: string
          description: First name of the employee
          example: John
        lastName:
          type: string
          description: Last name of the employee
          example: Doe
        email:
          type: string
          description: Email address of the employee. This is also the login email if canLogin is true.
          example: john.doe@company.com
        phone:
          type: object
          description: Phone number in the format stored in the system. Null if not provided.
          example: '+1234567890'
          nullable: true
        position:
          type: object
          description: Job title or position of the employee. Null if not set.
          example: Software Engineer
          nullable: true
        title:
          type: object
          description: Title prefix (e.g., "Mr.", "Mrs.", "Dr.", "Prof."). Null if not set.
          example: Mr.
          nullable: true
        degree:
          type: object
          description: Academic degree (e.g., "B.Sc.", "M.Sc.", "Ph.D."). Null if not set.
          example: M.Sc.
          nullable: true
        addressStreet:
          type: object
          description: Street name (without house number). Null if address is not set.
          example: Main Street
          nullable: true
        addressZip:
          type: object
          description: ZIP or postal code. Null if address is not set.
          example: '12345'
          nullable: true
        addressCity:
          type: object
          description: City name. Null if address is not set.
          example: Berlin
          nullable: true
        addressCountry:
          type: object
          description: Country code in ISO 3166-1 alpha-2 format (e.g., "DE", "US"). Null if address is not set.
          example: DE
          nullable: true
        addressHouseNumber:
          type: object
          description: House or building number. Null if address is not set.
          example: '42'
          nullable: true
        birthDate:
          type: object
          description: Birth date in ISO 8601 date format (YYYY-MM-DD). Null if not set.
          example: '1990-01-15'
          nullable: true
        employmentMode:
          type: object
          description: Employment mode indicating the type of employment (e.g., "FullTime", "PartTime", "Contract", "Temporary", "Freelance"). Null if not set.
          example: FullTime
          nullable: true
        entryDate:
          type: object
          description: Date when the employee joined the company, in ISO 8601 date format (YYYY-MM-DD). Null if not set.
          example: '2020-01-15'
          nullable: true
        exitDate:
          type: object
          description: Date when the employee left the company, in ISO 8601 date format (YYYY-MM-DD). Null if employee is still active. Used to calculate isActive status.
          example: null
          nullable: true
        isActive:
          type: boolean
          description: 'Whether the employee is currently active. Calculated as: exitDate is null OR exitDate is in the future. Active employees appear in employee lists and can be assigned to teams/projects.'
          example: true
        incomeTaxClass:
          type: object
          description: Income tax class (typically used in German tax system, values like "1", "2", "3", etc.). Null if not set.
          example: '1'
          nullable: true
        userId:
          type: string
          description: ID of the associated user account. This links the employee record to the user account that handles authentication and permissions.
          example: user_123
        roleId:
          type: string
          description: Role ID assigned to the employee user account. The role determines what permissions the employee has when they log in.
          example: ws_abc_role_123
        canLogin:
          type: boolean
          description: Whether the employee can login to the system. If true, the employee has an active user account and can access the system.
          example: true
        teamIds:
          description: Array of team IDs the employee is assigned to. Teams are used for organizational structure and permissions. Empty array if employee is not assigned to any teams.
          example:
            - team_1
            - team_2
          type: array
          items:
            type: string
        avatarId:
          type: object
          description: Avatar configuration ID. Use /api/avatars/public/{avatarId}.svg to render it.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        avatarUrl:
          type: object
          description: Full URL to the employee's avatar image. Generated from the avatar file ID. Null if employee has no avatar. Use this URL to display the avatar in your application.
          example: https://workc.example.com/api/avatars/public/avatar_123.svg
          nullable: true
        createdAt:
          type: string
          description: Timestamp when the employee record was created, in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ).
          example: '2020-01-15T10:00:00Z'
        updatedAt:
          type: string
          description: Timestamp when the employee record was last updated, in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ).
          example: '2020-01-15T10:00:00Z'
        chargeableHoursDayGoal:
          type: object
          description: Chargeable (billable) hours per day goal. Null if not set.
          example: 6.5
          nullable: true
        weeklyWorkingTime:
          description: Array of weekly working time entries for the employee. Each entry defines working days and hours that apply from a specific date. The active entry for a given date is the one with the most recent dateFrom that is still on or before that date.
          example:
            - id: wwt_123
              dateFrom: '2021-01-01'
              workingDays:
                - 1
                - 1
                - 1
                - 1
                - 1
                - 0
                - 0
              weeklyWorkingHours: 40
          type: array
          items:
            type: object
        salaries:
          description: Array of salary entries for the employee. Each entry defines a salary amount that applies from a specific date.
          example:
            - id: sal_123
              dateFrom: '2021-01-01'
              salary: 5000
          type: array
          items:
            type: object
        vacationDays:
          description: Array of vacation days entries for the employee. Each entry defines vacation days per year that apply from a specific date.
          example:
            - id: vac_123
              dateFrom: '2021-01-01'
              days: 30
          type: array
          items:
            type: object
      required:
        - id
        - firstName
        - lastName
        - email
        - phone
        - position
        - title
        - degree
        - addressStreet
        - addressZip
        - addressCity
        - addressCountry
        - addressHouseNumber
        - birthDate
        - employmentMode
        - entryDate
        - exitDate
        - isActive
        - incomeTaxClass
        - userId
        - roleId
        - canLogin
        - teamIds
        - avatarId
        - avatarUrl
        - createdAt
        - updatedAt
        - chargeableHoursDayGoal
    CreateEmployeeDto:
      type: object
      properties:
        firstName:
          type: string
          description: First name of the employee. This will be synced to the user account if canLogin is true.
          example: John
        lastName:
          type: string
          description: Last name of the employee. This will be synced to the user account if canLogin is true.
          example: Doe
        email:
          type: string
          description: Email address of the employee. Must be unique within the workspace. If canLogin is true, this will be the login email. If a HelpdeskUser with this email exists, it will be upgraded to Employee type.
          example: john.doe@company.com
        position:
          type: string
          description: Job title or position of the employee (e.g., "Software Engineer", "Product Manager", "Sales Director").
          example: Software Engineer
        roleId:
          type: string
          description: Role ID to assign to the employee user account. The role determines what permissions the employee has when they log in. Use GET /administration/roles to retrieve available role IDs.
          example: ws_abc_role_123
        canLogin:
          type: boolean
          description: Whether the employee can login to the system. If true, a user account will be created automatically with Active status. In ready workspaces, an invitation email is sent immediately. During initial workspace setup, invitation emails are suppressed until setup is finished. If false, no user account is created initially (but can be created later via update).
          example: false
          default: false
        teams:
          description: Array of team IDs to assign the employee to. Use GET /teams to retrieve available teams. Teams are used for organizational structure and permissions. Provide an empty array or omit to create an employee without team assignments.
          example:
            - team_1
            - team_2
          type: array
          items:
            type: string
        avatarId:
          type: string
          description: Avatar configuration ID. During the transition, an image file ID from POST /workspace/upload is also accepted and converted to an uploaded avatar configuration. Omit to create the default generated avatar.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        avatarConfig:
          type: object
          description: Generated avatar configuration. If provided, it takes precedence over avatarId.
          example:
            style: initials
            options:
              seed: Jane Doe
        employmentMode:
          type: string
          description: Employment mode classification (e.g., FullTime, PartTime, Contract, Temporary, Freelance). Omit to use the default FullTime. This is metadata only and does not change working hours, schedule, capacity, or availability.
          example: PartTime
          enum:
            - FullTime
            - PartTime
            - Contract
            - Temporary
            - Freelance
      required:
        - firstName
        - lastName
        - email
        - roleId
    CreateOrUpdateWeeklyWorkingTimeDto:
      type: object
      properties:
        id:
          type: string
          description: Entry ID. If provided, updates the existing entry. If omitted, creates a new entry.
          example: wwt_123
        dateFrom:
          type: string
          description: Date from which this working time configuration applies, in ISO 8601 date format (YYYY-MM-DD).
          example: '2021-01-01'
        workingDays:
          description: Array of 7 numbers representing working days (Monday=0, Sunday=6). Each value is 1 if the employee works that day, 0 if not. Must have at least one working day (at least one value must be 1).
          example:
            - 1
            - 1
            - 1
            - 1
            - 1
            - 0
            - 0
          type: array
          items:
            type: number
        weeklyWorkingHours:
          type: number
          description: Weekly working hours (total hours per week). Maximum value is 168 (24 hours × 7 days).
          example: 40
          minimum: 1
          maximum: 168
      required:
        - dateFrom
        - workingDays
        - weeklyWorkingHours
    CreateOrUpdateSalaryDto:
      type: object
      properties:
        id:
          type: string
          description: Entry ID. If provided, updates the existing entry. If omitted, creates a new entry.
          example: sal_123
        dateFrom:
          type: string
          description: Date from which this salary applies, in ISO 8601 date format (YYYY-MM-DD).
          example: '2021-01-01'
        salary:
          type: number
          description: Monthly salary amount. Must be a positive integer.
          example: 5000
          minimum: 0
      required:
        - dateFrom
        - salary
    CreateOrUpdateVacationDaysDto:
      type: object
      properties:
        id:
          type: string
          description: Entry ID. If provided, updates the existing entry. If omitted, creates a new entry.
          example: vac_123
        dateFrom:
          type: string
          description: Date from which this vacation days configuration applies, in ISO 8601 date format (YYYY-MM-DD).
          example: '2021-01-01'
        days:
          type: number
          description: Number of vacation days per year. Must be a positive integer.
          example: 30
          minimum: 0
      required:
        - dateFrom
        - days
    PatchEmployeeDto:
      type: object
      properties:
        firstName:
          type: string
          description: First name of the employee. If changed, the associated user account will be automatically synced.
          example: Jane
        lastName:
          type: string
          description: Last name of the employee. If changed, the associated user account will be automatically synced.
          example: Smith
        email:
          type: string
          description: Email address of the employee. Must be unique within the workspace. If changed, the associated user account will be synced. If a HelpdeskUser with the new email exists, it will be upgraded to Employee type.
          example: jane.smith@company.com
        title:
          type: object
          description: Title prefix (e.g., "Mr.", "Mrs.", "Ms.", "Dr.", "Prof."). Set to null to remove the title.
          example: Dr.
          nullable: true
        degree:
          type: object
          description: Academic degree (e.g., "B.Sc.", "M.Sc.", "Ph.D.", "MBA"). Set to null to remove the degree.
          example: M.Sc.
          nullable: true
        position:
          type: string
          description: Job title or position of the employee (e.g., "Senior Software Engineer", "Product Manager").
          example: Senior Software Engineer
        phone:
          type: object
          description: Phone number in any format. Set to null to remove the phone number.
          example: '+1234567890'
          nullable: true
        addressStreet:
          type: object
          description: Street name (without house number). Set to null to clear the street address.
          example: Main Street
          nullable: true
        addressZip:
          type: object
          description: ZIP or postal code. Set to null to clear the ZIP code.
          example: '12345'
          nullable: true
        addressCity:
          type: object
          description: City name. Set to null to clear the city.
          example: Berlin
          nullable: true
        addressCountry:
          type: object
          description: Country code in ISO 3166-1 alpha-2 format (e.g., "DE" for Germany, "US" for United States). Set to null to clear the country.
          example: DE
          nullable: true
        addressHouseNumber:
          type: object
          description: House or building number. Set to null to clear the house number.
          example: '42'
          nullable: true
        birthDate:
          type: object
          description: Birth date in ISO 8601 date format (YYYY-MM-DD). Set to null to remove the birth date.
          example: '1990-01-15'
          nullable: true
        avatarId:
          type: object
          description: Avatar configuration ID. During the transition, an image file ID from POST /workspace/upload is also accepted and converted to an uploaded avatar configuration. Set to null to remove the avatar.
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        avatarConfig:
          type: object
          description: Generated avatar configuration. If provided, it takes precedence over avatarId.
          example:
            style: initials
            options:
              seed: Jane Doe
        roleId:
          type: string
          description: Role ID to assign to the employee user account. The role determines permissions. Must be a valid role ID from your workspace.
          example: ws_abc_role_456
        canLogin:
          type: boolean
          description: 'Whether the employee can login to the system. When changed from false to true: user account is created (if it doesn''t exist), and user status becomes Active. In ready workspaces, an invitation email is sent automatically. During initial workspace setup, invitation emails are suppressed until setup is finished.'
          example: true
        teams:
          description: Array of team IDs to assign the employee to. This replaces the entire team assignment list. Provide an empty array to remove all team assignments. Omit this field to keep existing team assignments unchanged.
          example:
            - team_1
            - team_3
          type: array
          items:
            type: string
        chargeableHoursDayGoal:
          type: object
          description: Chargeable (billable) hours per day goal. Used for time tracking targets and notifications.
          example: 6.5
          nullable: true
        employmentMode:
          type: string
          description: Employment mode classification (e.g., FullTime, PartTime, Contract, Temporary, Freelance). This is metadata only and does not change working hours, schedule, capacity, or availability.
          example: PartTime
          enum:
            - FullTime
            - PartTime
            - Contract
            - Temporary
            - Freelance
    CustomPriceDto:
      type: object
      properties:
        priceFixed:
          type: number
          description: Custom fixed price override. Set to null to use the base price.
          example: 100.5
          nullable: true
        priceSubscription:
          type: number
          description: Custom subscription price override. Set to null to use the base price.
          example: 50.25
          nullable: true
        pricePerUnit:
          type: number
          description: Custom per-unit price override. Set to null to use the base price.
          example: 10
          nullable: true
    PipelineDayDto:
      type: object
      properties:
        date:
          type: string
          description: Date of the day in ISO string format (YYYY-MM-DDTHH:mm:ssZ)
          example: '2025-10-20T00:00:00.000Z'
        isWeekend:
          type: boolean
          description: Whether this day is a weekend (Saturday or Sunday)
          example: false
        workDayLength:
          type: number
          description: Length of the work day in hours for this employee. Used for capacity calculations and time validation.
          example: 8
        holiday:
          type: object
          description: Name of the holiday if this day is a regional holiday, null otherwise. Holidays affect capacity planning as employees are not available.
          example: Christmas Day
          nullable: true
        vacation:
          type: object
          description: Vacation data if the employee is on vacation this day, null otherwise. Includes vacation type and details.
          nullable: true
        sickness:
          type: object
          description: Sickness data if the employee is on sick leave this day, null otherwise. Includes sickness type and details.
          nullable: true
      required:
        - date
        - isWeekend
        - workDayLength
        - holiday
        - vacation
        - sickness
    PipelineTaskItemDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the pipeline item
          example: 123e4567-e89b-12d3-a456-426614174000
        taskId:
          type: string
          description: Unique identifier of the task being planned
          example: 123e4567-e89b-12d3-a456-426614174001
        userId:
          type: string
          description: Unique identifier of the user/team member this task is assigned to
          example: 123e4567-e89b-12d3-a456-426614174002
        dateStart:
          type: string
          description: Start date and time in ISO string format. The date part represents the calendar day, and the time component represents hours since work day start (not midnight). See controller documentation for details on time format.
          example: '2025-10-20T00:00:00.000Z'
        dateEnd:
          type: string
          description: End date and time in ISO string format. The date part represents the calendar day, and the time component represents hours since work day start (not midnight). See controller documentation for details on time format.
          example: '2025-10-22T00:00:00.000Z'
        task:
          type: object
          description: Full task object with all task details including name, description, project, status, priority, etc.
      required:
        - id
        - taskId
        - userId
        - dateStart
        - dateEnd
        - task
    PipelineRealityItemDto:
      type: object
      properties:
        hours:
          type: number
          description: Number of hours actually logged for this time entry. Represents actual work done, not planned time.
          example: 2.5
        date:
          type: string
          description: Date when the time was logged in ISO string format
          example: '2025-10-20T00:00:00.000Z'
        type:
          type: string
          description: 'Type of time log entry: "task" for task-specific time logs, or "project" for project-level time logs'
          example: task
          enum:
            - task
            - project
        taskId:
          type: object
          description: Task ID if this is a task-specific time log, null if it is a project-level time log
          example: 123e4567-e89b-12d3-a456-426614174000
          nullable: true
        projectId:
          type: object
          description: Project ID if this is a project-level time log, null if it is a task-specific time log
          example: 123e4567-e89b-12d3-a456-426614174001
          nullable: true
        task:
          type: object
          description: Full task object with task details, included if type is "task"
          nullable: true
        project:
          type: object
          description: Full project object with project details, included if type is "project"
          nullable: true
      required:
        - hours
        - date
        - type
        - taskId
        - projectId
        - task
        - project
    PipelineUserRowDto:
      type: object
      properties:
        employeeId:
          type: string
          description: Unique identifier of the employee record
          example: 123e4567-e89b-12d3-a456-426614174000
        userId:
          type: string
          description: Unique identifier of the user account
          example: 123e4567-e89b-12d3-a456-426614174001
        name:
          type: string
          description: Full name of the employee/team member
          example: John Doe
        days:
          description: Array of day data for each day in the pipeline period, including work day length, weekends, holidays, vacations, and sickness. Used for capacity planning and visual display.
          type: array
          items:
            $ref: '#/components/schemas/PipelineDayDto'
        items:
          description: Array of pipeline items (planned tasks) assigned to this user for the period. These are the tasks scheduled in the pipeline that will appear in the user's stack when the week is activated.
          type: array
          items:
            $ref: '#/components/schemas/PipelineTaskItemDto'
        reality:
          description: Array of reality items (actual time logs) for this user during the period. Only included if withReality=true and user has Tasks.seeLoggedTime permission. Used to compare planned vs. actual work.
          type: array
          items:
            $ref: '#/components/schemas/PipelineRealityItemDto'
      required:
        - employeeId
        - userId
        - name
        - days
        - items
        - reality
    TimeTableDayVacationDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the vacation request. Used to link the day to the full vacation record.
        status:
          type: string
          description: Current status of the vacation request. Only Pending and Approved vacations are shown in the time table. Rejected or cancelled vacations are excluded.
          enum:
            - Pending
            - Approved
            - Rejected
            - Cancelled
        dateFrom:
          type: string
          description: Start date of the vacation period in ISO 8601 format. The day is included in the vacation if it falls between dateFrom and dateTo (inclusive).
        dateTo:
          type: string
          description: End date of the vacation period in ISO 8601 format. The day is included in the vacation if it falls between dateFrom and dateTo (inclusive).
      required:
        - id
        - status
        - dateFrom
        - dateTo
    TimeTableDaySicknessDto:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the sickness record. Used to link the day to the full sickness record.
        dateFrom:
          type: string
          description: Start date of the sickness period in ISO 8601 format. The day is included in the sickness period if it falls between dateFrom and dateTo (inclusive).
        dateTo:
          type: string
          description: End date of the sickness period in ISO 8601 format. The day is included in the sickness period if it falls between dateFrom and dateTo (inclusive).
        comment:
          type: string
          description: Optional comment or note about the sickness. May include details like medical certificate information or symptoms.
          nullable: true
      required:
        - id
        - dateFrom
        - dateTo
    TimeTableExternalBusyBlockDto:
      type: object
      properties:
        startAt:
          type: string
          description: Interval start in ISO 8601 (pipeline grid convention).
        endAt:
          type: string
          description: Interval end in ISO 8601 (pipeline grid convention).
        allDay:
          type: boolean
          description: True when the source ICS event was an all-day (`VALUE=DATE`) event; the interval spans the full configured workday.
      required:
        - startAt
        - endAt
    TimeTableDayDto:
      type: object
      properties:
        type:
          type: number
          description: 'Type of time tracking mode for this day. 0 = Attendance mode (when workspace has attendanceEnabled): shows clock in/out times and attendance data. 1 = TimeBooking mode (when attendance is disabled): shows time logged on projects and tasks.'
          enum:
            - 0
            - 1
        date:
          type: string
          description: Date of this day in ISO 8601 format (YYYY-MM-DDTHH:mm:ss.sssZ). The time component is always midnight UTC.
        isWeekend:
          type: boolean
          description: Whether this day is considered a weekend based on the employee work schedule. Weekends are determined by the employee weeklyWorkingTime configuration.
        workDayLength:
          type: number
          description: Expected length of the work day in hours. Calculated from the employee weekly working hours divided by the number of working days per week. Only present if the employee has a configured work schedule.
        mood:
          type: string
          description: Employee mood recorded for attendance tracking. Only present when attendance is enabled and the employee has recorded attendance for this day.
          enum:
            - Good
            - Neutral
            - Bad
        timeFrom:
          type: string
          description: Clock-in time in ISO 8601 format. Only present when attendance is enabled and the employee has clocked in for this day.
        timeTo:
          type: string
          description: Clock-out time in ISO 8601 format. Only present when attendance is enabled and the employee has clocked out for this day.
        totalHours:
          type: number
          description: 'Total hours for the day. In Attendance mode: total hours worked (calculated from timeFrom to timeTo). In TimeBooking mode: sum of all time logged on tasks for this day. May be undefined if no attendance or time bookings exist.'
        spentHours:
          type: number
          description: Total hours logged on tasks and projects for this day. Only included if the requesting user has Tasks.seeLoggedTime permission. This represents all time entries regardless of whether they are billable.
        chargeableTime:
          type: number
          description: Total hours logged on billable projects (projects linked to organizations) for this day. Only included if the requesting user has Tasks.seeLoggedTime permission. Used for tracking chargeable time against goals.
        chargeableGoal:
          type: number
          description: Daily goal for chargeable hours set for this employee. Used to track performance against chargeable time targets. Only present if the employee has a chargeable hours goal configured.
        holiday:
          type: string
          description: Name of the public holiday if this day is a holiday. Holidays are determined based on the employee holiday code and the workspace default holiday code. Only present if the day is a recognized holiday.
        contractStarted:
          type: boolean
          description: Whether the employee contract has started by this date. Based on the employee entryDate. If entryDate is not set, defaults to true. Days before contract start are typically excluded from calculations.
        vacation:
          description: Vacation request data if this day falls within a vacation period. Only includes vacations with Pending or Approved status. Contains vacation ID, status, and date range for linking to the full vacation record.
          allOf:
            - $ref: '#/components/schemas/TimeTableDayVacationDto'
        sickness:
          description: Sickness record data if this day falls within a sickness period. Contains sickness ID, date range, and optional comment for linking to the full sickness record.
          allOf:
            - $ref: '#/components/schemas/TimeTableDaySicknessDto'
        comment:
          type: string
          description: Optional comment or note for this day. In Attendance mode, this is the attendance comment. May contain additional context about the day.
        highlight:
          type: boolean
          description: Whether this day should be highlighted in the UI. Used for visual emphasis of important days or periods.
        externalBusyBlocks:
          description: Busy intervals from the user’s enabled imported external calendars, clipped to this day. No event titles or details are included.
          type: array
          items:
            $ref: '#/components/schemas/TimeTableExternalBusyBlockDto'
      required:
        - date
        - isWeekend
        - contractStarted
    TimeTableUserRowDto:
      type: object
      properties:
        employeeId:
          type: string
          description: Unique identifier of the employee record. Used to link time table data to employee configuration (work schedule, goals, etc.).
        userId:
          type: string
          description: Unique identifier of the user account. This is the key used in the response object to identify each employee in the time table.
        name:
          type: string
          description: Full name of the employee as displayed in the system.
        days:
          description: Array of day objects, one for each date in the requested date range. Days are ordered chronologically from dateStart to dateEnd (inclusive). Includes all days in the range, including weekends and holidays.
          type: array
          items:
            $ref: '#/components/schemas/TimeTableDayDto'
      required:
        - employeeId
        - userId
        - name
        - days
security:
  - OAuth2: []
